Translate admin pages - Create admin.json files and translate all admin pages

- Create admin.json translation files for es, en, ca with all admin strings
- Update IntlProvider to include admin namespace
- Translate admin/invites/page.tsx - all strings now use translations
- Translate admin/trades/page.tsx - all strings now use translations
- Translate admin/price-history/page.tsx - all strings now use translations
- Translate admin/availability/page.tsx - all strings now use translations
- Add 'saving' key to common.json for all languages
- Fix linting errors: add t to useCallback dependencies
- All admin pages now fully multilingual
This commit is contained in:
counterweight 2025-12-26 11:49:50 +01:00
parent b8b3e8b9f6
commit e2376855ce
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
11 changed files with 473 additions and 87 deletions

View file

@ -5,6 +5,7 @@ import { Permission } from "../../auth-context";
import { adminApi } from "../../api";
import { Header } from "../../components/Header";
import { useRequireAuth } from "../../hooks/useRequireAuth";
import { useTranslation } from "../../hooks/useTranslation";
import { components } from "../../generated/api";
import constants from "../../../../shared/constants.json";
import {
@ -49,6 +50,8 @@ interface EditSlot {
}
export default function AdminAvailabilityPage() {
const t = useTranslation("admin");
const tCommon = useTranslation("common");
const { user, isLoading, isAuthorized } = useRequireAuth({
requiredPermission: Permission.MANAGE_AVAILABILITY,
fallbackRedirect: "/",
@ -145,7 +148,7 @@ export default function AdminAvailabilityPage() {
await fetchAvailability();
closeModal();
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to save");
setError(err instanceof Error ? err.message : t("availability.errors.saveFailed"));
} finally {
setIsSaving(false);
}
@ -166,7 +169,7 @@ export default function AdminAvailabilityPage() {
await fetchAvailability();
closeModal();
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to clear");
setError(err instanceof Error ? err.message : t("availability.errors.clearFailed"));
} finally {
setIsSaving(false);
}
@ -208,7 +211,7 @@ export default function AdminAvailabilityPage() {
await fetchAvailability();
cancelCopyMode();
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to copy");
setError(err instanceof Error ? err.message : t("availability.errors.copyFailed"));
} finally {
setIsCopying(false);
}
@ -221,7 +224,7 @@ export default function AdminAvailabilityPage() {
if (isLoading) {
return (
<main style={layoutStyles.main}>
<div style={layoutStyles.loader}>Loading...</div>
<div style={layoutStyles.loader}>{tCommon("loading")}</div>
</main>
);
}
@ -238,23 +241,25 @@ export default function AdminAvailabilityPage() {
<div style={styles.pageContainer}>
<div style={styles.headerRow}>
<div>
<h1 style={typographyStyles.pageTitle}>Availability</h1>
<h1 style={typographyStyles.pageTitle}>{t("availability.title")}</h1>
<p style={typographyStyles.pageSubtitle}>
Configure your available time slots for the next {maxAdvanceDays} days
{t("availability.subtitle", { days: maxAdvanceDays })}
</p>
</div>
{copySource && (
<div style={styles.copyActions}>
<span style={styles.copyHint}>Select days to copy to, then click Copy</span>
<span style={styles.copyHint}>{t("availability.copyMode.hint")}</span>
<button
onClick={executeCopy}
disabled={copyTargets.size === 0 || isCopying}
style={buttonStyles.accentButton}
>
{isCopying ? "Copying..." : `Copy to ${copyTargets.size} day(s)`}
{isCopying
? t("availability.copyMode.copying")
: t("availability.copyMode.copyTo", { count: copyTargets.size })}
</button>
<button onClick={cancelCopyMode} style={buttonStyles.secondaryButton}>
Cancel
{t("availability.copyMode.cancel")}
</button>
</div>
)}
@ -299,7 +304,7 @@ export default function AdminAvailabilityPage() {
startCopyMode(dateStr);
}}
style={styles.copyFromButton}
title="Copy to other days"
title={t("availability.copyMode.copyTo", { count: 0 })}
>
📋
</button>
@ -307,7 +312,7 @@ export default function AdminAvailabilityPage() {
</div>
<div style={styles.slotList}>
{slots.length === 0 ? (
<span style={styles.noSlots}>No availability</span>
<span style={styles.noSlots}>{t("availability.modal.noAvailability")}</span>
) : (
slots.map((slot, i) => (
<span key={i} style={styles.slotBadge}>
@ -328,7 +333,7 @@ export default function AdminAvailabilityPage() {
<div style={modalStyles.modalOverlay} onClick={closeModal}>
<div style={modalStyles.modal} onClick={(e) => e.stopPropagation()}>
<h2 style={modalStyles.modalTitle}>
Edit Availability - {formatDisplayDate(selectedDate)}
{t("availability.modal.title")} - {formatDisplayDate(selectedDate)}
</h2>
{error && <div style={modalStyles.modalError}>{error}</div>}
@ -362,31 +367,31 @@ export default function AdminAvailabilityPage() {
<button
onClick={() => removeSlot(index)}
style={styles.removeSlotButton}
title="Remove slot"
title={t("availability.modal.removeSlot")}
>
×
</button>
</div>
))}
<button onClick={addSlot} style={styles.addSlotButton}>
+ Add Time Range
+ {t("availability.modal.addSlot")}
</button>
</div>
<div style={modalStyles.modalActions}>
<button onClick={clearAvailability} disabled={isSaving} style={styles.clearButton}>
Clear All
{t("availability.modal.clear")}
</button>
<div style={modalStyles.modalActionsRight}>
<button onClick={closeModal} style={buttonStyles.secondaryButton}>
Cancel
{t("availability.modal.close")}
</button>
<button
onClick={saveAvailability}
disabled={isSaving}
style={buttonStyles.accentButton}
>
{isSaving ? "Saving..." : "Save"}
{isSaving ? tCommon("saving") : t("availability.modal.save")}
</button>
</div>
</div>