chore: extract shared dashboard chrome strings to i18n keys

Replaces 47 inline cs()/en ternaries with dashboard.* keys: demo
notices, save/delete failures, delete aria-labels, loading, and
zone/blocked-day copy. Demo fixture names, locale tags, and the
zón/locations plural stay inline (Czech plurals need count-aware
forms).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-22 21:42:25 +02:00
1 parent 334b17f4ff
commit f1b0a3a2f2
5 files changed
+82 -48

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@
## Still Needs Frontend Polish
- Dashboard inline i18n ternaries remain (~60 as of M30). Systematic extraction to `i18n.t()` keys is an ongoing task.
- Dashboard inline i18n ternaries: shared chrome strings extracted to `dashboard.*` keys (demo notices, save/delete failures, delete aria-labels, zone/blocked-day copy). ~56 `cs() ? ...` remain project-wide, of which the dashboard remainder is demo fixture names, `cs-CZ`/`en-US` locale tags, and the `zón`/`locations` plural — all correct inline (Czech plurals need count-aware forms, not a single key).
- Registration cannot be fully customer-tested locally until Neon Auth environment variables are configured.
## Fixed This Session (platform audit pass)
@@ -551,6 +551,23 @@ const dictionaries = {
"dashboard.notification.reminder": "Připomínka: rezervace u",
"dashboard.notification.upgrade": "Blížíte se limitu plánu",
"dashboard.notification.trialEnding": "Vaše zkušební doba končí za 3 dny",
"dashboard.demo.noSave": "V demo režimu nelze ukládat.",
"dashboard.demo.noDelete": "V demo režimu nelze mazat.",
"dashboard.demo.noModify": "V demo režimu nelze měnit rezervace.",
"dashboard.action.delete": "Smazat",
"dashboard.loading": "Načítání…",
"dashboard.saveFailed": "Uložení selhalo.",
"dashboard.deleteFailed": "Smazání selhalo.",
"dashboard.availability.hint": "Nastavte dny a časy, kdy přijímáte rezervace.",
"dashboard.blockedDay.hint": "Dny, kdy nejsou dostupné rezervace.",
"dashboard.blockedDay.empty": "Zatím žádné blokované dny.",
"dashboard.blockedDay.placeholder": "např. Státní svátek",
"dashboard.blockedDay.date": "Datum",
"dashboard.day.closed": "Zavřeno",
"dashboard.day.open": "Otevřeno",
"dashboard.zone.addFirst": "Přidejte první zónu nebo místnost.",
"dashboard.zone.limitHint": "Váš plán umožňuje pouze omezený počet lokací. Pro více lokací upgradujte.",
"dashboard.zone.saveFailed": "Uložení zóny selhalo.",
"booking.title": "Rezervace",
"booking.body": "Vyberte dostupný termín, doplňte kontaktní údaje a potvrzení přijde e-mailem.",
"booking.slots": "Dostupné termíny",
@@ -1190,6 +1207,23 @@ const dictionaries = {
"dashboard.notification.reminder": "Reminder: booking at",
"dashboard.notification.upgrade": "You're nearing your plan limit",
"dashboard.notification.trialEnding": "Your trial ends in 3 days",
"dashboard.demo.noSave": "Cannot save in demo mode.",
"dashboard.demo.noDelete": "Cannot delete in demo mode.",
"dashboard.demo.noModify": "Cannot modify bookings in demo mode.",
"dashboard.action.delete": "Delete",
"dashboard.loading": "Loading…",
"dashboard.saveFailed": "Save failed.",
"dashboard.deleteFailed": "Delete failed.",
"dashboard.availability.hint": "Set the days and times you accept bookings.",
"dashboard.blockedDay.hint": "Days when bookings are unavailable.",
"dashboard.blockedDay.empty": "No blocked days yet.",
"dashboard.blockedDay.placeholder": "e.g. Public holiday",
"dashboard.blockedDay.date": "Date",
"dashboard.day.closed": "Closed",
"dashboard.day.open": "Open",
"dashboard.zone.addFirst": "Add your first location or room.",
"dashboard.zone.limitHint": "Your plan only allows a limited number of locations. Upgrade for more.",
"dashboard.zone.saveFailed": "Failed to save location.",
"booking.title": "Book a visit",
"booking.body": "Choose an available time, add your contact details, and receive confirmation by email.",
"booking.slots": "Available times",
@@ -141,7 +141,7 @@ function ServicesPage() {
const duration = parseInt(form().durationMinutes, 10);
if (!name || !duration || duration <= 0) return;
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noSave"));
return;
}
setSaving(true);
@@ -180,7 +180,7 @@ function ServicesPage() {
const handleDelete = async (id: string) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze mazat." : "Cannot delete in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noDelete"));
return;
}
try {
@@ -287,7 +287,7 @@ function ServicesPage() {
<input type="checkbox" checked={field.required} onChange={(e) => setIntake(i(), { required: e.currentTarget.checked })} class="rounded accent-accent" />
{data.i18n.t("dashboard.service.intakeRequired")}
</label>
<button onClick={() => removeIntake(i())} class="p-1.5 text-ink-subtle hover:text-red-600 rounded-lg" aria-label={cs() ? "Smazat" : "Delete"}><Trash2Icon /></button>
<button onClick={() => removeIntake(i())} class="p-1.5 text-ink-subtle hover:text-red-600 rounded-lg" aria-label={data.i18n.t("dashboard.action.delete")}><Trash2Icon /></button>
</div>
)}
</For>
@@ -304,7 +304,7 @@ function ServicesPage() {
</Show>
<Show when={!services.loading} fallback={
<div class="p-12 text-center text-ink-muted text-sm animate-pulse">{cs() ? "Načítání…" : "Loading…"}</div>
<div class="p-12 text-center text-ink-muted text-sm animate-pulse">{data.i18n.t("dashboard.loading")}</div>
}>
<Show when={resolvedServices().length > 0} fallback={
<div class="p-12 text-center">
@@ -336,7 +336,7 @@ function ServicesPage() {
<button onClick={() => openEdit(service)} class="p-2 text-ink-subtle hover:text-accent hover:bg-accent-subtle rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.edit")}>
<EditIcon />
</button>
<button onClick={() => void handleDelete(service.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={cs() ? "Smazat" : "Delete"}>
<button onClick={() => void handleDelete(service.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.action.delete")}>
<Trash2Icon />
</button>
</div>
@@ -114,7 +114,7 @@ function StaffPage() {
const name = form().displayName.trim();
if (!name) return;
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noSave"));
return;
}
setSaving(true);
@@ -146,7 +146,7 @@ function StaffPage() {
const handleToggleActive = async (member: any) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noSave"));
return;
}
try {
@@ -164,7 +164,7 @@ function StaffPage() {
const handleDelete = async (id: string) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze mazat." : "Cannot delete in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noDelete"));
return;
}
try {
@@ -254,7 +254,7 @@ function StaffPage() {
</Show>
<Show when={!staff.loading} fallback={
<div class="p-12 text-center text-ink-muted text-sm animate-pulse">{cs() ? "Načítání…" : "Loading…"}</div>
<div class="p-12 text-center text-ink-muted text-sm animate-pulse">{data.i18n.t("dashboard.loading")}</div>
}>
<Show when={resolvedStaff().length > 0} fallback={
<div class="p-12 text-center">
@@ -294,7 +294,7 @@ function StaffPage() {
<button onClick={() => openEdit(member)} class="p-2 text-ink-subtle hover:text-accent hover:bg-accent-subtle rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.edit")}>
<EditIcon />
</button>
<button onClick={() => void handleDelete(member.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={cs() ? "Smazat" : "Delete"}>
<button onClick={() => void handleDelete(member.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.action.delete")}>
<Trash2Icon />
</button>
</div>
@@ -203,7 +203,7 @@ function ZonesPage() {
const handlePromote = async (bookingId: string, allowOverCapacity: boolean) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze měnit rezervace." : "Cannot modify bookings in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noModify"));
return;
}
setPromoteBusy(bookingId);
@@ -237,7 +237,7 @@ function ZonesPage() {
if (!newZoneName().trim()) return;
if (!canAddLocation()) { setShowUpgradePrompt(true); setShowAddZone(false); return; }
if (!bearer || bearer.startsWith("demo.")) {
data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode.");
data.setDemoNotice(data.i18n.t("dashboard.demo.noSave"));
return;
}
try {
@@ -248,25 +248,25 @@ function ZonesPage() {
setNewZoneName(""); setNewZoneType("room"); setNewZoneCapacity(10); setShowAddZone(false);
void refetchLocations();
} catch {
data.setDemoNotice(cs() ? "Uložení zóny selhalo." : "Failed to save location.");
data.setDemoNotice(data.i18n.t("dashboard.zone.saveFailed"));
}
};
const handleDeleteZone = async (id: string) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze mazat." : "Cannot delete in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noDelete")); return; }
try {
await (apiClient as any).DELETE(`/v1/catalog/locations/${id}`, authHeaders());
void refetchLocations();
} catch {
data.setDemoNotice(cs() ? "Smazání selhalo." : "Delete failed.");
data.setDemoNotice(data.i18n.t("dashboard.deleteFailed"));
}
};
const handleAddBlockedDay = async () => {
const bearer = data.token();
if (!newBlockedDate()) return;
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setBlockedSaving(true);
try {
await (apiClient as any).POST("/v1/catalog/blocked-days", {
@@ -274,14 +274,14 @@ function ZonesPage() {
body: {
date: new Date(`${newBlockedDate()}T00:00:00`).toISOString(),
type: "closure",
reason: newBlockedReason().trim() || (cs() ? "Zavřeno" : "Closed"),
reason: newBlockedReason().trim() || (data.i18n.t("dashboard.day.closed")),
...(newBlockedStaffId() ? { staffId: newBlockedStaffId() } : {}),
},
});
setNewBlockedDate(""); setNewBlockedReason(""); setNewBlockedStaffId("");
void refetchBlocked();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setBlockedSaving(false);
}
@@ -298,7 +298,7 @@ function ZonesPage() {
const handleSaveHours = async (day: { dayOfWeek: number; open: string; close: string; isOpen: boolean }) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setHoursSaving(day.dayOfWeek);
try {
const scope = hoursScope();
@@ -309,7 +309,7 @@ function ZonesPage() {
});
void refetchHours();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setHoursSaving(null);
}
@@ -322,7 +322,7 @@ function ZonesPage() {
const handleSaveTemplate = async () => {
const bearer = data.token();
if (!newTemplateTitle().trim()) return;
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setTemplateSaving(true);
try {
const body = { title: newTemplateTitle().trim(), durationMinutes: newTemplateDuration(), capacity: newTemplateCapacity() };
@@ -335,7 +335,7 @@ function ZonesPage() {
resetTemplateForm();
void refetchTemplates(); void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setTemplateSaving(false);
}
@@ -350,19 +350,19 @@ function ZonesPage() {
const handleDeleteTemplate = async (id: string) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze mazat." : "Cannot delete in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noDelete")); return; }
try {
await (apiClient as any).DELETE(`/v1/catalog/class-templates/${id}`, authHeaders());
void refetchTemplates(); void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Smazání selhalo." : "Delete failed.");
data.setDemoNotice(data.i18n.t("dashboard.deleteFailed"));
}
};
const handleAddSession = async () => {
const bearer = data.token();
if (!newSessionTemplateId() || !newSessionStartsAt()) return;
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setSessionSaving(true);
try {
await (apiClient as any).POST("/v1/catalog/class-sessions", {
@@ -376,7 +376,7 @@ function ZonesPage() {
setNewSessionTemplateId(""); setNewSessionLocationId(""); setNewSessionStartsAt("");
void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setSessionSaving(false);
}
@@ -384,19 +384,19 @@ function ZonesPage() {
const handleDeleteSession = async (id: string) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze mazat." : "Cannot delete in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noDelete")); return; }
try {
await (apiClient as any).DELETE(`/v1/catalog/class-sessions/${id}`, authHeaders());
void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Smazání selhalo." : "Delete failed.");
data.setDemoNotice(data.i18n.t("dashboard.deleteFailed"));
}
};
const handleAddSchedule = async () => {
const bearer = data.token();
if (!newSchedTemplateId() || !newSchedStartsLocal()) return;
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setSchedSaving(true);
try {
await (apiClient as any).POST("/v1/catalog/class-schedules", {
@@ -412,7 +412,7 @@ function ZonesPage() {
void refetchSchedules();
void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setSchedSaving(false);
}
@@ -431,7 +431,7 @@ function ZonesPage() {
const bearer = data.token();
const id = editingSchedId();
if (!id) return;
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setSchedEditSaving(true);
try {
await (apiClient as any).PATCH(`/v1/catalog/class-schedules/${id}`, {
@@ -448,7 +448,7 @@ function ZonesPage() {
void refetchSchedules();
void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setSchedEditSaving(false);
}
@@ -462,7 +462,7 @@ function ZonesPage() {
const handleSaveCapacity = async (session: any, allowOverCapacity: boolean) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze ukládat." : "Cannot save in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noSave")); return; }
setCapSaving(true);
try {
const response = await (apiClient as any).PATCH(`/v1/catalog/class-sessions/${session.id}`, {
@@ -477,7 +477,7 @@ function ZonesPage() {
setCapOverbookConfirm(null);
void refetchSessions();
} catch {
data.setDemoNotice(cs() ? "Uložení selhalo." : "Save failed.");
data.setDemoNotice(data.i18n.t("dashboard.saveFailed"));
} finally {
setCapSaving(false);
}
@@ -485,12 +485,12 @@ function ZonesPage() {
const handleDeleteSchedule = async (id: string) => {
const bearer = data.token();
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(cs() ? "V demo režimu nelze mazat." : "Cannot delete in demo mode."); return; }
if (!bearer || bearer.startsWith("demo.")) { data.setDemoNotice(data.i18n.t("dashboard.demo.noDelete")); return; }
try {
await (apiClient as any).DELETE(`/v1/catalog/class-schedules/${id}`, authHeaders());
void refetchSchedules();
} catch {
data.setDemoNotice(cs() ? "Smazání selhalo." : "Delete failed.");
data.setDemoNotice(data.i18n.t("dashboard.deleteFailed"));
}
};
@@ -558,7 +558,7 @@ function ZonesPage() {
<Show when={resolvedLocations().length > 0} fallback={
<div class="p-12 text-center">
<p class="text-ink-muted font-medium">{data.i18n.t("dashboard.empty.title")}</p>
<p class="text-sm text-ink-subtle mt-1">{cs() ? "Přidejte první zónu nebo místnost." : "Add your first location or room."}</p>
<p class="text-sm text-ink-subtle mt-1">{data.i18n.t("dashboard.zone.addFirst")}</p>
</div>
}>
<div class="divide-y divide-border/60">
@@ -571,7 +571,7 @@ function ZonesPage() {
<p class="text-sm text-ink-muted">{typeLabel(zone.type)} &bull; {data.i18n.t("dashboard.zone.capacity")}: {zone.capacity}</p>
</div>
</div>
<button onClick={() => void handleDeleteZone(zone.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={cs() ? "Smazat" : "Delete"}>
<button onClick={() => void handleDeleteZone(zone.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.action.delete")}>
<Trash2Icon />
</button>
</div>
@@ -585,12 +585,12 @@ function ZonesPage() {
<div class="surface-card overflow-hidden">
<div class="p-6 border-b border-border">
<h3 class="text-lg font-display font-semibold text-ink">{data.i18n.t("dashboard.zone.blockedDays")}</h3>
<p class="text-sm text-ink-muted mt-1">{cs() ? "Dny, kdy nejsou dostupné rezervace." : "Days when bookings are unavailable."}</p>
<p class="text-sm text-ink-muted mt-1">{data.i18n.t("dashboard.blockedDay.hint")}</p>
</div>
<div class="p-6 border-b border-border bg-canvas-subtle/50">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<Input type="date" label={cs() ? "Datum" : "Date"} value={newBlockedDate()} onInput={(e) => setNewBlockedDate(e.currentTarget.value)} />
<Input type="text" label={cs() ? "Důvod" : "Reason"} value={newBlockedReason()} onInput={(e) => setNewBlockedReason(e.currentTarget.value)} placeholder={cs() ? "např. Státní svátek" : "e.g. Public holiday"} />
<Input type="date" label={data.i18n.t("dashboard.blockedDay.date")} value={newBlockedDate()} onInput={(e) => setNewBlockedDate(e.currentTarget.value)} />
<Input type="text" label={cs() ? "Důvod" : "Reason"} value={newBlockedReason()} onInput={(e) => setNewBlockedReason(e.currentTarget.value)} placeholder={data.i18n.t("dashboard.blockedDay.placeholder")} />
<Select
label={data.i18n.t("dashboard.zone.staff")}
value={newBlockedStaffId()}
@@ -611,7 +611,7 @@ function ZonesPage() {
<div class="p-12 text-center">
<div class="w-16 h-16 rounded-full bg-canvas-subtle flex items-center justify-center mx-auto mb-4"><BanIcon /></div>
<p class="text-ink-muted font-medium">{data.i18n.t("dashboard.empty.title")}</p>
<p class="text-sm text-ink-muted">{cs() ? "Zatím žádné blokované dny." : "No blocked days yet."}</p>
<p class="text-sm text-ink-muted">{data.i18n.t("dashboard.blockedDay.empty")}</p>
</div>
}>
<div class="divide-y divide-border/60">
@@ -641,7 +641,7 @@ function ZonesPage() {
<div class="p-6 border-b border-border flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h3 class="text-lg font-display font-semibold text-ink">{data.i18n.t("dashboard.zone.workingHours")}</h3>
<p class="text-sm text-ink-muted mt-1">{cs() ? "Nastavte dny a časy, kdy přijímáte rezervace." : "Set the days and times you accept bookings."}</p>
<p class="text-sm text-ink-muted mt-1">{data.i18n.t("dashboard.availability.hint")}</p>
</div>
<div class="w-full sm:w-56 shrink-0">
<Select
@@ -673,7 +673,7 @@ function ZonesPage() {
onChange={(e) => setField({ isOpen: e.currentTarget.checked, open: e.currentTarget.checked ? (day.open || "09:00") : day.open, close: e.currentTarget.checked ? (day.close || "18:00") : day.close })}
class="rounded border-border accent-[#c25e3a]"
/>
{cs() ? "Otevřeno" : "Open"}
{data.i18n.t("dashboard.day.open")}
</label>
<Show when={isOpen()}>
<div class="flex items-center gap-2">
@@ -808,7 +808,7 @@ function ZonesPage() {
</div>
<div class="flex items-center gap-1">
<button onClick={() => startEditSchedule(sch)} class="p-2 text-ink-subtle hover:text-accent hover:bg-accent-subtle/40 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.class.editSchedule")}><PencilIcon /></button>
<button onClick={() => void handleDeleteSchedule(sch.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={cs() ? "Smazat" : "Delete"}><Trash2Icon /></button>
<button onClick={() => void handleDeleteSchedule(sch.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.action.delete")}><Trash2Icon /></button>
</div>
</div>
}
@@ -927,7 +927,7 @@ function ZonesPage() {
<span class={`inline-flex transition-transform duration-200 ${expanded() ? "rotate-180" : ""}`}><ChevronDownIcon /></span>
</button>
<button onClick={() => startEditCapacity(session)} class="p-2 text-ink-subtle hover:text-accent hover:bg-accent-subtle/40 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.class.editCapacity")}><PencilIcon /></button>
<button onClick={() => void handleDeleteSession(session.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={cs() ? "Smazat" : "Delete"}><Trash2Icon /></button>
<button onClick={() => void handleDeleteSession(session.id)} class="p-2 text-ink-subtle hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors" aria-label={data.i18n.t("dashboard.action.delete")}><Trash2Icon /></button>
</div>
</div>
<Show when={editingCapSessionId() === session.id}>
@@ -1045,7 +1045,7 @@ function ZonesPage() {
<div class="w-10 h-10 rounded-full bg-accent-subtle flex items-center justify-center shrink-0"><SparklesIcon /></div>
<div class="flex-1">
<h4 class="font-medium text-ink mb-1">{data.i18n.t("dashboard.locationLimitReached")}</h4>
<p class="text-sm text-ink-muted">{cs() ? "Váš plán umožňuje pouze omezený počet lokací. Pro více lokací upgradujte." : "Your plan only allows a limited number of locations. Upgrade for more."}</p>
<p class="text-sm text-ink-muted">{data.i18n.t("dashboard.zone.limitHint")}</p>
<div class="flex gap-3 mt-3">
<button onClick={() => { setShowUpgradePrompt(false); void data.openCheckout(); }} class="px-4 py-2 bg-accent text-canvas text-sm font-medium rounded-lg hover:bg-accent-hover transition-colors">{data.i18n.t("dashboard.upgrade")}</button>
<button onClick={() => setShowUpgradePrompt(false)} class="px-4 py-2 bg-canvas-subtle text-ink-muted text-sm font-medium rounded-lg hover:bg-canvas transition-colors">{data.i18n.t("dashboard.close")}</button>