Skip to content

Commit

Permalink
Merge pull request #72 from trycompai/lewis/user-portal
Browse files Browse the repository at this point in the history
Lewis/user portal
  • Loading branch information
carhartlewis authored Feb 19, 2025
2 parents 7a30516 + 9550d2b commit 71613e2
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 25 deletions.
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;

0 comments on commit 71613e2

Please sign in to comment.