Add risk assessment tab

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Jonathan
2025-06-03 18:35:21 +02:00
committed by Sacha Al Himdani
parent 7d2d26aab5
commit b660c0ce7f
27 changed files with 2020 additions and 311 deletions

View File

@@ -9,7 +9,7 @@ import { tv, type VariantProps } from "tailwind-variants";
import { Slot } from "../Slot";
export const button = tv({
base: "flex items-center justify-center gap-[6px] px-3 py-2 rounded-full cursor-pointer text-sm font-medium h-8 focus:outline-none",
base: "flex items-center justify-center gap-[6px] px-3 py-2 rounded-full cursor-pointer text-sm font-medium h-8 focus:outline-none whitespace-nowrap",
variants: {
variant: {
primary:

View File

@@ -80,8 +80,6 @@ export function Dropzone(props: Props) {
hasError: !!error,
});
console.log(error);
return (
<div {...getRootProps()} className={wrapper()}>
<div className={zone()}>

View File

@@ -15,16 +15,17 @@ import { IconChevronGrabberVertical } from "../Icons/IconChevronGrabberVertical.
import { tv } from "tailwind-variants";
import { Children, type ComponentProps, type PropsWithChildren } from "react";
type Props = PropsWithChildren<
type Props<T> = PropsWithChildren<
{
id?: string;
placeholder?: string;
onValueChange?: (s: string) => void;
onValueChange?: (s: NonNullable<T>) => void;
variant?: "default" | "editor" | "dashed";
invalid?: boolean;
disabled?: boolean;
className?: string;
} & Omit<ComponentProps<typeof Root>, "onChange">
value?: T;
} & Omit<ComponentProps<typeof Root>, "onChange" | "value">
>;
const select = tv({
@@ -70,19 +71,19 @@ const select = tv({
},
});
export function Select({
export function Select<T>({
placeholder,
children,
onValueChange,
value,
...props
}: Props) {
}: Props<T>) {
const { trigger, content, icon } = select({
...props,
});
return (
<Root onValueChange={onValueChange} value={value}>
<Root onValueChange={onValueChange} value={value as string}>
<Trigger
{...props}
className={trigger({ className: props.className })}
@@ -122,7 +123,7 @@ export function Option({ children, ...props }: ComponentProps<typeof Item>) {
<Item
{...props}
className={
"flex gap-2 items-center h-8 text-sm font-medium text-txt-primary hover:bg-tertiary-hover active:bg-tertiary-pressed cursor-pointer px-[10px]"
"flex gap-2 items-center min-h-8 py-1 text-sm font-medium text-txt-primary hover:bg-tertiary-hover active:bg-tertiary-pressed cursor-pointer px-[10px] text-start"
}
>
<ItemText asChild>

View File

@@ -1,4 +1,9 @@
import { createContext, useContext, type PropsWithChildren } from "react";
import {
createContext,
useContext,
type HTMLAttributes,
type PropsWithChildren,
} from "react";
import { Card } from "../Card/Card";
import { Link } from "react-router";
import clsx from "clsx";
@@ -25,9 +30,13 @@ export function Thead({ children }: PropsWithChildren) {
export function Th({
children,
className,
}: PropsWithChildren<{ className?: string }>) {
width,
}: PropsWithChildren<{ className?: string; width?: number }>) {
return (
<th className={clsx("first:pl-6 last:pr-6 py-3", className)}>
<th
className={clsx("first:pl-6 last:pr-6 py-3", className)}
style={{ width }}
>
{children}
</th>
);
@@ -35,17 +44,21 @@ export function Th({
const TrContext = createContext({} as { to?: string });
export function Tr({ children, to }: PropsWithChildren<{ to?: string }>) {
export function Tr({
to,
className,
...props
}: { to?: string } & HTMLAttributes<HTMLTableRowElement>) {
return (
<TrContext value={{ to }}>
<tr
{...props}
className={clsx(
"border-border-low border-y first:border-none last:border-none",
to && "hover:bg-subtle",
(to || props.onClick) && "hover:bg-subtle",
className,
)}
>
{children}
</tr>
/>
</TrContext>
);
}
@@ -63,15 +76,17 @@ export function Td({
noLink,
className,
width,
}: PropsWithChildren<{
...props
}: {
noLink?: boolean;
className?: string;
width?: number;
}>) {
colSpan?: number;
} & HTMLAttributes<HTMLTableCellElement>) {
const { to } = useContext(TrContext);
if (!to || noLink) {
return (
<td
{...props}
width={width}
className={clsx("first:pl-6 last:pr-6 py-3", className)}
>
@@ -81,6 +96,7 @@ export function Td({
}
return (
<td
{...props}
width={width}
className={clsx(
"first:*:pl-6 *:block last:*:pr-6 *:py-3",

View File

@@ -161,7 +161,6 @@ export const Typography: Story = {
export const Icons: Story = {
render() {
console.log();
return (
<div className="flex flex-wrap gap-4">
{objectKeys(icons).map((k) => {

View File

@@ -0,0 +1,21 @@
import { ImpactOptions } from "./ImpactOptions.tsx";
import type { Meta, StoryObj } from "@storybook/react";
import { Select } from "../../Atoms/Select/Select.tsx";
export default {
title: "Molecules/Select/ImpactSelect",
component: ImpactOptions,
argTypes: {},
} satisfies Meta<typeof ImpactOptions>;
type Story = StoryObj<typeof ImpactOptions>;
export const Default: Story = {
render() {
return (
<Select placeholder="Select impact">
<ImpactOptions />
</Select>
);
},
};

View File

@@ -0,0 +1,47 @@
import { Option } from "../../Atoms/Select/Select";
import { useTranslate } from "@probo/i18n";
export function ImpactOptions() {
const { __ } = useTranslate();
const descriptions = {
LOW: {
label: __("Low"),
description: __("Minimal impact on business"),
},
MEDIUM: {
label: __("Medium"),
description: __("Moderate impact on business"),
},
HIGH: {
label: __("High"),
description: __("Significant business impact"),
},
CRITICAL: {
label: __("Critical"),
description: __("Critical to business operations"),
},
} as const;
return (
<>
{Object.entries(descriptions).map(([key, description]) => (
<Option
key={key}
value={key}
className="border-b border-border-low"
>
<span>
<span className="text-sm font-bold">
{description.label}
</span>
,{" "}
<span className="text-sm text-txt-secondary">
{description.description}
</span>
</span>
</Option>
))}
</>
);
}

View File

@@ -0,0 +1,21 @@
import { SentitivityOptions } from "./SentitivityOptions.tsx";
import type { Meta, StoryObj } from "@storybook/react";
import { Select } from "../../Atoms/Select/Select.tsx";
export default {
title: "Molecules/Select/SentitivitySelect",
component: SentitivityOptions,
argTypes: {},
} satisfies Meta<typeof SentitivityOptions>;
type Story = StoryObj<typeof SentitivityOptions>;
export const Default: Story = {
render() {
return (
<Select placeholder="Select sensitivity">
<SentitivityOptions />
</Select>
);
},
};

View File

@@ -0,0 +1,51 @@
import { Option } from "../../Atoms/Select/Select";
import { useTranslate } from "@probo/i18n";
export function SentitivityOptions() {
const { __ } = useTranslate();
const descriptions = {
NONE: {
label: __("None"),
description: __("No sensitive data"),
},
LOW: {
label: __("Low"),
description: __("Public or non-sensitive data"),
},
MEDIUM: {
label: __("Medium"),
description: __("Internal/restricted data"),
},
HIGH: {
label: __("High"),
description: __("Confidential data"),
},
CRITICAL: {
label: __("Critical"),
description: __("Regulated/PII/financial data"),
},
} as const;
return (
<>
{Object.entries(descriptions).map(([key, description]) => (
<Option
key={key}
value={key}
className="border-b border-border-low"
>
<span>
<span className="text-sm font-bold">
{description.label}
</span>
,{" "}
<span className="text-sm text-txt-secondary">
{description.description}
</span>
</span>
</Option>
))}
</>
);
}

View File

@@ -62,6 +62,8 @@ export { MeasureBadge } from "./Molecules/Badge/MeasureBadge";
export { MeasureImplementation } from "./Molecules/MeasureImplementation/MeasureImplementation";
export { FrameworkSelector } from "./Molecules/FrameworkSelector/FrameworkSelector";
export { DocumentTypeBadge } from "./Molecules/Badge/DocumentTypeBadge";
export { SentitivityOptions } from "./Molecules/Select/SentitivityOptions";
export { ImpactOptions } from "./Molecules/Select/ImpactOptions";
// Hooks
export { useToast, Toasts } from "./Atoms/Toasts/Toasts";