src/features/reviews/search.helpers.ts
Linked repository file from this build.
import type { ReviewCommand } from "./commands";
import type { StoredReview } from "./review-store";
const MONTHS = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
] as const;
/** Stored appointment is Eastern wall-clock date/time without a timezone suffix. */
export function formatAppointmentEastern(stored: string): string {
const value = stored.trim();
if (!value) return "No appointment scheduled";
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/);
if (!match) return `${value} Eastern`;
const [, year, month, day, hour = "00", minute = "00"] = match;
const monthLabel = MONTHS[Math.max(0, Math.min(11, Number(month) - 1))];
const hour24 = Number(hour);
const suffix = hour24 >= 12 ? "PM" : "AM";
const hour12 = hour24 % 12 || 12;
return `${monthLabel} ${Number(day)}, ${year} at ${hour12}:${minute} ${suffix} Eastern`;
}
export type PostReviewCommandResult =
| { ok: true; stored: StoredReview }
| { ok: false; error: string; conflict?: StoredReview };
export async function postReviewCommand(
reviewKey: string,
version: number,
command: ReviewCommand,
): Promise<PostReviewCommandResult> {
const body = {
version,
requestId: crypto.randomUUID(),
commands: [{ id: crypto.randomUUID(), command }],
};
const response = await fetch(`/api/reviews/${reviewKey}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = await response.json();
if (response.status === 409) {
return {
ok: false,
error: data.error ?? "This review changed elsewhere.",
conflict: data.current as StoredReview | undefined,
};
}
if (!response.ok) {
return { ok: false, error: data.error ?? "Unable to update review." };
}
return { ok: true, stored: data as StoredReview };
}
export function buildMailedDocument(
file: File,
storageKey: string,
metadata: { name: string; description: string },
): Extract<ReviewCommand, { type: "document" }>["document"] {
if (!metadata.name.trim() || !metadata.description.trim())
throw new Error("Enter a document name and purpose for each PDF.");
return {
id: crypto.randomUUID(),
filename: file.name,
name: metadata.name.trim(),
category: "Other",
description: metadata.description.trim(),
mimeType: "application/pdf",
size: file.size,
storageKey,
};
}