refactor(web): migrate feature screens to design tokens + radius token (#49)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,7 @@ export function DeleteObjectDialog({ id }: { id: string }) {
|
||||
<AlertDialog open={open} onOpenChange={setOpen}>
|
||||
<AlertDialogTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="sm" className="text-red-600">
|
||||
<Button variant="ghost" size="sm" className="text-destructive">
|
||||
{t("actions.delete")}
|
||||
</Button>
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export function DeleteObjectDialog({ id }: { id: string }) {
|
||||
<AlertDialogTitle>{t("actions.delete")}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{t("actions.confirmDelete")}</AlertDialogDescription>
|
||||
{error && (
|
||||
<p role="alert" className="text-sm text-red-600">
|
||||
<p role="alert" className="text-sm text-destructive">
|
||||
{t("form.rejected")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -49,9 +49,9 @@ function TermValue({
|
||||
if (typeof value !== "string") return <>—</>;
|
||||
const term = terms?.find((x) => x.id === value);
|
||||
if (term) return <>{labelText(term.labels, lang)}</>;
|
||||
if (isLoading) return <span className="text-neutral-400">…</span>;
|
||||
if (isLoading) return <span className="text-muted-foreground">…</span>;
|
||||
return (
|
||||
<span className="text-neutral-400">
|
||||
<span className="text-muted-foreground">
|
||||
{value} {t("objects.unknownRef")}
|
||||
</span>
|
||||
);
|
||||
@@ -72,9 +72,9 @@ function AuthorityValue({
|
||||
if (typeof value !== "string") return <>—</>;
|
||||
const authority = authorities?.find((x) => x.id === value);
|
||||
if (authority) return <>{labelText(authority.labels, lang)}</>;
|
||||
if (isLoading) return <span className="text-neutral-400">…</span>;
|
||||
if (isLoading) return <span className="text-muted-foreground">…</span>;
|
||||
return (
|
||||
<span className="text-neutral-400">
|
||||
<span className="text-muted-foreground">
|
||||
{value} {t("objects.unknownRef")}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ export function ObjectDetailDrawer({
|
||||
<div className="flex justify-end border-b p-2">
|
||||
<DrawerClose
|
||||
aria-label={t("actions.closeDetail")}
|
||||
className="rounded p-1 text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900"
|
||||
className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||
>
|
||||
<X className="size-4" aria-hidden="true" />
|
||||
</DrawerClose>
|
||||
|
||||
@@ -19,8 +19,8 @@ function Field({ label, value }: { label: string; value: ReactNode }) {
|
||||
|
||||
return (
|
||||
<div className="border-b py-2">
|
||||
<div className="text-xs uppercase tracking-wide text-neutral-400">{label}</div>
|
||||
<div className="text-sm text-neutral-900">{empty ? "—" : value}</div>
|
||||
<div className="label-caption">{label}</div>
|
||||
<div className="text-sm text-foreground">{empty ? "—" : value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -39,9 +39,9 @@ export function ObjectDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) return <p className="p-4 text-sm text-red-600">{t("objects.loadError")}</p>;
|
||||
if (isError) return <p className="p-4 text-sm text-destructive">{t("objects.loadError")}</p>;
|
||||
|
||||
if (!object) return <p className="p-4 text-sm text-neutral-500">{t("objects.notFound")}</p>;
|
||||
if (!object) return <p className="p-4 text-sm text-muted-foreground">{t("objects.notFound")}</p>;
|
||||
|
||||
// Prefer the active locale's label, then English, then the raw key.
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
@@ -105,7 +105,7 @@ export function ObjectDetail() {
|
||||
/>
|
||||
{groups.map((g) => (
|
||||
<div key={g.group} className="mt-4">
|
||||
<div className="mb-1 text-xs font-medium uppercase text-neutral-500">{g.group}</div>
|
||||
<div className="mb-1 label-caption">{g.group}</div>
|
||||
{g.defs.map((d) => (
|
||||
<Field
|
||||
key={d.key}
|
||||
@@ -119,7 +119,7 @@ export function ObjectDetail() {
|
||||
key={key}
|
||||
label={key}
|
||||
value={
|
||||
<span className="text-neutral-400">
|
||||
<span className="text-muted-foreground">
|
||||
{typeof value === "object" ? JSON.stringify(value) : String(value)}
|
||||
</span>
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function ObjectEditForm() {
|
||||
|
||||
if (isLoading) return <div className="p-4" role="status" aria-label="loading" />;
|
||||
|
||||
if (!object) return <p className="p-4 text-sm text-neutral-500">{t("objects.notFound")}</p>;
|
||||
if (!object) return <p className="p-4 text-sm text-muted-foreground">{t("objects.notFound")}</p>;
|
||||
|
||||
const core: ObjectCore = {
|
||||
object_number: object.object_number,
|
||||
|
||||
@@ -117,7 +117,7 @@ export function ObjectForm({
|
||||
/>
|
||||
|
||||
{errors.core?.[key] && (
|
||||
<p role="alert" className="text-xs text-red-600">
|
||||
<p role="alert" className="text-xs text-destructive">
|
||||
{t("form.required")}
|
||||
</p>
|
||||
)}
|
||||
@@ -127,7 +127,7 @@ export function ObjectForm({
|
||||
return (
|
||||
<form onSubmit={submit} className="space-y-4 overflow-auto p-4">
|
||||
{formError && (
|
||||
<p role="alert" className="text-sm text-red-600">
|
||||
<p role="alert" className="text-sm text-destructive">
|
||||
{formError}
|
||||
</p>
|
||||
)}
|
||||
@@ -147,7 +147,7 @@ export function ObjectForm({
|
||||
|
||||
<select
|
||||
id="visibility"
|
||||
className="w-full rounded border px-2 py-1 text-sm"
|
||||
className="w-full rounded-md border px-2 py-1 text-sm"
|
||||
{...register("visibility")}
|
||||
>
|
||||
<option value="draft">{t("form.draft")}</option>
|
||||
@@ -158,7 +158,7 @@ export function ObjectForm({
|
||||
|
||||
{definitions && definitions.length > 0 && (
|
||||
<fieldset className="space-y-3 border-t pt-3">
|
||||
<legend className="text-xs font-medium uppercase text-neutral-500">
|
||||
<legend className="label-caption">
|
||||
{t("form.flexibleHeading")}
|
||||
</legend>
|
||||
|
||||
@@ -167,7 +167,7 @@ export function ObjectForm({
|
||||
<FieldInput definition={def} form={form} />
|
||||
|
||||
{errors.fields?.[def.key] && (
|
||||
<p role="alert" className="text-xs text-red-600">
|
||||
<p role="alert" className="text-xs text-destructive">
|
||||
{errors.fields[def.key]?.message ?? t("form.required")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -42,7 +42,7 @@ export function ObjectsPage() {
|
||||
type="button"
|
||||
onClick={closeDetail}
|
||||
aria-label={t("actions.closeDetail")}
|
||||
className="rounded p-1 text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900"
|
||||
className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||
>
|
||||
<X className="size-4" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
@@ -138,10 +138,10 @@ export function ObjectsTable() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleSort(col)}
|
||||
className="flex items-center gap-1 hover:text-neutral-900"
|
||||
className="flex items-center gap-1 hover:text-foreground"
|
||||
>
|
||||
{t(COLUMN_KEYS[col])}
|
||||
<Icon className="size-3.5 text-neutral-400" aria-hidden="true" />
|
||||
<Icon className="size-3.5 text-muted-foreground" aria-hidden="true" />
|
||||
</button>
|
||||
</th>
|
||||
);
|
||||
@@ -170,7 +170,7 @@ export function ObjectsTable() {
|
||||
type="button"
|
||||
aria-pressed={active}
|
||||
onClick={() => setVisibility(value)}
|
||||
className={`rounded px-2 py-1 ${active ? "bg-indigo-600 text-white" : "border"}`}
|
||||
className={`rounded-md px-2 py-1 ${active ? "bg-primary text-primary-foreground" : "border"}`}
|
||||
>
|
||||
{value === "all" ? t("search.all") : t(`visibility.${value}`)}
|
||||
</button>
|
||||
@@ -184,7 +184,7 @@ export function ObjectsTable() {
|
||||
);
|
||||
|
||||
const columns = (
|
||||
<thead className="border-b bg-neutral-50 text-xs text-neutral-500">
|
||||
<thead className="border-b bg-muted text-xs text-muted-foreground">
|
||||
<tr>
|
||||
{headerCell("object_number")}
|
||||
{headerCell("object_name")}
|
||||
@@ -220,7 +220,7 @@ export function ObjectsTable() {
|
||||
body = (
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={6} className="px-3 py-6 text-center text-sm text-red-600">
|
||||
<td colSpan={6} className="px-3 py-6 text-center text-sm text-destructive">
|
||||
{t("objects.loadError")}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -230,7 +230,7 @@ export function ObjectsTable() {
|
||||
body = (
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={6} className="px-3 py-6 text-center text-sm text-neutral-500">
|
||||
<td colSpan={6} className="px-3 py-6 text-center text-sm text-muted-foreground">
|
||||
{t("objects.empty")}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -254,17 +254,17 @@ export function ObjectsTable() {
|
||||
if (event.key === "Enter") navigate(`/objects/${object.id}?${params}`);
|
||||
}}
|
||||
className={`cursor-pointer border-b text-sm ${
|
||||
selected ? "bg-indigo-50" : "hover:bg-neutral-50"
|
||||
selected ? "bg-primary/10" : "hover:bg-muted"
|
||||
}`}
|
||||
>
|
||||
<td className="px-3 py-2 text-neutral-500">{object.object_number}</td>
|
||||
<td className="px-3 py-2 text-muted-foreground">{object.object_number}</td>
|
||||
<td className="px-3 py-2 font-medium">{object.object_name}</td>
|
||||
<td className="px-3 py-2">
|
||||
<VisibilityBadge visibility={object.visibility} />
|
||||
</td>
|
||||
<td className="px-3 py-2 text-neutral-600">{object.current_location ?? "—"}</td>
|
||||
<td className="px-3 py-2 text-muted-foreground">{object.current_location ?? "—"}</td>
|
||||
<td className="px-3 py-2 text-right tabular-nums">{object.number_of_objects}</td>
|
||||
<td className="px-3 py-2 text-neutral-600">{formatUpdated(object.updated_at)}</td>
|
||||
<td className="px-3 py-2 text-muted-foreground">{formatUpdated(object.updated_at)}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
@@ -281,14 +281,14 @@ export function ObjectsTable() {
|
||||
{body}
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2 border-t px-3 py-2 text-xs text-neutral-500">
|
||||
<div className="flex items-center justify-between gap-2 border-t px-3 py-2 text-xs text-muted-foreground">
|
||||
<label className="flex items-center gap-1">
|
||||
<span>{t("objects.pageSize")}</span>
|
||||
<select
|
||||
value={limit}
|
||||
onChange={(event) => setLimit(Number(event.target.value))}
|
||||
aria-label={t("objects.pageSize")}
|
||||
className="rounded border bg-white px-1 py-0.5"
|
||||
className="rounded-md border bg-white px-1 py-0.5"
|
||||
>
|
||||
{PAGE_SIZES.map((size) => (
|
||||
<option key={size} value={size}>
|
||||
|
||||
@@ -53,7 +53,7 @@ export function OptionsCombobox({
|
||||
<ComboboxList>
|
||||
{(option: Option) => (
|
||||
<ComboboxItem key={option.id} value={option}>
|
||||
<ComboboxItemIndicator className="text-indigo-600">✓</ComboboxItemIndicator>
|
||||
<ComboboxItemIndicator className="text-primary">✓</ComboboxItemIndicator>
|
||||
{labelText(option.labels, lang)}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
|
||||
@@ -48,7 +48,7 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
|
||||
|
||||
return (
|
||||
<section className="border-t p-4">
|
||||
<div className="mb-2 text-xs font-medium uppercase text-neutral-500">
|
||||
<div className="mb-2 label-caption">
|
||||
{t("publish.heading")}
|
||||
</div>
|
||||
|
||||
@@ -59,10 +59,10 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
|
||||
aria-current={i === currentIndex ? "step" : undefined}
|
||||
className={`flex-1 border px-2 py-1 text-center text-xs ${
|
||||
i === currentIndex
|
||||
? "bg-neutral-800 font-semibold text-white"
|
||||
? "bg-primary font-semibold text-primary-foreground"
|
||||
: i < currentIndex
|
||||
? "bg-neutral-100 text-neutral-600"
|
||||
: "text-neutral-400"
|
||||
? "bg-muted text-muted-foreground"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
{t(`visibility.${step}`)}
|
||||
@@ -117,7 +117,7 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
|
||||
</div>
|
||||
|
||||
{errorKind === "gate" && (
|
||||
<p role="alert" className="mt-2 text-sm text-red-600">
|
||||
<p role="alert" className="mt-2 text-sm text-destructive">
|
||||
{t("publish.gateError")}{" "}
|
||||
<Link to={`/objects/${object.id}/edit`} className="underline">
|
||||
{t("publish.editLink")}
|
||||
@@ -125,12 +125,12 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
|
||||
</p>
|
||||
)}
|
||||
{errorKind === "illegal" && (
|
||||
<p role="alert" className="mt-2 text-sm text-red-600">
|
||||
<p role="alert" className="mt-2 text-sm text-destructive">
|
||||
{t("publish.illegalError")}
|
||||
</p>
|
||||
)}
|
||||
{errorKind === "other" && (
|
||||
<p role="alert" className="mt-2 text-sm text-red-600">
|
||||
<p role="alert" className="mt-2 text-sm text-destructive">
|
||||
{t("form.rejected")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user