feat(frontend): i18n template pages (list, form, picker modal)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { extractApiError } from '@/api/client';
|
||||
import type { SimulationTemplate } from '@/api/types';
|
||||
import { useTemplates } from '@/hooks/useTemplates';
|
||||
@@ -23,6 +24,7 @@ export function TemplatePickerModal({
|
||||
onSelectTemplate,
|
||||
isPending = false,
|
||||
}: TemplatePickerModalProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const { data, isLoading, isError, error, refetch } = useTemplates();
|
||||
|
||||
return (
|
||||
@@ -37,11 +39,11 @@ export function TemplatePickerModal({
|
||||
<div className="relative card-product max-w-xl w-full mx-md flex flex-col gap-md max-h-[80vh] overflow-hidden">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 id="tpl-picker-title" className="text-[20px] font-medium text-ink">
|
||||
From template…
|
||||
{t('template.picker.title')}
|
||||
</h2>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Close"
|
||||
aria-label={t('common.close')}
|
||||
onClick={onClose}
|
||||
className="text-graphite hover:text-ink text-[20px] leading-none"
|
||||
>
|
||||
@@ -50,19 +52,18 @@ export function TemplatePickerModal({
|
||||
</div>
|
||||
|
||||
<div className="overflow-y-auto flex-1 -mx-xl px-xl">
|
||||
{isLoading ? <LoadingState label="Loading templates…" /> : null}
|
||||
{isLoading ? <LoadingState label={t('template.picker.loading')} /> : null}
|
||||
|
||||
{isError ? (
|
||||
<ErrorState
|
||||
message={extractApiError(error, 'Could not load templates')}
|
||||
message={extractApiError(error, t('template.form.errorLoad'))}
|
||||
onRetry={() => refetch()}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!isLoading && !isError && data && data.length === 0 ? (
|
||||
<EmptyState
|
||||
title="No templates available"
|
||||
description="Create one from the Templates page."
|
||||
title={t('template.picker.empty')}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -70,22 +71,22 @@ export function TemplatePickerModal({
|
||||
<table className="w-full text-left" data-testid="template-picker-table">
|
||||
<thead className="bg-cloud border-b border-hairline">
|
||||
<tr className="text-[12px] uppercase tracking-[0.5px] text-graphite">
|
||||
<th className="px-md py-sm">Name</th>
|
||||
<th className="px-md py-sm">MITRE</th>
|
||||
<th className="px-md py-sm">Created by</th>
|
||||
<th className="px-md py-sm">{t('template.list.col.name')}</th>
|
||||
<th className="px-md py-sm">{t('simulation.list.col.mitre')}</th>
|
||||
<th className="px-md py-sm">{t('template.list.col.createdBy')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((t) => (
|
||||
{data.map((template) => (
|
||||
<tr
|
||||
key={t.id}
|
||||
key={template.id}
|
||||
className="border-b border-hairline last:border-0 hover:bg-cloud cursor-pointer"
|
||||
onClick={() => !isPending && onSelectTemplate(t)}
|
||||
data-testid={`template-row-${t.id}`}
|
||||
onClick={() => !isPending && onSelectTemplate(template)}
|
||||
data-testid={`template-row-${template.id}`}
|
||||
>
|
||||
<td className="px-md py-sm text-ink font-medium">{t.name}</td>
|
||||
<td className="px-md py-sm text-charcoal text-[14px]">{mitreCount(t)}</td>
|
||||
<td className="px-md py-sm text-charcoal text-[14px]">{t.created_by.username}</td>
|
||||
<td className="px-md py-sm text-ink font-medium">{template.name}</td>
|
||||
<td className="px-md py-sm text-charcoal text-[14px]">{mitreCount(template)}</td>
|
||||
<td className="px-md py-sm text-charcoal text-[14px]">{template.created_by.username}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@@ -95,7 +96,7 @@ export function TemplatePickerModal({
|
||||
|
||||
<div className="border-t border-hairline pt-sm">
|
||||
<button type="button" className="btn-outline-ink" onClick={onClose}>
|
||||
Cancel
|
||||
{t('common.cancel')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user