refactor: card title and layout tweaks (#675)

* refactor: card title and layout tweaks

* chore: review comments

* refactor: update domain warning screen
This commit is contained in:
Stavros
2026-03-01 13:36:43 +02:00
committed by GitHub
parent 89da4028bb
commit d0e39c6149
20 changed files with 356 additions and 142 deletions

View File

@@ -10,17 +10,17 @@ import {
FormLabel,
FormMessage,
} from "../ui/form";
import { Button } from "../ui/button";
import { loginSchema, LoginSchema } from "@/schemas/login-schema";
import z from "zod";
interface Props {
onSubmit: (data: LoginSchema) => void;
loading?: boolean;
formId?: string;
}
export const LoginForm = (props: Props) => {
const { onSubmit, loading } = props;
const { onSubmit, loading, formId } = props;
const { t } = useTranslation();
z.config({
@@ -34,7 +34,7 @@ export const LoginForm = (props: Props) => {
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<form id={formId} onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="username"
@@ -57,7 +57,7 @@ export const LoginForm = (props: Props) => {
control={form.control}
name="password"
render={({ field }) => (
<FormItem className="mb-4 gap-0">
<FormItem className="gap-0">
<div className="relative mb-1">
<FormLabel className="mb-2">{t("loginPassword")}</FormLabel>
<FormControl>
@@ -71,7 +71,7 @@ export const LoginForm = (props: Props) => {
</FormControl>
<a
href="/forgot-password"
className="text-muted-foreground text-sm absolute right-0 bottom-[2.565rem]" // 2.565 is *just* perfect
className="text-muted-foreground hover:text-muted-foreground/80 text-sm absolute right-0 bottom-[2.565rem]" // 2.565 is *just* perfect
>
{t("forgotPasswordTitle")}
</a>
@@ -80,9 +80,6 @@ export const LoginForm = (props: Props) => {
</FormItem>
)}
/>
<Button className="w-full" type="submit" loading={loading}>
{t("loginSubmit")}
</Button>
</form>
</Form>
);

View File

@@ -1,12 +1,12 @@
import {
Card,
CardDescription,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "../ui/card";
import { Button } from "../ui/button";
import { Trans, useTranslation } from "react-i18next";
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router";
interface Props {
@@ -24,23 +24,26 @@ export const DomainWarning = (props: Props) => {
const redirectUri = searchParams.get("redirect_uri");
return (
<Card role="alert" aria-live="assertive" className="min-w-xs sm:min-w-sm">
<Card role="alert" aria-live="assertive">
<CardHeader>
<CardTitle className="text-3xl">{t("domainWarningTitle")}</CardTitle>
<CardDescription>
<Trans
t={t}
i18nKey="domainWarningSubtitle"
values={{ appUrl, currentUrl }}
components={{ code: <code /> }}
shouldUnescape={true}
/>
</CardDescription>
<CardTitle className="text-xl">{t("domainWarningTitle")}</CardTitle>
</CardHeader>
<CardFooter className="flex flex-col items-stretch gap-2">
<Button onClick={onClick} variant="warning">
{t("ignoreTitle")}
</Button>
<CardContent className="flex flex-col gap-3 text-sm mb-1.25">
<p className="text-muted-foreground">{t("domainWarningSubtitle")}</p>
<pre>
<span className="text-muted-foreground">
{t("domainWarningExpected")}&nbsp;
<span className="text-primary">{appUrl}</span>
</span>
</pre>
<pre>
<span className="text-muted-foreground">
{t("domainWarningCurrent")}&nbsp;
<span className="text-primary">{currentUrl}</span>
</span>
</pre>
</CardContent>
<CardFooter className="flex flex-col items-stretch gap-3">
<Button
onClick={() =>
window.location.assign(
@@ -51,6 +54,9 @@ export const DomainWarning = (props: Props) => {
>
{t("goToCorrectDomainTitle")}
</Button>
<Button onClick={onClick} variant="warning">
{t("ignoreTitle")}
</Button>
</CardFooter>
</Card>
);

View File

@@ -14,18 +14,18 @@ const BaseLayout = ({ children }: { children: React.ReactNode }) => {
return (
<div
className="relative flex flex-col justify-center items-center min-h-svh"
className="flex flex-col justify-center items-center min-h-svh px-4"
style={{
backgroundImage: `url(${backgroundImage})`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<div className="absolute top-5 right-5 flex flex-row gap-2">
<div className="absolute top-4 right-4 flex flex-row gap-2">
<ThemeToggle />
<LanguageSelector />
</div>
{children}
<div className="max-w-sm md:min-w-sm min-w-xs">{children}</div>
</div>
);
};

View File

@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
"bg-card text-card-foreground flex flex-col gap-3 rounded-xl border py-6 shadow-sm",
className,
)}
{...props}
@@ -20,7 +20,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className,
)}
{...props}
@@ -75,7 +75,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
className={cn("flex items-center px-6 [.border-t]:pt-6 mt-2", className)}
{...props}
/>
);

View File

@@ -0,0 +1,55 @@
import * as React from "react"
import { Tooltip as TooltipPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
function TooltipProvider({
delayDuration = 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delayDuration={delayDuration}
{...props}
/>
)
}
function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}
function TooltipContent({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
className
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }