Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions apps/portal/src/app/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
import { UserMenu } from "@/app/components/user-menu";
import { getI18n } from "@/app/locales/server";
import { Skeleton } from "@bubba/ui/skeleton";
import { headers } from "next/headers";
import { Suspense } from "react";
import { auth } from "../lib/auth";
import { MobileMenu } from "./mobile-menu";

export async function Header() {
const session = await auth.api.getSession({
headers: await headers(),
});

const t = await getI18n();

return (
<header className="-ml-4 -mr-4 md:m-0 z-10 px-4 md:px-0 md:border-b-[1px] flex justify-between pt-4 pb-2 md:pb-4 items-center todesktop:sticky todesktop:top-0 todesktop:bg-background todesktop:border-none sticky md:static top-0 backdrop-filter backdrop-blur-xl md:backdrop-filter md:backdrop-blur-none bg-opacity-70">
<MobileMenu />

<div className="flex space-x-2 ml-auto">
<div className="flex gap-2">Employee Portal</div>
</div>

<div className="flex space-x-2 ml-auto">
<Suspense fallback={<Skeleton className="h-8 w-8 rounded-full" />}>
<UserMenu session={session} />
<UserMenu />
</Suspense>
</div>
</header>
Expand Down
49 changes: 49 additions & 0 deletions apps/portal/src/app/components/locale-switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import {
useChangeLocale,
useCurrentLocale,
useI18n,
} from "@/app/locales/client";
import { languages } from "@/app/locales/client";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@bubba/ui/select";
import { Globe } from "lucide-react";

export const LocaleSwitch = () => {
const t = useI18n();
const locale = useCurrentLocale();
const changeLocale = useChangeLocale();

return (
<div className="flex items-center relative">
<Select
defaultValue={locale}
onValueChange={(value: keyof typeof languages) => changeLocale(value)}
>
<SelectTrigger className="w-full pl-6 pr-3 py-1.5 bg-transparent outline-none capitalize h-[32px] text-xs">
<SelectValue placeholder={t("language.placeholder")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{Object.entries(languages).map(([code, name]) => (
<SelectItem key={code} value={code} className="capitalize">
{name}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>

<div className="absolute left-2 pointer-events-none">
<Globe size={12} />
</div>
</div>
);
};
64 changes: 64 additions & 0 deletions apps/portal/src/app/components/theme-switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use client";

import { useI18n } from "@/app/locales/client";
import { Monitor, Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";

import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@bubba/ui/select";

type Theme = "dark" | "system" | "light";

type Props = {
currentTheme?: Theme;
};

const ThemeIcon = ({ currentTheme }: Props) => {
switch (currentTheme) {
case "dark":
return <Moon size={12} />;
case "system":
return <Monitor size={12} />;
default:
return <Sun size={12} />;
}
};

export const ThemeSwitch = () => {
const t = useI18n();
const { theme, setTheme, themes } = useTheme();

return (
<div className="flex items-center relative">
<Select
defaultValue={theme}
onValueChange={(value: Theme) => setTheme(value)}
>
<SelectTrigger className="w-full pl-6 pr-3 py-1.5 bg-transparent outline-none capitalize h-[32px] text-xs">
<SelectValue placeholder={t("user_menu.theme")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{themes.map((theme) => (
<SelectItem key={theme} value={theme} className="capitalize">
{t(
`theme.options.${theme.toLowerCase() as "dark" | "system" | "light"}`,
)}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>

<div className="absolute left-2 pointer-events-none">
<ThemeIcon currentTheme={theme as Theme} />
</div>
</div>
);
};
47 changes: 40 additions & 7 deletions apps/portal/src/app/components/user-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@bubba/ui/dropdown-menu";
import type { auth } from "../lib/auth";
import { headers } from "next/headers";
import { auth } from "../lib/auth";
import { LocaleSwitch } from "./locale-switch";
import { Logout } from "./logout";
import { ThemeSwitch } from "./theme-switch";

export async function UserMenu({
session,
}: { session: Awaited<ReturnType<typeof auth.api.getSession>> }) {
export async function UserMenu() {
const t = await getI18n();

const session = await auth.api.getSession({
headers: await headers(),
});

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
Expand All @@ -34,9 +42,34 @@ export async function UserMenu({
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[240px]" sideOffset={10} align="end">
<DropdownMenuItem>
<Logout />
</DropdownMenuItem>
{" "}
<DropdownMenuLabel>
<div className="flex justify-between items-center">
<div className="flex flex-col">
<span className="truncate line-clamp-1 max-w-[155px] block">
{session?.user?.name}
</span>
<span className="truncate text-xs text-muted-foreground font-normal">
{session?.user?.email}
</span>
</div>
<div className="border py-0.5 px-3 rounded-full text-[11px] font-normal">
Beta
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<div className="flex flex-row justify-between items-center p-2">
<p className="text-sm">{t("user_menu.theme")}</p>
<ThemeSwitch />
</div>{" "}
<DropdownMenuSeparator />{" "}
<div className="flex flex-row justify-between items-center p-2">
<p className="text-sm">{t("user_menu.language")}</p>
<LocaleSwitch />
</div>{" "}
<DropdownMenuSeparator />
<Logout />
</DropdownMenuContent>
</DropdownMenu>
);
Expand Down
12 changes: 12 additions & 0 deletions apps/portal/src/app/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default {
learn_more_link: "https://trycomp.ai",
},
user_menu: {
theme: "Theme",
language: "Language",
sign_out: "Sign Out",
},
not_found: {
Expand All @@ -29,4 +31,14 @@ export default {
sidebar: {
dashboard: "Employee Portal Overview",
},
language: {
placeholder: "Select Language",
},
theme: {
options: {
dark: "Dark",
light: "Light",
system: "System",
},
},
} as const;
14 changes: 13 additions & 1 deletion apps/portal/src/app/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default {
otp_sent: "Contraseña de un solo uso enviada",
otp_description: "Revisa tu correo electrónico para ver la contraseña de un solo uso",
otp_try_again: "Intentar de nuevo",
placeholder: "Tu correo electrónico del trabajo",
placeholder: "Tu correo del trabajo",
button: "Continuar",
},
},
Expand All @@ -19,6 +19,8 @@ export default {
learn_more_link: "https://trycomp.ai",
},
user_menu: {
theme: "Tema",
language: "Idioma",
sign_out: "Cerrar Sesión",
},
not_found: {
Expand All @@ -29,4 +31,14 @@ export default {
sidebar: {
dashboard: "Vista General del Portal del Empleado",
},
language: {
placeholder: "Seleccionar Idioma",
},
theme: {
options: {
dark: "Oscuro",
light: "Claro",
system: "Sistema",
},
},
} as const;
18 changes: 15 additions & 3 deletions apps/portal/src/app/locales/fr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default {
auth: {
title: "Portail Employé",
title: "Portail des Employés",
description: "Saisissez votre adresse e-mail pour recevoir un mot de passe à usage unique.",
options: "Plus d'options",
email: {
Expand All @@ -15,10 +15,12 @@ export default {
title: "Comp AI - Plateforme de Conformité OSS",
description:
"Obtenez la conformité SOC 2, ISO 27001 et RGPD en quelques semaines, pas en mois. Open source, inscription instantanée, essai gratuit.",
learn_more: "Commencer l'essai gratuit et devenir conforme",
learn_more: "Commencer l'essai gratuit et être conforme",
learn_more_link: "https://trycomp.ai",
},
user_menu: {
theme: "Thème",
language: "Langue",
sign_out: "Se déconnecter",
},
not_found: {
Expand All @@ -27,6 +29,16 @@ export default {
return: "Retourner à la page d'accueil",
},
sidebar: {
dashboard: "Vue d'ensemble du portail employé",
dashboard: "Vue d'ensemble du Portail des Employés",
},
language: {
placeholder: "Sélectionner la langue",
},
theme: {
options: {
dark: "Sombre",
light: "Clair",
system: "Système",
},
},
} as const;
14 changes: 13 additions & 1 deletion apps/portal/src/app/locales/no.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default {
otp_sent: "Engangspassord sendt",
otp_description: "Sjekk e-posten din for engangspassordet",
otp_try_again: "Prøv igjen",
placeholder: "Din jobb-e-post",
placeholder: "Din jobbmail",
button: "Fortsett",
},
},
Expand All @@ -19,6 +19,8 @@ export default {
learn_more_link: "https://trycomp.ai",
},
user_menu: {
theme: "Tema",
language: "Språk",
sign_out: "Logg ut",
},
not_found: {
Expand All @@ -29,4 +31,14 @@ export default {
sidebar: {
dashboard: "Oversikt over ansattportal",
},
language: {
placeholder: "Velg språk",
},
theme: {
options: {
dark: "Mørk",
light: "Lys",
system: "System",
},
},
} as const;
16 changes: 14 additions & 2 deletions apps/portal/src/app/locales/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default {
options: "Mais opções",
email: {
otp_sent: "Senha única enviada",
otp_description: "Verifique seu e-mail para ver a senha única",
otp_description: "Verifique seu e-mail para a senha única",
otp_try_again: "Tentar novamente",
placeholder: "Seu e-mail profissional",
button: "Continuar",
Expand All @@ -14,11 +14,13 @@ export default {
powered_by: {
title: "Comp AI - Plataforma de Conformidade OSS",
description:
"Obtenha conformidade com SOC 2, ISO 27001 e GDPR em semanas, não meses. Código aberto, inscrição instantânea, teste gratuito.",
"Obtenha conformidade com SOC 2, ISO 27001 e GDPR em semanas, não meses. Código aberto, cadastro instantâneo, teste gratuito.",
learn_more: "Comece o Teste Gratuito e Obtenha Conformidade",
learn_more_link: "https://trycomp.ai",
},
user_menu: {
theme: "Tema",
language: "Idioma",
sign_out: "Sair",
},
not_found: {
Expand All @@ -29,4 +31,14 @@ export default {
sidebar: {
dashboard: "Visão Geral do Portal do Funcionário",
},
language: {
placeholder: "Selecionar Idioma",
},
theme: {
options: {
dark: "Escuro",
light: "Claro",
system: "Sistema",
},
},
} as const;