7559102479
- Add dark mode with theme toggle, localStorage/cookie persistence, anti-FOUC script - Add toast notification system with auto-dismiss and color-coded types - Add api() fetch wrapper with auto JSON, X-Socket-ID, and error toasts - Add keyboard shortcuts (/ search, b boards, ? help, n new card, Esc close) - Add error page with status code and navigation - Add focus trap utility and apply to CardModal with full ARIA dialog support - Add hamburger menu for mobile, responsive columns, fullscreen card modal on mobile - Add aria-labels to icon-only buttons and DnD live region - Migrate all ~30 fetch calls to api() wrapper with success toasts - Add dark: Tailwind variants to all 20+ pages and components Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
48 lines
1.2 KiB
TypeScript
48 lines
1.2 KiB
TypeScript
import { toast } from '$lib/stores/toasts.js';
|
|
import { getSocketId } from '$lib/stores/socket.js';
|
|
|
|
export type ApiResult<T = any> = {
|
|
ok: boolean;
|
|
data: T;
|
|
status: number;
|
|
};
|
|
|
|
export async function api<T = any>(
|
|
url: string,
|
|
opts: RequestInit = {}
|
|
): Promise<ApiResult<T>> {
|
|
const headers = new Headers(opts.headers);
|
|
|
|
// Auto-set JSON content type for non-FormData bodies
|
|
if (opts.body && !(opts.body instanceof FormData) && !headers.has('Content-Type')) {
|
|
headers.set('Content-Type', 'application/json');
|
|
}
|
|
|
|
// Pass socket ID for self-dedup
|
|
const sid = getSocketId();
|
|
if (sid) {
|
|
headers.set('X-Socket-ID', sid);
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(url, { ...opts, headers });
|
|
let data: any = null;
|
|
|
|
const contentType = res.headers.get('content-type');
|
|
if (contentType?.includes('application/json')) {
|
|
data = await res.json();
|
|
}
|
|
|
|
if (!res.ok) {
|
|
const msg = data?.error || data?.message || `Request failed (${res.status})`;
|
|
toast.error(msg);
|
|
return { ok: false, data, status: res.status };
|
|
}
|
|
|
|
return { ok: true, data, status: res.status };
|
|
} catch {
|
|
toast.error('Network error. Please try again.');
|
|
return { ok: false, data: null as any, status: 0 };
|
|
}
|
|
}
|