refactor(web): adopt useLang + segmentClass/rowStateClass across sites (#66)
This commit is contained in:
@@ -6,17 +6,17 @@ import { FilteredRecordList } from "../components/filtered-record-list";
|
||||
import { LabelledRecordCreateForm } from "../components/labelled-record-create-form";
|
||||
import { PageTitle } from "@/components/ui/page-title";
|
||||
import { AuthorityRow } from "./authority-row";
|
||||
import { focusRing } from "../lib/focus-ring";
|
||||
import { useLang } from "../lib/use-lang";
|
||||
import { segmentClass } from "../lib/class-recipes";
|
||||
import { useDocumentTitle } from "../lib/use-document-title";
|
||||
import { useBreadcrumb } from "../shell/use-breadcrumb";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const KINDS = ["person", "organisation", "place"] as const;
|
||||
|
||||
export function AuthoritiesPage() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const { kind } = useParams();
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
const lang = useLang();
|
||||
|
||||
const isValidKind = (KINDS as readonly string[]).includes(kind ?? "");
|
||||
const currentKind = isValidKind ? (kind as string) : "person";
|
||||
@@ -37,9 +37,7 @@ export function AuthoritiesPage() {
|
||||
<NavLink
|
||||
key={k}
|
||||
to={`/authorities/${k}`}
|
||||
className={({ isActive }) =>
|
||||
cn("rounded-md px-3 py-1 text-sm", focusRing, isActive ? "bg-primary text-primary-foreground" : "border")
|
||||
}
|
||||
className={({ isActive }) => segmentClass(isActive, "px-3 py-1 text-sm")}
|
||||
>
|
||||
{t(`authorities.${k}`)}
|
||||
</NavLink>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import type { components } from "../api/schema";
|
||||
import { useFieldDefinitions, useDeleteFieldDefinition } from "../api/queries";
|
||||
import { useLang } from "../lib/use-lang";
|
||||
import { rowStateClass } from "../lib/class-recipes";
|
||||
import { labelText } from "../lib/labels";
|
||||
import { byLabel, compareStrings } from "../lib/sort";
|
||||
import { focusRing } from "../lib/focus-ring";
|
||||
@@ -21,10 +23,10 @@ export function FieldList({
|
||||
selectedKey: string | null;
|
||||
onSelect: (def: FieldDefinitionView) => void;
|
||||
}) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const { data, isLoading, isError } = useFieldDefinitions();
|
||||
const deleteField = useDeleteFieldDefinition();
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
const lang = useLang();
|
||||
const [filter, setFilter] = useState("");
|
||||
|
||||
if (isLoading) return <ListSkeleton rows={6} />;
|
||||
@@ -82,9 +84,9 @@ export function FieldList({
|
||||
{[...defs].sort(byLabel(lang)).map((def) => (
|
||||
<li
|
||||
key={def.key}
|
||||
className={`flex items-center gap-2 border-b px-3 py-2 text-sm ${
|
||||
def.key === selectedKey ? "bg-primary/10" : ""
|
||||
}`}
|
||||
className={`flex items-center gap-2 border-b px-3 py-2 text-sm ${rowStateClass(
|
||||
def.key === selectedKey,
|
||||
)}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import type { components } from "../api/schema";
|
||||
import { useAuthorities, useTerms } from "../api/queries";
|
||||
import { useConfig } from "../config/config-context";
|
||||
import { useLang } from "../lib/use-lang";
|
||||
import { labelText } from "../lib/labels";
|
||||
import { OptionsCombobox } from "./options-combobox";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
@@ -27,9 +28,9 @@ export function FieldInput<TValues extends { fields: Record<string, unknown> }>(
|
||||
definition: FieldDefinitionView;
|
||||
form: FieldForm<TValues>;
|
||||
}) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const { default_language } = useConfig();
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
const lang = useLang();
|
||||
const label = labelText(definition.labels, lang);
|
||||
const name = fieldPath<TValues>(definition.key);
|
||||
const placeholder = t("form.selectPlaceholder");
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import type { components } from "../api/schema";
|
||||
import { useObject, useFieldDefinitions } from "../api/queries";
|
||||
import { useLang } from "../lib/use-lang";
|
||||
import { groupDefinitions } from "../lib/group-fields";
|
||||
import { formatDate } from "../lib/format-date";
|
||||
import { useDocumentTitle } from "../lib/use-document-title";
|
||||
@@ -49,14 +50,14 @@ export function ObjectDetail() {
|
||||
}
|
||||
|
||||
function ObjectDetailLoaded({ object }: { object: AdminObjectView }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const { data: definitions } = useFieldDefinitions();
|
||||
|
||||
useDocumentTitle(object.object_number);
|
||||
useBreadcrumb([{ label: t("nav.objects"), to: "/objects" }, { label: object.object_number }]);
|
||||
|
||||
// Prefer the active locale's label, then English, then the raw key.
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
const lang = useLang();
|
||||
const labelFor = (key: string) => {
|
||||
const labels = definitions?.find((d) => d.key === key)?.labels;
|
||||
const byLang = labels?.find((l) => l.lang === lang)?.label;
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { components } from "../api/schema";
|
||||
import { useObjectsPage } from "../api/queries";
|
||||
import { useDebouncedValue } from "../lib/use-debounced-value";
|
||||
import { focusRing } from "../lib/focus-ring";
|
||||
import { segmentClass, rowStateClass } from "../lib/class-recipes";
|
||||
import { useConfig } from "../config/config-context";
|
||||
import { VisibilityBadge } from "./visibility-badge";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
@@ -171,7 +172,7 @@ export function ObjectsTable() {
|
||||
type="button"
|
||||
aria-pressed={active}
|
||||
onClick={() => setVisibility(value)}
|
||||
className={`${focusRing} rounded-md px-2 py-1 ${active ? "bg-primary text-primary-foreground" : "border"}`}
|
||||
className={segmentClass(active, "px-2 py-1")}
|
||||
>
|
||||
{value === "all" ? t("search.all") : t(`visibility.${value}`)}
|
||||
</button>
|
||||
@@ -248,9 +249,7 @@ export function ObjectsTable() {
|
||||
<tr
|
||||
key={object.id}
|
||||
onClick={() => navigate(`/objects/${object.id}?${params}`)}
|
||||
className={`cursor-pointer border-b text-sm ${
|
||||
selected ? "bg-primary/10" : "hover:bg-muted"
|
||||
}`}
|
||||
className={`cursor-pointer border-b text-sm ${rowStateClass(selected)}`}
|
||||
>
|
||||
<td className="px-3 py-2 text-muted-foreground">
|
||||
<Link
|
||||
|
||||
@@ -4,9 +4,8 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useSearch, HttpError } from "../api/queries";
|
||||
import { useDebouncedValue } from "../lib/use-debounced-value";
|
||||
import { focusRing } from "../lib/focus-ring";
|
||||
import { segmentClass } from "../lib/class-recipes";
|
||||
import { SearchResultRow } from "./search-result-row";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { ListSkeleton } from "@/components/ui/skeletons";
|
||||
@@ -73,7 +72,7 @@ export function SearchPanel() {
|
||||
type="button"
|
||||
aria-pressed={active}
|
||||
onClick={() => setVisibility(value)}
|
||||
className={cn("rounded-md px-2 py-0.5", focusRing, active ? "bg-primary text-primary-foreground" : "border")}
|
||||
className={segmentClass(active, "px-2 py-0.5")}
|
||||
>
|
||||
{value === "all" ? t("search.all") : t(`visibility.${value}`)}
|
||||
</button>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { NavLink } from "react-router-dom";
|
||||
|
||||
import type { components } from "../api/schema";
|
||||
import { VisibilityBadge } from "../objects/visibility-badge";
|
||||
import { rowStateClass } from "../lib/class-recipes";
|
||||
import { Highlight } from "./highlight";
|
||||
|
||||
type SearchHitView = components["schemas"]["SearchHitView"];
|
||||
@@ -12,7 +13,7 @@ export function SearchResultRow({ hit }: { hit: SearchHitView }) {
|
||||
<NavLink
|
||||
to={`/search/${hit.id}`}
|
||||
className={({ isActive }) =>
|
||||
`block border-b px-3 py-2 ${isActive ? "bg-primary/10" : "hover:bg-muted"}`
|
||||
`block border-b px-3 py-2 ${rowStateClass(isActive)}`
|
||||
}
|
||||
>
|
||||
<div className="text-sm font-semibold">{hit.object_name}</div>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { NavLink } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useVocabularies, useCreateVocabulary, useRenameVocabulary, useDeleteVocabulary } from "../api/queries";
|
||||
import { useLang } from "../lib/use-lang";
|
||||
import { rowStateClass } from "../lib/class-recipes";
|
||||
import { byKey } from "../lib/sort";
|
||||
import { DeleteConfirmDialog } from "../components/delete-confirm-dialog";
|
||||
import { MutationError } from "../components/mutation-error";
|
||||
@@ -12,9 +14,9 @@ import { Label } from "@/components/ui/label";
|
||||
import { ListSkeleton } from "@/components/ui/skeletons";
|
||||
|
||||
export function VocabularyList() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
const lang = useLang();
|
||||
|
||||
const { data, isLoading, isError } = useVocabularies();
|
||||
|
||||
@@ -110,7 +112,7 @@ export function VocabularyList() {
|
||||
<NavLink
|
||||
to={`/vocabularies/${v.id}`}
|
||||
className={({ isActive }) =>
|
||||
`block flex-1 px-3 py-2 text-sm ${isActive ? "bg-primary/10" : "hover:bg-muted"}`
|
||||
`block flex-1 px-3 py-2 text-sm ${rowStateClass(isActive)}`
|
||||
}
|
||||
>
|
||||
{v.key}
|
||||
|
||||
@@ -2,15 +2,16 @@ import { useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useTerms, useAddTerm, useVocabularies } from "../api/queries";
|
||||
import { useLang } from "../lib/use-lang";
|
||||
import { useBreadcrumb } from "../shell/use-breadcrumb";
|
||||
import { FilteredRecordList } from "../components/filtered-record-list";
|
||||
import { LabelledRecordCreateForm } from "../components/labelled-record-create-form";
|
||||
import { TermRow } from "./term-row";
|
||||
|
||||
export function VocabularyTerms() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t } = useTranslation();
|
||||
const { id } = useParams();
|
||||
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
|
||||
const lang = useLang();
|
||||
|
||||
const { data: terms, isLoading, isError } = useTerms(id);
|
||||
const addTerm = useAddTerm();
|
||||
|
||||
Reference in New Issue
Block a user