feat(auth): add side visual illustration
This commit is contained in:
128
src/pages/auth/AuthSideVisual.tsx
Normal file
128
src/pages/auth/AuthSideVisual.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { BarChart3, BriefcaseBusiness, Clock3, FileText, Tags, TimerReset } from "lucide-react"
|
||||||
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
|
import { useTranslation } from "../../hooks/useTranslation"
|
||||||
|
|
||||||
|
const content = {
|
||||||
|
en: {
|
||||||
|
eyebrow: "Workspace time engine",
|
||||||
|
title: "Track time with context, not clutter.",
|
||||||
|
description: "Connect every timer to a client, project, tag, and reportable result.",
|
||||||
|
timer: "02:34:18",
|
||||||
|
billable: "Billable",
|
||||||
|
project: "Project",
|
||||||
|
projectName: "Mobile app",
|
||||||
|
report: "PDF export",
|
||||||
|
insight: "Weekly report",
|
||||||
|
tags: "Design, QA",
|
||||||
|
points: ["Accurate timers", "Project clarity", "Fast reports"],
|
||||||
|
},
|
||||||
|
fa: {
|
||||||
|
eyebrow: "موتور زمان ورکاسپیس",
|
||||||
|
title: "زمان را با زمینهی کاری ثبت کنید، نه با شلوغی.",
|
||||||
|
description: "هر تایمر را به مشتری، پروژه، تگ و خروجی قابل گزارش وصل کنید.",
|
||||||
|
timer: "۰۲:۳۴:۱۸",
|
||||||
|
billable: "قابل محاسبه",
|
||||||
|
project: "پروژه",
|
||||||
|
projectName: "اپلیکیشن موبایل",
|
||||||
|
report: "خروجی PDF",
|
||||||
|
insight: "گزارش هفتگی",
|
||||||
|
tags: "طراحی، تست",
|
||||||
|
points: ["تایمر دقیق", "شفافیت پروژه", "گزارش سریع"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuthSideVisual() {
|
||||||
|
const { lang } = useTranslation()
|
||||||
|
const copy = lang === "fa" ? content.fa : content.en
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative z-10 flex min-h-0 flex-1 items-center justify-center py-2 xl:py-4 2xl:py-14">
|
||||||
|
<div className="relative w-full max-w-[22rem] xl:max-w-[25rem] 2xl:max-w-[38rem]">
|
||||||
|
<div className="absolute -inset-8 rounded-full bg-blue-500/10 blur-3xl dark:bg-blue-400/10 xl:-inset-10 2xl:-inset-16" />
|
||||||
|
<div className="absolute start-10 top-12 h-16 w-16 rounded-full bg-cyan-400/20 blur-2xl dark:bg-cyan-300/15 xl:h-20 xl:w-20 2xl:h-28 2xl:w-28" />
|
||||||
|
<div className="absolute bottom-6 end-8 h-20 w-20 rounded-full bg-emerald-400/20 blur-2xl dark:bg-emerald-300/15 xl:h-24 xl:w-24 2xl:h-36 2xl:w-36" />
|
||||||
|
|
||||||
|
<div className="relative mx-auto aspect-square max-h-[18rem] rounded-[1.75rem] border border-white/70 bg-white/55 p-4 shadow-2xl shadow-blue-950/10 backdrop-blur-2xl dark:border-white/10 dark:bg-slate-900/45 dark:shadow-black/30 xl:max-h-[21rem] xl:rounded-[2rem] xl:p-5 2xl:max-h-[34rem] 2xl:rounded-[3rem] 2xl:p-8">
|
||||||
|
<div className="absolute inset-0 rounded-[2rem] bg-[linear-gradient(135deg,rgba(59,130,246,0.12),transparent_42%,rgba(16,185,129,0.10))] dark:bg-[linear-gradient(135deg,rgba(96,165,250,0.12),transparent_42%,rgba(45,212,191,0.08))] xl:rounded-[2.5rem] 2xl:rounded-[3rem]" />
|
||||||
|
<div className="absolute inset-3 rounded-[1.25rem] border border-dashed border-slate-300/70 dark:border-slate-700/70 xl:inset-4 xl:rounded-[1.5rem] 2xl:inset-6 2xl:rounded-[2.25rem]" />
|
||||||
|
<div className="absolute inset-10 rounded-full border border-slate-200/80 dark:border-slate-700/80 xl:inset-12 2xl:inset-16" />
|
||||||
|
<div className="absolute inset-16 rounded-full border border-blue-200/80 dark:border-blue-500/25 xl:inset-20 2xl:inset-28" />
|
||||||
|
|
||||||
|
<div className="absolute left-1/2 top-1/2 z-10 flex h-24 w-24 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border border-blue-200 bg-white shadow-xl shadow-blue-950/10 dark:border-blue-500/30 dark:bg-slate-950 xl:h-28 xl:w-28 2xl:h-44 2xl:w-44">
|
||||||
|
<div className="absolute inset-3 rounded-full border border-slate-100 dark:border-slate-800 2xl:inset-4" />
|
||||||
|
<div className="absolute left-1/2 top-3 h-10 w-1 -translate-x-1/2 origin-bottom rounded-full bg-blue-600 dark:bg-blue-400 xl:h-12 2xl:top-5 2xl:h-[4.5rem]" />
|
||||||
|
<div className="absolute left-1/2 top-1/2 h-1 w-8 -translate-y-1/2 rounded-full bg-cyan-500 dark:bg-cyan-300 xl:w-9 2xl:w-14" />
|
||||||
|
<div className="absolute left-1/2 top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-slate-950 ring-4 ring-white dark:bg-white dark:ring-slate-950 2xl:h-4 2xl:w-4" />
|
||||||
|
<Clock3 className="h-9 w-9 text-slate-200 dark:text-slate-800 xl:h-10 xl:w-10 2xl:h-16 2xl:w-16" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FloatingCard className="start-3 top-11 xl:start-4 xl:top-12 2xl:start-7 2xl:top-20" icon={<TimerReset className="h-4 w-4" />} label={copy.billable} value={copy.timer} accent="blue" />
|
||||||
|
<FloatingCard className="end-3 top-20 xl:end-4 xl:top-22 2xl:end-5 2xl:top-32" icon={<BriefcaseBusiness className="h-4 w-4" />} label={copy.project} value={copy.projectName} accent="emerald" />
|
||||||
|
<FloatingCard className="bottom-12 start-3 xl:bottom-14 xl:start-4 2xl:bottom-24 2xl:start-6" icon={<Tags className="h-4 w-4" />} label={copy.tags} value={copy.report} accent="amber" />
|
||||||
|
<FloatingCard className="bottom-7 end-4 xl:bottom-8 xl:end-5 2xl:bottom-16 2xl:end-8" icon={<BarChart3 className="h-4 w-4" />} label={copy.insight} value="+18%" accent="cyan" />
|
||||||
|
|
||||||
|
<div className="absolute left-1/2 top-4 z-20 flex -translate-x-1/2 items-center gap-2 rounded-full border border-slate-200 bg-white/90 px-2.5 py-1 text-[0.65rem] font-semibold text-slate-700 shadow-sm dark:border-slate-700 dark:bg-slate-950/90 dark:text-slate-200 2xl:top-8 2xl:px-3 2xl:py-2 2xl:text-xs">
|
||||||
|
<FileText className="h-3.5 w-3.5 text-blue-500" />
|
||||||
|
{copy.report}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative mt-4 space-y-2 text-center xl:mt-5 2xl:mt-10 2xl:space-y-5">
|
||||||
|
<p className="text-[0.65rem] font-semibold uppercase tracking-[0.24em] text-blue-600 dark:text-blue-300 2xl:text-xs 2xl:tracking-[0.3em]">
|
||||||
|
{copy.eyebrow}
|
||||||
|
</p>
|
||||||
|
<div className="space-y-1.5 2xl:space-y-3">
|
||||||
|
<h2 className="text-lg font-semibold tracking-tight text-slate-950 dark:text-white xl:text-xl 2xl:text-3xl">
|
||||||
|
{copy.title}
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto max-w-xs text-[0.72rem] leading-5 text-slate-600 dark:text-slate-300 xl:max-w-sm 2xl:max-w-md 2xl:text-sm 2xl:leading-7">
|
||||||
|
{copy.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="hidden flex-wrap items-center justify-center gap-2 2xl:flex">
|
||||||
|
{copy.points.map((point) => (
|
||||||
|
<span
|
||||||
|
key={point}
|
||||||
|
className="rounded-full border border-slate-200 bg-white/70 px-2.5 py-1 text-[0.68rem] font-medium text-slate-600 shadow-sm dark:border-slate-700 dark:bg-slate-900/70 dark:text-slate-300 xl:px-3 xl:py-1.5 xl:text-xs"
|
||||||
|
>
|
||||||
|
{point}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FloatingCardProps {
|
||||||
|
className: string
|
||||||
|
icon: ReactNode
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
accent: "blue" | "emerald" | "amber" | "cyan"
|
||||||
|
}
|
||||||
|
|
||||||
|
function FloatingCard({ className, icon, label, value, accent }: FloatingCardProps) {
|
||||||
|
const accentClass = {
|
||||||
|
blue: "bg-blue-500/10 text-blue-600 dark:bg-blue-400/10 dark:text-blue-300",
|
||||||
|
emerald: "bg-emerald-500/10 text-emerald-600 dark:bg-emerald-400/10 dark:text-emerald-300",
|
||||||
|
amber: "bg-amber-500/10 text-amber-700 dark:bg-amber-400/10 dark:text-amber-300",
|
||||||
|
cyan: "bg-cyan-500/10 text-cyan-600 dark:bg-cyan-400/10 dark:text-cyan-300",
|
||||||
|
}[accent]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`absolute z-20 min-w-28 rounded-xl border border-white/80 bg-white/90 p-2 text-start shadow-lg shadow-slate-950/10 backdrop-blur-xl dark:border-white/10 dark:bg-slate-950/90 dark:shadow-black/30 2xl:min-w-36 2xl:rounded-2xl 2xl:p-3 ${className}`}>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className={`flex h-6 w-6 items-center justify-center rounded-lg 2xl:h-8 2xl:w-8 2xl:rounded-xl ${accentClass}`}>
|
||||||
|
{icon}
|
||||||
|
</span>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="truncate text-[0.62rem] font-medium text-slate-500 dark:text-slate-400 2xl:text-[0.68rem]">{label}</p>
|
||||||
|
<p className="truncate text-[0.72rem] font-semibold text-slate-900 dark:text-slate-50 2xl:text-sm">{value}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user