mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-11-04 08:05:42 +00:00 
			
		
		
		
	feat: finalize pages
This commit is contained in:
		
							
								
								
									
										22
									
								
								frontend/src/components/ui/code.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								frontend/src/components/ui/code.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
import { twMerge } from "tailwind-merge";
 | 
			
		||||
 | 
			
		||||
interface CodeProps extends React.ComponentPropsWithoutRef<"code"> {
 | 
			
		||||
  children?: React.ReactNode;
 | 
			
		||||
  className?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Code({ children, className, ...props }: CodeProps) {
 | 
			
		||||
  return (
 | 
			
		||||
    <code
 | 
			
		||||
      className={twMerge(
 | 
			
		||||
        "relative rounded bg-muted px-[0.2rem] py-[0.1rem] font-mono text-sm font-semibold",
 | 
			
		||||
        className,
 | 
			
		||||
      )}
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
    </code>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { Code };
 | 
			
		||||
@@ -16,7 +16,7 @@ export const OAuthButton = (props: Props) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Button
 | 
			
		||||
      onClick={onClick}
 | 
			
		||||
      className={twMerge("rounded-full", className)}
 | 
			
		||||
      className={twMerge("rounded-md", className)}
 | 
			
		||||
      variant="outline"
 | 
			
		||||
      {...rest}
 | 
			
		||||
    >
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "loginTitle": "Welcome back, login with",
 | 
			
		||||
    "loginTitleSimple": "Welcome back, please login",
 | 
			
		||||
    "loginDivider": "Or continue with password",
 | 
			
		||||
    "loginDivider": "Or",
 | 
			
		||||
    "loginUsername": "Username",
 | 
			
		||||
    "loginPassword": "Password",
 | 
			
		||||
    "loginSubmit": "Login",
 | 
			
		||||
    "loginFailTitle": "Failed to log in",
 | 
			
		||||
    "loginFailSubtitle": "Please check your username and password",
 | 
			
		||||
    "loginFailRateLimit": "You failed to login too many times, please try again later",
 | 
			
		||||
    "loginFailRateLimit": "You failed to login too many times. Please try again later",
 | 
			
		||||
    "loginSuccessTitle": "Logged in",
 | 
			
		||||
    "loginSuccessSubtitle": "Welcome back!",
 | 
			
		||||
    "loginOauthFailTitle": "Internal error",
 | 
			
		||||
    "loginOauthFailTitle": "An error occurred",
 | 
			
		||||
    "loginOauthFailSubtitle": "Failed to get OAuth URL",
 | 
			
		||||
    "loginOauthSuccessTitle": "Redirecting",
 | 
			
		||||
    "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
 | 
			
		||||
@@ -19,19 +19,19 @@
 | 
			
		||||
    "continueInvalidRedirectTitle": "Invalid redirect",
 | 
			
		||||
    "continueInvalidRedirectSubtitle": "The redirect URL is invalid",
 | 
			
		||||
    "continueInsecureRedirectTitle": "Insecure redirect",
 | 
			
		||||
    "continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code>, are you sure you want to continue?",
 | 
			
		||||
    "continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
 | 
			
		||||
    "continueTitle": "Continue",
 | 
			
		||||
    "continueSubtitle": "Click the button to continue to your app.",
 | 
			
		||||
    "internalErrorTitle": "Internal Server Error",
 | 
			
		||||
    "internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
 | 
			
		||||
    "internalErrorTitle": "An error occurred",
 | 
			
		||||
    "internalErrorSubtitle": "An error occurred in the server and it currently cannot serve your request.",
 | 
			
		||||
    "internalErrorButton": "Try again",
 | 
			
		||||
    "logoutFailTitle": "Failed to log out",
 | 
			
		||||
    "logoutFailSubtitle": "Please try again",
 | 
			
		||||
    "logoutSuccessTitle": "Logged out",
 | 
			
		||||
    "logoutSuccessSubtitle": "You have been logged out",
 | 
			
		||||
    "logoutTitle": "Logout",
 | 
			
		||||
    "logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>, click the button below to logout.",
 | 
			
		||||
    "logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider, click the button below to logout.",
 | 
			
		||||
    "logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
 | 
			
		||||
    "logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
 | 
			
		||||
    "notFoundTitle": "Page not found",
 | 
			
		||||
    "notFoundSubtitle": "The page you are looking for does not exist.",
 | 
			
		||||
    "notFoundButton": "Go home",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    "loginTitle": "Welcome back, login with",
 | 
			
		||||
    "loginTitleSimple": "Welcome back, please login",
 | 
			
		||||
    "loginDivider": "Or continue with password",
 | 
			
		||||
    "loginDivider": "Or",
 | 
			
		||||
    "loginUsername": "Username",
 | 
			
		||||
    "loginPassword": "Password",
 | 
			
		||||
    "loginSubmit": "Login",
 | 
			
		||||
    "loginFailTitle": "Failed to log in",
 | 
			
		||||
    "loginFailSubtitle": "Please check your username and password",
 | 
			
		||||
    "loginFailRateLimit": "You failed to login too many times, please try again later",
 | 
			
		||||
    "loginFailRateLimit": "You failed to login too many times. Please try again later",
 | 
			
		||||
    "loginSuccessTitle": "Logged in",
 | 
			
		||||
    "loginSuccessSubtitle": "Welcome back!",
 | 
			
		||||
    "loginOauthFailTitle": "Internal error",
 | 
			
		||||
    "loginOauthFailTitle": "An error occurred",
 | 
			
		||||
    "loginOauthFailSubtitle": "Failed to get OAuth URL",
 | 
			
		||||
    "loginOauthSuccessTitle": "Redirecting",
 | 
			
		||||
    "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
 | 
			
		||||
@@ -19,19 +19,19 @@
 | 
			
		||||
    "continueInvalidRedirectTitle": "Invalid redirect",
 | 
			
		||||
    "continueInvalidRedirectSubtitle": "The redirect URL is invalid",
 | 
			
		||||
    "continueInsecureRedirectTitle": "Insecure redirect",
 | 
			
		||||
    "continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code>, are you sure you want to continue?",
 | 
			
		||||
    "continueInsecureRedirectSubtitle": "You are trying to redirect from <code>https</code> to <code>http</code> which is not secure. Are you sure you want to continue?",
 | 
			
		||||
    "continueTitle": "Continue",
 | 
			
		||||
    "continueSubtitle": "Click the button to continue to your app.",
 | 
			
		||||
    "internalErrorTitle": "Internal Server Error",
 | 
			
		||||
    "internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
 | 
			
		||||
    "internalErrorTitle": "An error occurred",
 | 
			
		||||
    "internalErrorSubtitle": "An error occurred in the server and it currently cannot serve your request.",
 | 
			
		||||
    "internalErrorButton": "Try again",
 | 
			
		||||
    "logoutFailTitle": "Failed to log out",
 | 
			
		||||
    "logoutFailSubtitle": "Please try again",
 | 
			
		||||
    "logoutSuccessTitle": "Logged out",
 | 
			
		||||
    "logoutSuccessSubtitle": "You have been logged out",
 | 
			
		||||
    "logoutTitle": "Logout",
 | 
			
		||||
    "logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>, click the button below to logout.",
 | 
			
		||||
    "logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider, click the button below to logout.",
 | 
			
		||||
    "logoutUsernameSubtitle": "You are currently logged in as <code>{{username}}</code>. Click the button below to logout.",
 | 
			
		||||
    "logoutOauthSubtitle": "You are currently logged in as <code>{{username}}</code> using the {{provider}} OAuth provider. Click the button below to logout.",
 | 
			
		||||
    "notFoundTitle": "Page not found",
 | 
			
		||||
    "notFoundSubtitle": "The page you are looking for does not exist.",
 | 
			
		||||
    "notFoundButton": "Go home",
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,9 @@ import { ErrorPage } from "./pages/error-page.tsx";
 | 
			
		||||
import { NotFoundPage } from "./pages/not-found-page.tsx";
 | 
			
		||||
import { ContinuePage } from "./pages/continue-page.tsx";
 | 
			
		||||
import { TotpPage } from "./pages/totp-page.tsx";
 | 
			
		||||
import { ForgotPasswordPage } from "./pages/forgot-password.tsx";
 | 
			
		||||
import { ForgotPasswordPage } from "./pages/forgot-password-page.tsx";
 | 
			
		||||
import { LogoutPage } from "./pages/logout-page.tsx";
 | 
			
		||||
import { UnauthorizedPage } from "./pages/unauthorized-page.tsx";
 | 
			
		||||
 | 
			
		||||
const router = createBrowserRouter([
 | 
			
		||||
  {
 | 
			
		||||
@@ -22,6 +24,11 @@ const router = createBrowserRouter([
 | 
			
		||||
    element: <LoginPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/logout",
 | 
			
		||||
    element: <LogoutPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/continue",
 | 
			
		||||
    element: <ContinuePage />,
 | 
			
		||||
@@ -37,6 +44,11 @@ const router = createBrowserRouter([
 | 
			
		||||
    element: <ForgotPasswordPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/unauthorized",
 | 
			
		||||
    element: <UnauthorizedPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "*",
 | 
			
		||||
    element: <NotFoundPage />,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import {
 | 
			
		||||
  CardHeader,
 | 
			
		||||
  CardTitle,
 | 
			
		||||
} from "@/components/ui/card";
 | 
			
		||||
import { Code } from "@/components/ui/code";
 | 
			
		||||
import { isValidUrl } from "@/lib/utils";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
import { Navigate, useNavigate } from "react-router";
 | 
			
		||||
@@ -47,9 +48,7 @@ export const ContinuePage = () => {
 | 
			
		||||
              i18nKey="untrustedRedirectSubtitle"
 | 
			
		||||
              t={t}
 | 
			
		||||
              components={{
 | 
			
		||||
                code: (
 | 
			
		||||
                  <code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold" />
 | 
			
		||||
                ),
 | 
			
		||||
                code: <Code />,
 | 
			
		||||
              }}
 | 
			
		||||
              values={{ domain }}
 | 
			
		||||
            />
 | 
			
		||||
@@ -82,9 +81,7 @@ export const ContinuePage = () => {
 | 
			
		||||
              i18nKey="continueInsecureRedirectSubtitle"
 | 
			
		||||
              t={t}
 | 
			
		||||
              components={{
 | 
			
		||||
                code: (
 | 
			
		||||
                  <code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold" />
 | 
			
		||||
                ),
 | 
			
		||||
                code: <Code />,
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </CardDescription>
 | 
			
		||||
@@ -105,7 +102,7 @@ export const ContinuePage = () => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs md:max-w-sm">
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("continueTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>{t("continueSubtitle")}</CardDescription>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ export const ErrorPage = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs md:max-w-sm">
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("errorTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>{t("errorSubtitle")}</CardDescription>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ export const ForgotPasswordPage = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs md:max-w-sm">
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("forgotPasswordTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>
 | 
			
		||||
@@ -24,7 +24,7 @@ export const LoginPage = () => {
 | 
			
		||||
  const userAuthConfigured = configuredProviders.includes("username");
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="max-w-xs md:max-w-sm">
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-center text-3xl">{title}</CardTitle>
 | 
			
		||||
        {configuredProviders.length > 0 && (
 | 
			
		||||
@@ -33,17 +33,29 @@ export const LoginPage = () => {
 | 
			
		||||
          </CardDescription>
 | 
			
		||||
        )}
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardContent className="flex flex-col gap-5">
 | 
			
		||||
      <CardContent className="flex flex-col gap-4">
 | 
			
		||||
        {oauthConfigured && (
 | 
			
		||||
          <div className="flex flex-row flex-wrap gap-3 items-center justify-center">
 | 
			
		||||
          <div className="flex flex-col gap-2 items-center justify-center">
 | 
			
		||||
            {configuredProviders.includes("google") && (
 | 
			
		||||
              <OAuthButton title="Google" icon={<GoogleIcon />} />
 | 
			
		||||
              <OAuthButton
 | 
			
		||||
                title="Google"
 | 
			
		||||
                icon={<GoogleIcon />}
 | 
			
		||||
                className="w-full"
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
            {configuredProviders.includes("github") && (
 | 
			
		||||
              <OAuthButton title="Github" icon={<GithubIcon />} />
 | 
			
		||||
              <OAuthButton
 | 
			
		||||
                title="Github"
 | 
			
		||||
                icon={<GithubIcon />}
 | 
			
		||||
                className="w-full"
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
            {configuredProviders.includes("generic") && (
 | 
			
		||||
              <OAuthButton title="Generic" icon={<GenericIcon />} />
 | 
			
		||||
              <OAuthButton
 | 
			
		||||
                title="Generic"
 | 
			
		||||
                icon={<GenericIcon />}
 | 
			
		||||
                className="w-full"
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								frontend/src/pages/logout-page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								frontend/src/pages/logout-page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
import { Button } from "@/components/ui/button";
 | 
			
		||||
import {
 | 
			
		||||
  Card,
 | 
			
		||||
  CardDescription,
 | 
			
		||||
  CardFooter,
 | 
			
		||||
  CardHeader,
 | 
			
		||||
  CardTitle,
 | 
			
		||||
} from "@/components/ui/card";
 | 
			
		||||
import { Code } from "@/components/ui/code";
 | 
			
		||||
import { capitalize } from "@/utils/utils";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
 | 
			
		||||
export const LogoutPage = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const provider = "google";
 | 
			
		||||
  const genericName = "generic";
 | 
			
		||||
  const username = "username";
 | 
			
		||||
  const email = "smbd@example.com";
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("logoutTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>
 | 
			
		||||
          {provider !== "username" ? (
 | 
			
		||||
            <Trans
 | 
			
		||||
              i18nKey="logoutOauthSubtitle"
 | 
			
		||||
              t={t}
 | 
			
		||||
              components={{
 | 
			
		||||
                code: <Code />,
 | 
			
		||||
              }}
 | 
			
		||||
              values={{
 | 
			
		||||
                username: email,
 | 
			
		||||
                provider:
 | 
			
		||||
                  provider === "generic" ? genericName : capitalize(provider),
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          ) : (
 | 
			
		||||
            <Trans
 | 
			
		||||
              i18nKey="logoutUsernameSubtitle"
 | 
			
		||||
              t={t}
 | 
			
		||||
              components={{
 | 
			
		||||
                code: <Code />,
 | 
			
		||||
              }}
 | 
			
		||||
              values={{
 | 
			
		||||
                username: username,
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          )}
 | 
			
		||||
        </CardDescription>
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardFooter className="flex flex-col items-stretch">
 | 
			
		||||
        <Button>{t("logoutTitle")}</Button>
 | 
			
		||||
      </CardFooter>
 | 
			
		||||
    </Card>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -14,7 +14,7 @@ export const NotFoundPage = () => {
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs md:max-w-sm">
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("notFoundTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>{t("notFoundSubtitle")}</CardDescription>
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ export const TotpPage = () => {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs md:max-w-sm">
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("totpTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>{t("totpSubtitle")}</CardDescription>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								frontend/src/pages/unauthorized-page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								frontend/src/pages/unauthorized-page.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
import { Button } from "@/components/ui/button";
 | 
			
		||||
import {
 | 
			
		||||
  Card,
 | 
			
		||||
  CardDescription,
 | 
			
		||||
  CardFooter,
 | 
			
		||||
  CardHeader,
 | 
			
		||||
  CardTitle,
 | 
			
		||||
} from "@/components/ui/card";
 | 
			
		||||
import { Code } from "@/components/ui/code";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
import { Navigate } from "react-router";
 | 
			
		||||
 | 
			
		||||
export const UnauthorizedPage = () => {
 | 
			
		||||
  const searchParams = new URLSearchParams(window.location.search);
 | 
			
		||||
  const username = searchParams.get("username");
 | 
			
		||||
  const resource = searchParams.get("resource");
 | 
			
		||||
  const groupErr = searchParams.get("groupErr");
 | 
			
		||||
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  if (!username) {
 | 
			
		||||
    return <Navigate to="/" />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let i18nKey = "unaothorizedLoginSubtitle";
 | 
			
		||||
 | 
			
		||||
  if (resource) {
 | 
			
		||||
    i18nKey = "unauthorizedResourceSubtitle";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (groupErr === "true") {
 | 
			
		||||
    i18nKey = "unauthorizedGroupsSubtitle";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
      <CardHeader>
 | 
			
		||||
        <CardTitle className="text-3xl">{t("unauthorizedTitle")}</CardTitle>
 | 
			
		||||
        <CardDescription>
 | 
			
		||||
          <Trans
 | 
			
		||||
            i18nKey={i18nKey}
 | 
			
		||||
            t={t}
 | 
			
		||||
            components={{
 | 
			
		||||
              code: <Code />,
 | 
			
		||||
            }}
 | 
			
		||||
            values={{
 | 
			
		||||
              username: username,
 | 
			
		||||
              resource: resource,
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
        </CardDescription>
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardFooter className="flex flex-col items-stretch">
 | 
			
		||||
        <Button onClick={() => window.location.replace("/")}>
 | 
			
		||||
          {t("unauthorizedButton")}
 | 
			
		||||
        </Button>
 | 
			
		||||
      </CardFooter>
 | 
			
		||||
    </Card>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										3
									
								
								frontend/src/utils/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								frontend/src/utils/utils.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
export const capitalize = (str: string) => {
 | 
			
		||||
  return str.charAt(0).toUpperCase() + str.slice(1);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user