mirror of
https://github.com/temetro/temetro.git
synced 2026-08-12 19:46:53 +00:00
release workflow: publish multi-arch images; settings: confirm language switch
- release.yml: add QEMU + platforms: linux/amd64,linux/arm64 to both build-push steps so the published images work on Apple Silicon (fixes 'no matching manifest for linux/arm64/v8' on docker compose pull). - Settings -> Profile language picker is now a select that opens a confirmation dialog before applying, instead of switching instantly. - CLAUDE.md: require a dated docs changelog entry for every release. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,15 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectItem,
|
||||
SelectPopup,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { DeleteAccountDialog } from "@/components/settings/delete-account-dialog";
|
||||
import {
|
||||
CopyField,
|
||||
@@ -68,6 +76,10 @@ export function ProfilePanel() {
|
||||
const [baselineName, setBaselineName] = useState("");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||
// A language picked in the Select but not yet applied — its presence opens the
|
||||
// confirmation dialog. The Select stays bound to `activeLang`, so cancelling
|
||||
// (clearing this) automatically reverts the shown selection.
|
||||
const [pendingLang, setPendingLang] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@@ -222,21 +234,23 @@ export function ProfilePanel() {
|
||||
>
|
||||
<SettingsCard className="space-y-1.5 p-5">
|
||||
<FieldLabel>{t("settings.profile.language.label")}</FieldLabel>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{supportedLanguages.map((lng) => (
|
||||
<Button
|
||||
aria-pressed={activeLang === lng}
|
||||
className="rounded-3xl"
|
||||
key={lng}
|
||||
onClick={() => void i18n.changeLanguage(lng)}
|
||||
size="sm"
|
||||
type="button"
|
||||
variant={activeLang === lng ? "default" : "outline"}
|
||||
>
|
||||
{t(`settings.profile.language.${lng}`)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
if (value !== activeLang) setPendingLang(value);
|
||||
}}
|
||||
value={activeLang}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectPopup>
|
||||
{supportedLanguages.map((lng) => (
|
||||
<SelectItem key={lng} value={lng}>
|
||||
{t(`settings.profile.language.${lng}`)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectPopup>
|
||||
</Select>
|
||||
</SettingsCard>
|
||||
</SettingsSection>
|
||||
|
||||
@@ -316,6 +330,27 @@ export function ProfilePanel() {
|
||||
|
||||
<DeleteAccountDialog onOpenChange={setDeleteOpen} open={deleteOpen} />
|
||||
|
||||
<ConfirmDialog
|
||||
cancelLabel={t("settings.profile.language.cancel")}
|
||||
confirmLabel={t("settings.profile.language.confirmCta")}
|
||||
description={
|
||||
pendingLang
|
||||
? t("settings.profile.language.confirmBody", {
|
||||
language: t(`settings.profile.language.${pendingLang}`),
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
onConfirm={() => {
|
||||
if (pendingLang) void i18n.changeLanguage(pendingLang);
|
||||
}}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) setPendingLang(null);
|
||||
}}
|
||||
open={pendingLang !== null}
|
||||
title={t("settings.profile.language.confirmTitle")}
|
||||
variant="default"
|
||||
/>
|
||||
|
||||
{dirty ? (
|
||||
<div className="sticky bottom-4 z-10">
|
||||
<div className="flex items-center justify-between gap-4 rounded-2xl border border-border bg-background/95 px-4 py-3 shadow-lg backdrop-blur">
|
||||
|
||||
Reference in New Issue
Block a user