Add react i18next to console

Signed-off-by: Jonathan <contact@grafikart.fr>
This commit is contained in:
Jonathan
2026-07-21 16:09:41 +02:00
committed by Bryan Frimin
parent 1b7b2594eb
commit a7ff5f07bc
420 changed files with 10251 additions and 6825 deletions

View File

@@ -38,6 +38,7 @@
"cmdk": "^1.1.1",
"mermaid": "^11.15.0",
"react-dropzone": "^15.0.0",
"react-i18next": "^17.0.10",
"react-intersection-observer": "^10.0.3",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",

View File

@@ -19,9 +19,9 @@
// SOFTWARE.
import { useRefSync } from "@probo/hooks";
import { useTranslate } from "@probo/i18n";
import { useCallback } from "react";
import { type FileRejection, useDropzone } from "react-dropzone";
import { useTranslation } from "react-i18next";
import { tv } from "tailwind-variants";
import { IconPageCross, IconUpload } from "../Icons";
@@ -79,7 +79,7 @@ export const dropzone = tv({
const MB = 1024 * 1024;
export function Dropzone(props: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const onDropRef = useRefSync(props.onDrop);
const onDrop = useCallback(
(files: File[]) => {
@@ -94,7 +94,7 @@ export function Dropzone(props: Props) {
onDrop,
maxSize: props.maxSize ? props.maxSize * MB : undefined,
});
const error = getDropzoneError(__, fileRejections);
const error = getDropzoneError(t, fileRejections);
const { wrapper, zone, title, description } = dropzone({
...props,
isDragActive,
@@ -109,7 +109,7 @@ export function Dropzone(props: Props) {
? (
<div className={title({ isDragActive: true })}>
<Spinner />
{__("Uploading file")}
{t("ui.dropzone.uploading")}
</div>
)
: (
@@ -122,7 +122,7 @@ export function Dropzone(props: Props) {
: (
<IconUpload size={20} />
)}
{error ?? __("Drag and drop or browse files")}
{error ?? t("ui.dropzone.browse")}
</div>
<div className={description()}>{props.description}</div>
</div>
@@ -133,7 +133,7 @@ export function Dropzone(props: Props) {
}
function getDropzoneError(
__: (s: string) => string,
t: (key: string) => string,
fileRejections: readonly FileRejection[],
) {
if (fileRejections.length === 0) {
@@ -143,10 +143,10 @@ function getDropzoneError(
const code = fileRejections[0].errors[0].code;
switch (code) {
case "file-invalid-type":
return __("This file is not supported");
return t("ui.dropzone.errors.unsupported");
case "file-too-large":
return __("This file is too large");
return t("ui.dropzone.errors.tooLarge");
default:
return __("Something went wrong");
return t("ui.dropzone.errors.generic");
}
}

View File

@@ -19,8 +19,8 @@
// SOFTWARE.
import { useRefSync } from "@probo/hooks";
import { useTranslate } from "@probo/i18n";
import { type ReactNode, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useInView } from "react-intersection-observer";
import { Spinner } from "../Spinner/Spinner";
@@ -32,7 +32,7 @@ type Props = {
};
export function InfiniteScrollTrigger({ children, onView, loading }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const { ref, inView } = useInView({
threshold: 0,
});
@@ -49,7 +49,7 @@ export function InfiniteScrollTrigger({ children, onView, loading }: Props) {
{children ?? (
<>
<Spinner size={16} />
{__("Loading")}
{t("ui.loading")}
</>
)}
</div>

View File

@@ -18,8 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { getDocumentClassificationLabel } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -28,7 +27,7 @@ type Props = {
};
export function DocumentClassificationBadge({ classification }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
// Choose badge variant based on classification level
const variant = (() => {
@@ -48,7 +47,7 @@ export function DocumentClassificationBadge({ classification }: Props) {
return (
<Badge variant={variant}>
{getDocumentClassificationLabel(__, classification)}
{t(`ui.documentClassification.${classification.toLowerCase()}`)}
</Badge>
);
}

View File

@@ -18,8 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { getDocumentTypeLabel } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -28,6 +27,10 @@ type Props = {
};
export function DocumentTypeBadge({ type }: Props) {
const { __ } = useTranslate();
return <Badge variant="neutral">{getDocumentTypeLabel(__, type)}</Badge>;
const { t } = useTranslation();
return (
<Badge variant="neutral">
{t(`ui.documentType.${type.toLowerCase()}`)}
</Badge>
);
}

View File

@@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -27,7 +27,7 @@ type Props = {
};
export function DocumentVersionBadge(props: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const variant = {
DRAFT: "neutral",
@@ -36,9 +36,9 @@ export function DocumentVersionBadge(props: Props) {
} as const;
const label = {
DRAFT: __("Draft"),
PENDING_APPROVAL: __("Pending approval"),
PUBLISHED: __("Published"),
DRAFT: t("ui.documentVersionBadge.draft"),
PENDING_APPROVAL: t("ui.documentVersionBadge.pendingApproval"),
PUBLISHED: t("ui.documentVersionBadge.published"),
} as const;
return <Badge variant={variant[props.state]}>{label[props.state]}</Badge>;

View File

@@ -19,9 +19,8 @@
// SOFTWARE.
import type { measureStates } from "@probo/helpers";
import { getMeasureStateLabel } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import type { ComponentProps } from "react";
import { useTranslation } from "react-i18next";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -42,10 +41,10 @@ const stateToVariant: Record<
};
export function MeasureBadge({ state }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
return (
<Badge variant={stateToVariant[state]}>
{getMeasureStateLabel(__, state)}
{t(`ui.measureState.${state.toLowerCase()}`)}
</Badge>
);
}

View File

@@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -51,30 +51,30 @@ const badgeVariant = (level: string | number) => {
};
export function RiskBadge({ level }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const label = () => {
if (typeof level === "number") {
if (level >= 15) {
return __("High");
return t("ui.risk.severity.high");
}
if (level >= 8) {
return __("Medium");
return t("ui.risk.severity.medium");
}
return __("Low");
return t("ui.risk.severity.low");
}
switch (level) {
case "CRITICAL":
return __("Critical");
return t("ui.risk.severity.critical");
case "HIGH":
return __("High");
return t("ui.risk.severity.high");
case "LOW":
return __("Low");
return t("ui.risk.severity.low");
case "MEDIUM":
return __("Medium");
return t("ui.risk.severity.medium");
case "NONE":
return __("None");
return t("ui.risk.severity.none");
default:
return __("Low");
return t("ui.risk.severity.low");
}
};
return <Badge variant={badgeVariant(level)}>{label()}</Badge>;

View File

@@ -18,8 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { getSeverity } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -28,7 +27,15 @@ type Props = {
};
export function SeverityBadge({ score }: Props) {
const { __ } = useTranslate();
const severity = getSeverity(__, score);
return <Badge variant={severity?.variant}>{severity?.label}</Badge>;
const { t } = useTranslation();
const severity = score >= 15
? { variant: "danger" as const, key: "critical" }
: score >= 5
? { variant: "warning" as const, key: "high" }
: { variant: "neutral" as const, key: "low" };
return (
<Badge variant={severity.variant}>
{t(`ui.risk.severity.${severity.key}`)}
</Badge>
);
}

View File

@@ -18,7 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import {
Cancel,
Content,
@@ -29,6 +28,7 @@ import {
} from "@radix-ui/react-alert-dialog";
import { Root as Portal } from "@radix-ui/react-portal";
import { type ComponentProps, useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { create } from "zustand";
import { combine } from "zustand/middleware";
@@ -70,7 +70,7 @@ const useConfirmStore = create(
*/
export function useConfirm() {
const open = useConfirmStore(state => state.open);
const { __ } = useTranslate();
const { t } = useTranslation();
return useCallback(
(cb: State["onConfirm"], props: Omit<State, "onConfirm">) => {
@@ -78,12 +78,12 @@ export function useConfirm() {
onConfirm: cb,
...props,
message: props.message,
title: props.title ?? __("Are you sure ?"),
title: props.title ?? t("ui.confirmDialog.defaultTitle"),
variant: props.variant ?? "danger",
label: props.label ?? __("Delete"),
label: props.label ?? t("ui.actions.delete"),
});
},
[open, __],
[open, t],
);
}
@@ -109,7 +109,7 @@ function ConfirmDialogContent() {
const onConfirm = useConfirmStore(state => state.onConfirm);
const close = useConfirmStore(state => state.close);
const { __ } = useTranslate();
const { t } = useTranslation();
const isOpen = !!message;
const [loading, setLoading] = useState(false);
@@ -156,7 +156,7 @@ function ConfirmDialogContent() {
<footer className={dialogStyles.footer}>
<Cancel asChild>
<Button disabled={loading} variant="tertiary">
{__("Cancel")}
{t("ui.actions.cancel")}
</Button>
</Cancel>
<Button

View File

@@ -18,7 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import {
Close,
Content,
@@ -42,6 +41,7 @@ import {
useRef,
useState,
} from "react";
import { useTranslation } from "react-i18next";
import { tv } from "tailwind-variants";
import { Button } from "../../Atoms/Button/Button";
@@ -210,14 +210,14 @@ export function DialogFooter({
// switches to justify-between; otherwise it keeps the default right alignment.
start?: ReactNode;
}) {
const { __ } = useTranslate();
const { t } = useTranslation();
const { footer } = dialog();
return (
<footer className={footer({ className: clsx(start != null && "justify-between", className) })}>
{start != null && <div className="flex items-center gap-2">{start}</div>}
<div className="flex items-center gap-2">
<Close asChild>
<Button variant="secondary">{exitLabel ?? __("Cancel")}</Button>
<Button variant="secondary">{exitLabel ?? t("ui.actions.cancel")}</Button>
</Close>
{children}
</div>

View File

@@ -18,8 +18,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import type { HTMLAttributes } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "../../Atoms/Button/Button";
import { IconPlusLarge } from "../../Atoms/Icons";
@@ -60,7 +60,7 @@ const parse = (value: string): { amount: number; unit: string } => {
};
export function DurationPicker({ value, onValueChange, ...props }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
if (!value) {
return (
<div>
@@ -92,10 +92,10 @@ export function DurationPicker({ value, onValueChange, ...props }: Props) {
onValueChange={(v: string) =>
onValueChange(stringify(amount, v))}
>
<Option value="M">{__("Minutes")}</Option>
<Option value="H">{__("Hours")}</Option>
<Option value="D">{__("Days")}</Option>
<Option value="W">{__("Weeks")}</Option>
<Option value="M">{t("ui.durationPicker.minutes")}</Option>
<Option value="H">{t("ui.durationPicker.hours")}</Option>
<Option value="D">{t("ui.durationPicker.days")}</Option>
<Option value="W">{t("ui.durationPicker.weeks")}</Option>
</Select>
</div>
);

View File

@@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Button } from "../../Atoms/Button/Button";
import { Dropdown, DropdownItem } from "../../Atoms/Dropdown/Dropdown";
@@ -128,7 +128,7 @@ type Props = {
};
export function FrameworkSelector({ disabled, onSelect }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
return (
<Dropdown
toggle={(
@@ -137,7 +137,7 @@ export function FrameworkSelector({ disabled, onSelect }: Props) {
iconAfter={IconChevronDown}
disabled={disabled}
>
{__("New framework")}
{t("ui.frameworkSelector.new")}
</Button>
)}
>
@@ -154,7 +154,7 @@ export function FrameworkSelector({ disabled, onSelect }: Props) {
}
function FrameworkItem(props: { framework?: Framework; onClick: () => void }) {
const { __ } = useTranslate();
const { t } = useTranslation();
if (!props.framework) {
return (
<DropdownItem onClick={props.onClick} className="">
@@ -163,10 +163,10 @@ function FrameworkItem(props: { framework?: Framework; onClick: () => void }) {
</div>
<div className="space-y-[2px]">
<div className="text-sm font-medium">
{__("Custom framework")}
{t("ui.frameworkSelector.custom.title")}
</div>
<div className="text-xs text-txt-secondary">
{__("Start from scratch")}
{t("ui.frameworkSelector.custom.description")}
</div>
</div>
</DropdownItem>

View File

@@ -18,10 +18,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { getMeasureStateLabel, measureStates } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { measureStates } from "@probo/helpers";
import { clsx } from "clsx";
import type { ComponentProps } from "react";
import { useTranslation } from "react-i18next";
import type { MeasureBadge } from "../Badge/MeasureBadge";
@@ -42,7 +42,7 @@ const stateToColor: Record<MeasureState, string> = {
};
export function MeasureImplementation({ measures, className }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const counts = measures.reduce(
(acc, measure) => {
acc[measure.state] = (acc[measure.state] ?? 0) + 1;
@@ -58,7 +58,7 @@ export function MeasureImplementation({ measures, className }: Props) {
return (
<div className={clsx("space-y-3", className)}>
<h2 className="text-base font-medium">
{__("Measure implementation")}
{t("ui.measureImplementation.title")}
</h2>
<div className="h-2 rounded overflow-hidden bg-highlight flex justify-stretch item-stretch">
{measureStates.map(state => (
@@ -76,7 +76,7 @@ export function MeasureImplementation({ measures, className }: Props) {
<div className="mr-auto">
{percent}
%
{__("Complete")}
{t("ui.measureImplementation.complete")}
</div>
)}
{measureStates.map(state => (
@@ -91,7 +91,7 @@ export function MeasureImplementation({ measures, className }: Props) {
)}
>
</div>
{getMeasureStateLabel(__, state)}
{t(`ui.measureState.${state.toLowerCase()}`)}
</div>
))}
</div>

View File

@@ -23,8 +23,8 @@ import {
getRiskLikelihoods,
getSeverity,
} from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { clsx } from "clsx";
import { useTranslation } from "react-i18next";
import { Card } from "../../Atoms/Card/Card";
@@ -48,29 +48,31 @@ const getColor = (score: number): string => {
};
export function RiskOverview({ type, risk }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const impact = risk?.[`${type}Impact`] ?? 0;
const likelihood = risk?.[`${type}Likelihood`] ?? 0;
const severity = getSeverity(__, impact * likelihood);
const severity = getSeverity(t, impact * likelihood);
return (
<Card padded>
<h2 className="font-semibold text-base mb-6">
{type === "inherent" ? __("Initial Risk") : __("Residual Risk")}
{type === "inherent"
? t("ui.risk.initial")
: t("ui.risk.residual")}
</h2>
<div className="grid grid-cols-2 gap-4 mb-4">
<RiskOverviewBadge
label={__("Impact")}
label={t("ui.risk.impact.label")}
textCb={getRiskImpacts}
score={impact}
/>
<RiskOverviewBadge
label={__("Likelihood")}
label={t("ui.risk.likelihood.label")}
textCb={getRiskLikelihoods}
score={likelihood}
/>
</div>
<div className="space-y-2">
<div className="font-medium text-xs">{__("Score")}</div>
<div className="font-medium text-xs">{t("ui.risk.score")}</div>
<div
className={clsx(
severity?.bg,
@@ -94,17 +96,17 @@ function RiskOverviewBadge({
label: string;
textCb: (t: (s: string) => string) => { value: number; label: string }[];
}) {
const { __ } = useTranslate();
const { t } = useTranslation();
return (
<div className="space-y-2">
<div className="font-medium text-xs">{__(label)}</div>
<div className="font-medium text-xs">{label}</div>
<div
className={clsx(
getColor(score),
"py-2 text-sm font-semibold rounded-lg text-txt-invert text-center",
)}
>
{textCb(__).find(i => i.value === score)?.label}
{textCb(t).find(i => i.value === score)?.label}
{" "}
(
{score}

View File

@@ -19,9 +19,9 @@
// SOFTWARE.
import { getRiskImpacts, getRiskLikelihoods, groupBy } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
import { clsx } from "clsx";
import { Fragment, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import { Card } from "../../Atoms/Card/Card";
@@ -66,12 +66,16 @@ const cellKey = (impact: number, likelihood: number) =>
* Displays a grid of risk grouped by impact & likelihood
*/
export function RisksChart({ organizationId, type, risks }: Props) {
const { __ } = useTranslate();
const { t } = useTranslation();
const legend = [__("Low"), __("High"), __("Critical")];
const legend = [
t("ui.risk.severity.low"),
t("ui.risk.severity.high"),
t("ui.risk.severity.critical"),
];
const impacts = getRiskImpacts(__).reverse();
const likelihoods = getRiskLikelihoods(__);
const impacts = getRiskImpacts(t).reverse();
const likelihoods = getRiskLikelihoods(t);
const impactField
= type === "inherent" ? "inherentImpact" : "residualImpact";
const likelihoodField
@@ -88,8 +92,8 @@ export function RisksChart({ organizationId, type, risks }: Props) {
<div className="flex justify-between items-center mb-6">
<h2 className="font-semibold text-lg">
{type === "inherent"
? __("Initial Risk")
: __("Residual Risk")}
? t("ui.risk.initial")
: t("ui.risk.residual")}
</h2>
<div className="flex gap-3">
{legend.map((label, i) => (
@@ -114,7 +118,7 @@ export function RisksChart({ organizationId, type, risks }: Props) {
className="text-xs font-medium flex-none text-center"
style={{ writingMode: "sideways-lr" }}
>
{__("Impact")}
{t("ui.risk.impact.label")}
</div>
<div className="grid grid-cols-[90px_1fr_1fr_1fr_1fr_1fr] gap-1 w-full">
{impacts.map(impact => (
@@ -158,7 +162,7 @@ export function RisksChart({ organizationId, type, risks }: Props) {
)
{likelihood.value === 3 && (
<div className="text-xs text-txt-primary font-medium flex-none text-center mt-3">
{__("Likelihood")}
{t("ui.risk.likelihood.label")}
</div>
)}
</div>
@@ -180,7 +184,7 @@ function RisksChartCell({
likelihood: number;
organizationId: string;
}) {
const { __ } = useTranslate();
const { t } = useTranslation();
const level = getLevel(impact * likelihood);
const baseClass
= "flex items-center justify-center aspect-square rounded-xl text-txt-invert text-sm font-semibold";
@@ -189,9 +193,9 @@ function RisksChartCell({
}
const infos = [
{ label: __("Number of risks"), value: risks.length },
{ label: __("Impact"), value: impact },
{ label: __("Likelihood"), value: likelihood },
{ label: t("ui.risk.numberOfRisks"), value: risks.length },
{ label: t("ui.risk.impact.label"), value: impact },
{ label: t("ui.risk.likelihood.label"), value: likelihood },
];
return (
@@ -224,7 +228,9 @@ function RisksChartCell({
<div className="text-txt-primary">{impact * likelihood}</div>
</div>
<DropdownSeparator className="my-3" />
<div className="text-txt-secondary mb-1">{__("Linked Risks")}</div>
<div className="text-txt-secondary mb-1">
{t("ui.risk.linkedRisks")}
</div>
{risks.map(risk => (
<DropdownItem key={risk.id} asChild>
<Link

View File

@@ -18,29 +18,29 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Option } from "../../Atoms/Select/Select";
export function ImpactOptions() {
const { __ } = useTranslate();
const { t } = useTranslation();
const descriptions = {
LOW: {
label: __("Low"),
description: __("Minimal impact on business"),
label: t("ui.risk.severity.low"),
description: t("ui.impactOptions.low"),
},
MEDIUM: {
label: __("Medium"),
description: __("Moderate impact on business"),
label: t("ui.risk.severity.medium"),
description: t("ui.impactOptions.medium"),
},
HIGH: {
label: __("High"),
description: __("Significant business impact"),
label: t("ui.risk.severity.high"),
description: t("ui.impactOptions.high"),
},
CRITICAL: {
label: __("Critical"),
description: __("Critical to business operations"),
label: t("ui.risk.severity.critical"),
description: t("ui.impactOptions.critical"),
},
} as const;

View File

@@ -18,33 +18,33 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import { useTranslate } from "@probo/i18n";
import { useTranslation } from "react-i18next";
import { Option } from "../../Atoms/Select/Select";
export function SentitivityOptions() {
const { __ } = useTranslate();
const { t } = useTranslation();
const descriptions = {
NONE: {
label: __("None"),
description: __("No sensitive data"),
label: t("ui.risk.severity.none"),
description: t("ui.sensitivityOptions.none"),
},
LOW: {
label: __("Low"),
description: __("Public or non-sensitive data"),
label: t("ui.risk.severity.low"),
description: t("ui.sensitivityOptions.low"),
},
MEDIUM: {
label: __("Medium"),
description: __("Internal/restricted data"),
label: t("ui.risk.severity.medium"),
description: t("ui.sensitivityOptions.medium"),
},
HIGH: {
label: __("High"),
description: __("Confidential data"),
label: t("ui.risk.severity.high"),
description: t("ui.sensitivityOptions.high"),
},
CRITICAL: {
label: __("Critical"),
description: __("Regulated/PII/financial data"),
label: t("ui.risk.severity.critical"),
description: t("ui.sensitivityOptions.critical"),
},
} as const;

View File

@@ -19,9 +19,9 @@
// SOFTWARE.
import { useStateWithRef } from "@probo/hooks";
import { useTranslate } from "@probo/i18n";
import { Command } from "cmdk";
import { Fragment, type ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { tv } from "tailwind-variants";
import { Badge } from "../../Atoms/Badge/Badge";
@@ -54,7 +54,7 @@ export function SelectCell<T extends NonNullable<unknown>>(props: Props<T>) {
props.defaultValue,
);
const cellRef = useEditableCellRef();
const { __ } = useTranslate();
const { t } = useTranslation();
const filteredValue = Array.isArray(value) ? value.filter(v => v !== undefined) : value ? [value] : [];
const usedKeys = new Set<string>(filteredValue.map(getKey).filter(Boolean) as string[]);
const { onUpdate } = useEditableRowContext();
@@ -105,7 +105,7 @@ export function SelectCell<T extends NonNullable<unknown>>(props: Props<T>) {
{props.multiple && (
<Command.Input
className={classNames.input()}
placeholder={__("Search")}
placeholder={t("ui.actions.search")}
/>
)}
<Command.List>