Add people pages

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Jonathan
2025-05-31 18:04:54 +02:00
committed by Sacha Al Himdani
parent 6862841459
commit b94b58e2de
30 changed files with 2589 additions and 213 deletions

View File

@@ -9,3 +9,4 @@ export {
export { times, groupBy, isEmpty } from "./array";
export { randomInt } from "./number";
export { getMeasureStateLabel, measureStates } from "./measure";
export { getRole, getRoles, peopleRoles } from "./people";

View File

@@ -0,0 +1,37 @@
type Translator = (s: string) => string;
export const peopleRoles = [
"EMPLOYEE",
"CONTRACTOR",
"SERVICE_ACCOUNT",
] as const;
export function getRoles(__: Translator) {
return [
{
value: "EMPLOYEE",
label: __("Employee"),
},
{
value: "CONTRACTOR",
label: __("Contractor"),
},
{
value: "SERVICE_ACCOUNT",
label: __("Service account"),
},
];
}
export function getRole(__: Translator, role?: string): string {
switch (role) {
case "EMPLOYEE":
return __("Employee");
case "CONTRACTOR":
return __("Contractor");
case "SERVICE_ACCOUNT":
return __("Service account");
default:
return __("Unknown");
}
}

View File

@@ -5,13 +5,13 @@ import { Outlet } from "react-router";
export function AuthLayout() {
const { __ } = useTranslate();
return (
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen">
<div className="grid grid-cols-1 lg:grid-cols-2 min-h-screen text-txt-primary">
<div className="bg-level-0 flex flex-col items-center justify-center">
<div className="max-w-112">
<Outlet />
</div>
</div>
<div className="hidden lg:flex bg-dialog text-invert text-5xl font-bold flex flex-col items-center justify-center p-8 text-balance lg:p-10">
<div className="hidden lg:flex bg-dialog text-invert text-5xl font-bold flex flex-col items-center justify-center p-8 text-txt-primary lg:p-10">
<div className="flex flex-col 2xl:flex-row-reverse items-center justify-center gap-4">
<img src={logo} alt="Probo logo" className="h-auto w-96" />
<span>

View File

@@ -12,6 +12,7 @@ import { Logo } from "../Atoms/Logo/Logo.tsx";
import { Toasts } from "../Atoms/Toasts/Toasts.tsx";
import { createPortal } from "react-dom";
import clsx from "clsx";
import { ConfirmDialog } from "../Molecules/Dialog/ConfirmDialog.tsx";
type Props = PropsWithChildren<{
header: ReactNode;
@@ -62,6 +63,7 @@ export function Layout({ header, sidebar, children }: Props) {
</main>
</div>
<Toasts />
<ConfirmDialog />
</div>
</LayoutContext>
);

View File

@@ -9,6 +9,7 @@ import {
Cancel,
} from "@radix-ui/react-alert-dialog";
import {
useCallback,
useRef,
useState,
type ComponentProps,
@@ -18,57 +19,94 @@ import {
import { Button } from "../../Atoms/Button/Button";
import { Root as Portal } from "@radix-ui/react-portal";
import { dialog } from "./Dialog";
import { create } from "zustand";
import { combine } from "zustand/middleware";
type Props = {
children?: ReactNode;
title?: ReactNode;
message: string;
type State = {
title?: string;
message: string | null;
variant?: ComponentProps<typeof Button>["variant"];
label?: string;
onConfirm?: () => Promise<void>;
ref?: RefObject<{ open: () => void } | null>;
onConfirm: () => Promise<void>;
};
export const useConfirmDialogRef = () => {
return useRef<{ open: () => void } | null>(null);
};
const useConfirmStore = create(
combine(
{
message: null,
onConfirm: () => Promise.resolve(),
} as State,
(set) => ({
open: (props: State) => {
set(props);
},
close: () => {
set({
message: null,
});
},
}),
),
);
export function ConfirmDialog(props: Props) {
/**
* Hook used to open a confirm dialog
*/
export function useConfirm() {
const open = useConfirmStore((state) => state.open);
const { __ } = useTranslate();
const title = props.title ?? __("Are you sure ?");
const variant = props.variant ?? "danger";
const label = variant === "danger" ? __("Delete") : props.label;
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
return useCallback(
(cb: State["onConfirm"], props: Omit<State, "onConfirm">) => {
open({
onConfirm: cb,
...props,
message: props.message,
title: props.title ?? __("Are you sure ?"),
variant: props.variant ?? "danger",
label: props.label ?? __("Delete"),
});
},
[open],
);
}
/**
* Global component that displays a dialog when confirm() is called
*/
export function ConfirmDialog() {
const { message, title, variant, label, onConfirm, close } =
useConfirmStore();
const { __ } = useTranslate();
const isOpen = !!message;
const {
overlay,
content,
header,
footer,
title: titleClassname,
footer,
} = dialog();
const onConfirm = () => {
const [loading, setLoading] = useState(false);
const handleConfirm = () => {
setLoading(true);
props.onConfirm?.().then(() => {
setLoading(false);
setOpen(false);
});
onConfirm()
.then(() => {
close();
})
.finally(() => {
setLoading(false);
});
};
if (props.ref) {
props.ref.current = {
open: () => setOpen(true),
};
}
return (
<Root open={open} onOpenChange={setOpen}>
{props.children && <Trigger asChild children={props.children} />}
<Root open={isOpen} onOpenChange={close}>
<Portal>
<Overlay className={overlay()} />
<Content className={content({ className: "max-w-[500px]" })}>
<header className={header()}>
<Title children={title} className={titleClassname()} />
</header>
<Description className="p-6" children={props.message} />
<Description className="p-6" children={message} />
<footer className={footer()}>
<Cancel asChild>
<Button disabled={loading} variant="tertiary">
@@ -78,7 +116,7 @@ export function ConfirmDialog(props: Props) {
<Button
disabled={loading}
variant={variant}
onClick={onConfirm}
onClick={handleConfirm}
>
{label}
</Button>

View File

@@ -47,13 +47,10 @@ export {
DialogTitle,
useDialogRef,
} from "./Molecules/Dialog/Dialog";
export { useConfirm } from "./Molecules/Dialog/ConfirmDialog";
export { RiskBadge } from "./Molecules/Badge/RiskBadge";
export { SeverityBadge } from "./Molecules/Badge/SeverityBadge.tsx";
export { PolicyVersionBadge } from "./Molecules/Badge/PolicyVersionBadge.tsx";
export {
ConfirmDialog,
useConfirmDialogRef,
} from "./Molecules/Dialog/ConfirmDialog.tsx";
export { RisksChart } from "./Molecules/Risks/RisksChart";
export { RiskOverview } from "./Molecules/Risks/RiskOverview";
export { Combobox, ComboboxItem } from "./Molecules/Combobox/Combobox";