File size: 5,968 Bytes
ec1a96a
7804693
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629fd4f
7804693
 
ba42f3b
7804693
629fd4f
7804693
 
 
 
629fd4f
 
7804693
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba42f3b
7804693
 
 
 
ba42f3b
 
ec1a96a
7804693
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { Briefcase, FileCheck2, FileUser, History, LayoutDashboard, LogOut, Menu, Moon, Plus, Search, Settings, Sun, UploadCloud, X } from "lucide-react";
import { useState, type ReactNode } from "react";
import { Link, NavLink, useNavigate } from "react-router-dom";
import { useAuth } from "../lib/auth";
import { useI18n } from "../lib/i18n";
import { Logo, useTheme } from "./ui";

export function LangSwitch() {
  const { lang, setLang } = useI18n();
  return (
    <button onClick={() => setLang(lang === "ar" ? "en" : "ar")} className="rounded-lg px-2.5 py-1.5 text-sm font-medium text-muted hover:bg-sunken hover:text-ink"
      aria-label="Change language">
      {lang === "ar" ? "English" : "العربية"}
    </button>
  );
}

export function ThemeSwitch() {
  const [dark, toggle] = useTheme();
  const { t } = useI18n();
  return (
    <button onClick={toggle} className="rounded-lg p-2 text-muted hover:bg-sunken hover:text-ink" aria-label={dark ? t("theme_light") : t("theme_dark")}>
      {dark ? <Sun size={18} /> : <Moon size={18} />}
    </button>
  );
}

export function PublicHeader() {
  const { profile } = useAuth();
  const { t } = useI18n();
  return (
    <header className="mx-auto flex max-w-6xl items-center justify-between gap-2 px-3 py-5 sm:gap-4 sm:px-6">
      <Link to="/" aria-label="EvalBot home"><Logo /></Link>
      <nav className="flex items-center gap-1 sm:gap-2">
        <Link to="/jobs" className="hidden rounded-xl px-3 py-2 text-sm font-medium text-ink hover:bg-sunken sm:inline-block">{t("browse_jobs")}</Link>
        <LangSwitch />
        <span className="hidden sm:inline-flex"><ThemeSwitch /></span>
        {profile ? (
          <Link to="/app" className="rounded-xl bg-nile px-4 py-2 text-sm font-medium text-white dark:text-paper">{t("nav_dashboard")}</Link>
        ) : (
          <>
            <Link to="/login" className="whitespace-nowrap rounded-xl border border-line bg-surface px-2.5 py-2 text-sm font-medium text-ink hover:border-nile hover:text-nile sm:px-3.5">{t("sign_in")}</Link>
            <Link to="/signup" className="whitespace-nowrap rounded-xl bg-nile px-2.5 py-2 text-sm font-medium text-white hover:bg-nile-deep sm:px-3.5 dark:text-paper">{t("sign_up")}</Link>
          </>
        )}
      </nav>
    </header>
  );
}

export function AppShell({ children }: { children: ReactNode }) {
  const { profile, signOut } = useAuth();
  const { t } = useI18n();
  const nav = useNavigate();
  const [open, setOpen] = useState(false);
  const isRecruiter = profile?.role === "recruiter";
  const links = isRecruiter
    ? [
        { to: "/app", icon: LayoutDashboard, label: t("nav_overview"), end: true },
        { to: "/app/jobs", icon: Briefcase, label: t("nav_jobs") },
      ]
    : [
        { to: "/app", icon: LayoutDashboard, label: t("nav_dashboard"), end: true },
        { to: "/app/board", icon: Search, label: t("nav_board") },
        { to: "/app/applications", icon: FileCheck2, label: t("nav_applications") },
        { to: "/app/cv", icon: FileUser, label: t("nav_cv") },
        { to: "/app/new", icon: Plus, label: t("nav_new") },
        { to: "/app/history", icon: History, label: t("nav_history") },
      ];
  const item = "flex items-center gap-3 rounded-xl px-3 py-2.5 text-[15px] font-medium transition-colors";
  const sidebar = (
    <div className="flex h-full flex-col gap-1 p-4">
      <Link to="/" className="mb-6 px-2" onClick={() => setOpen(false)}><Logo /></Link>
      {links.map((l) => (
        <NavLink key={l.to} to={l.to} end={l.end} onClick={() => setOpen(false)}
          className={({ isActive }) => `${item} ${isActive ? "bg-nile-soft text-nile-deep" : "text-muted hover:bg-sunken hover:text-ink"}`}>
          <l.icon size={18} /> {l.label}
        </NavLink>
      ))}
      <a href="/demo" className={`${item} text-muted hover:bg-sunken hover:text-ink`}><UploadCloud size={18} /> {t("nav_quick")}</a>
      <div className="mt-auto flex flex-col gap-1 border-t border-line pt-3">
        <NavLink to="/app/settings" onClick={() => setOpen(false)}
          className={({ isActive }) => `${item} ${isActive ? "bg-nile-soft text-nile-deep" : "text-muted hover:bg-sunken hover:text-ink"}`}>
          <Settings size={18} /> {t("nav_settings")}
        </NavLink>
        <button className={`${item} text-muted hover:bg-sunken hover:text-ink`} onClick={async () => { await signOut(); nav("/"); }}>
          <LogOut size={18} className="rtl:rotate-180" /> {t("sign_out")}
        </button>
        <div className="flex items-center gap-1 px-1 pt-2"><LangSwitch /><ThemeSwitch /></div>
        {profile && <p className="truncate px-3 pt-1 text-xs text-muted">{profile.full_name || profile.email}{profile.company ? `, ${profile.company.name}` : ""}</p>}
      </div>
    </div>
  );
  return (
    <div className="min-h-screen lg:flex">
      <aside className="sticky top-0 hidden h-screen w-64 flex-none border-e border-line bg-surface lg:block">{sidebar}</aside>
      <div className="flex items-center justify-between border-b border-line bg-surface px-4 py-3 lg:hidden">
        <Link to="/app"><Logo size={24} /></Link>
        <button onClick={() => setOpen(true)} className="rounded-lg p-2 text-ink hover:bg-sunken" aria-label="Menu"><Menu size={22} /></button>
      </div>
      {open && (
        <div className="fixed inset-0 z-40 lg:hidden" role="dialog" aria-modal="true">
          <div className="absolute inset-0 bg-ink/40" onClick={() => setOpen(false)} />
          <div className="absolute inset-y-0 start-0 w-72 bg-surface shadow-xl">
            <button onClick={() => setOpen(false)} className="absolute end-3 top-4 rounded-lg p-2 text-muted hover:bg-sunken" aria-label="Close"><X size={20} /></button>
            {sidebar}
          </div>
        </div>
      )}
      <main className="min-w-0 flex-1 px-4 py-6 sm:px-8 sm:py-8">
        <div className="mx-auto max-w-5xl">{children}</div>
      </main>
    </div>
  );
}