diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 93ffbea..286d247 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -38,6 +38,20 @@ async function handleResponse(res: Response, path: string): Promise { return res.json() as Promise; } +/** + * The server's `{"error": "..."}` message from a failed call, or `fallback` + * when the failure carried none (network error, non-JSON body). The one place + * the error envelope is unpacked on the client — views used to restate this + * as a six-line `"body" in e` branch at every catch site. + */ +export function apiErrorMessage(e: unknown, fallback: string): string { + if (e && typeof e === "object" && "body" in e) { + const body = (e as { body?: { error?: unknown } }).body; + if (body && typeof body.error === "string" && body.error) return body.error; + } + return fallback; +} + export async function apiGet(path: string): Promise { const res = await fetch(path); return handleResponse(res, path); diff --git a/frontend/src/assets/auth-shared.css b/frontend/src/assets/auth-shared.css new file mode 100644 index 0000000..fe507dd --- /dev/null +++ b/frontend/src/assets/auth-shared.css @@ -0,0 +1,115 @@ +/* ── Auth surface (Login / Register / RegisterInvite / ForgotPassword / ResetPassword) ── + The five auth views used to carry byte-identical copies of these rules in + their scoped blocks (2026-08 shape audit). Loaded per view with + diff --git a/frontend/src/components/TaskLogSection.vue b/frontend/src/components/TaskLogSection.vue index 57c917b..dc8ed2e 100644 --- a/frontend/src/components/TaskLogSection.vue +++ b/frontend/src/components/TaskLogSection.vue @@ -3,6 +3,7 @@ import { ref, onMounted } from "vue"; import { apiGet, apiPost, apiPatch, apiDelete } from "@/api/client"; import { renderMarkdown } from "@/utils/markdown"; import type { TaskLog } from "@/types/task"; +import { fmtStamp } from "@/utils/dateFormat"; const props = defineProps<{ taskId: number }>(); @@ -15,13 +16,6 @@ const editingId = ref(null); const editContent = ref(""); const editDuration = ref(""); -function formatDate(iso: string): string { - const d = new Date(iso); - const datePart = d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }); - const timePart = d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" }); - return `${datePart}, ${timePart}`; -} - function formatDuration(minutes: number): string { if (minutes < 60) return `${minutes} min`; const h = Math.floor(minutes / 60); @@ -128,7 +122,7 @@ onMounted(loadLogs);