Ignore local storage; admin users NIP-05, media, events, footer updates
- Add /storage/ and /backend/storage/ to .gitignore - Track meetup time helper, logo asset, and assorted frontend/backend fixes
This commit is contained in:
@@ -11,7 +11,7 @@ async function request<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
const res = await fetch(apiUrl(path), { ...options, headers });
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => ({ message: "Request failed" }));
|
||||
throw new Error(error.message || `HTTP ${res.status}`);
|
||||
throw new Error(error.message || error.error || `HTTP ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
@@ -88,6 +88,11 @@ export const api = {
|
||||
request<any>("/users/promote", { method: "POST", body: JSON.stringify({ pubkey }) }),
|
||||
demoteUser: (pubkey: string) =>
|
||||
request<any>("/users/demote", { method: "POST", body: JSON.stringify({ pubkey }) }),
|
||||
updateUserUsername: (pubkey: string, username: string) =>
|
||||
request<any>(`/users/${encodeURIComponent(pubkey)}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ username }),
|
||||
}),
|
||||
|
||||
// Categories
|
||||
getCategories: () => request<any[]>("/categories"),
|
||||
|
||||
53
frontend/lib/meetupEventTime.ts
Normal file
53
frontend/lib/meetupEventTime.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Event start times in Brussels local wall time, converted to UTC the same way as
|
||||
* backend/src/api/calendar.ts (parseEventDates). Keeps admin/public UI aligned with ICS.
|
||||
*/
|
||||
|
||||
// Parse "HH:MM", "H:MM am/pm", "Hpm" etc.
|
||||
function parseLocalTime(t: string): { h: number; m: number } {
|
||||
const clean = t.trim();
|
||||
const m24 = clean.match(/^(\d{1,2}):(\d{2})$/);
|
||||
if (m24) return { h: parseInt(m24[1], 10), m: parseInt(m24[2], 10) };
|
||||
|
||||
const mAp = clean.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/i);
|
||||
if (mAp) {
|
||||
let h = parseInt(mAp[1], 10);
|
||||
const m = mAp[2] ? parseInt(mAp[2], 10) : 0;
|
||||
if (mAp[3].toLowerCase() === "pm" && h !== 12) h += 12;
|
||||
if (mAp[3].toLowerCase() === "am" && h === 12) h = 0;
|
||||
return { h, m };
|
||||
}
|
||||
return { h: 18, m: 0 };
|
||||
}
|
||||
|
||||
// Brussels is UTC+1 (CET) / UTC+2 (CEST). Same as calendar.ts.
|
||||
const BRUSSELS_OFFSET_HOURS = 1;
|
||||
|
||||
/** Extract YYYY-MM-DD from stored date (ISO date-only or full ISO datetime). */
|
||||
export function normalizeMeetupDateKey(dateStr: string): string | null {
|
||||
const s = dateStr?.trim();
|
||||
if (!s) return null;
|
||||
const dayPart = s.includes("T") ? s.split("T")[0]! : s.slice(0, 10);
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(dayPart)) return null;
|
||||
return dayPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event start instant in UTC, or Invalid Date if date/time cannot be parsed.
|
||||
*/
|
||||
export function getMeetupStartUtc(dateStr: string, timeStr: string): Date {
|
||||
const key = normalizeMeetupDateKey(dateStr);
|
||||
if (!key) return new Date(NaN);
|
||||
const parts = key.split("-").map(Number);
|
||||
const year = parts[0];
|
||||
const month = parts[1];
|
||||
const day = parts[2];
|
||||
if (!year || !month || !day) return new Date(NaN);
|
||||
|
||||
const t = timeStr?.trim() ? timeStr : "00:00";
|
||||
const timeParts = t.split(/\s*[-–]\s*/);
|
||||
const { h: startH, m: startM } = parseLocalTime(timeParts[0] ?? "");
|
||||
|
||||
const utcStartH = startH - BRUSSELS_OFFSET_HOURS;
|
||||
return new Date(Date.UTC(year, month - 1, day, utcStartH, startM, 0));
|
||||
}
|
||||
Reference in New Issue
Block a user