Skip to content

Commit 59b58a8

Browse files
authored
Merge pull request #361 from Chrisbankz0/feature/330-insurance-reminders-ui
feat: add insurance reminders preview and toggle in settings
2 parents b8c591a + bcd7cc8 commit 59b58a8

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

app/settings/page.tsx

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,110 @@ function SaveButton({ label = "Save changes" }: { label?: string }) {
234234
</div>
235235
</div>
236236

237+
type InsuranceReminder = {
238+
policyId: string;
239+
name: string;
240+
nextPaymentDate: string;
241+
monthlyPremium: number;
242+
};
243+
244+
function InsuranceReminderPreview() {
245+
const [status, setStatus] = useState<
246+
"loading" | "loaded" | "empty" | "unauthorized" | "error"
247+
>("loading");
248+
const [reminders, setReminders] = useState<InsuranceReminder[]>([]);
249+
const [error, setError] = useState<string | null>(null);
250+
251+
useEffect(() => {
252+
let active = true;
253+
254+
async function loadReminders() {
255+
try {
256+
const response = await fetch("/api/insurance/reminders");
257+
if (!active) return;
258+
259+
if (response.status === 401 || response.status === 403) {
260+
setStatus("unauthorized");
261+
setError("Connect your wallet to view upcoming insurance reminders.");
262+
return;
263+
}
264+
265+
if (!response.ok) {
266+
const body = await response.text();
267+
throw new Error(body || response.statusText);
268+
}
269+
270+
const data = (await response.json()) as InsuranceReminder[];
271+
if (!active) return;
272+
273+
setReminders(data);
274+
setStatus(data.length > 0 ? "loaded" : "empty");
275+
} catch (err) {
276+
if (!active) return;
277+
setStatus("error");
278+
setError(err instanceof Error ? err.message : String(err));
279+
}
280+
}
281+
282+
loadReminders();
283+
return () => {
284+
active = false;
285+
};
286+
}, []);
287+
288+
return (
289+
<div className="mx-6 mt-4 rounded-2xl border border-gray-100 dark:border-gray-800 bg-gray-50 dark:bg-gray-950 p-4">
290+
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
291+
<div>
292+
<p className="text-sm font-semibold text-gray-900 dark:text-white">
293+
Insurance reminders
294+
</p>
295+
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
296+
Show upcoming or overdue premium payments for your active policies.
297+
</p>
298+
</div>
299+
<div className="text-xs text-gray-500 dark:text-gray-400">
300+
{status === "loading" && "Loading…"}
301+
{status === "empty" && "No reminders in the next 7 days."}
302+
{status === "loaded" && `${reminders.length} reminder${reminders.length === 1 ? "" : "s"}`}
303+
{status === "unauthorized" && "Sign in to view reminders."}
304+
{status === "error" && "Unable to load reminders."}
305+
</div>
306+
</div>
307+
308+
<div className="mt-4 space-y-3">
309+
{status === "loaded" &&
310+
reminders.map((reminder) => (
311+
<div
312+
key={reminder.policyId}
313+
className="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 px-4 py-3"
314+
>
315+
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
316+
<p className="text-sm font-medium text-gray-900 dark:text-white">
317+
{reminder.name}
318+
</p>
319+
<span className="text-xs text-gray-500 dark:text-gray-400">
320+
Due {new Date(reminder.nextPaymentDate).toLocaleDateString()}
321+
</span>
322+
</div>
323+
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
324+
Premium: ${reminder.monthlyPremium.toFixed(2)}
325+
</p>
326+
</div>
327+
))}
328+
{status === "unauthorized" && (
329+
<p className="text-sm text-gray-500 dark:text-gray-400">{error}</p>
330+
)}
331+
{status === "error" && (
332+
<p className="text-sm text-red-600 dark:text-red-400">
333+
{error ?? "Could not load insurance reminders."}
334+
</p>
335+
)}
336+
</div>
337+
</div>
338+
);
339+
}
340+
237341
// ─── Sections ────────────────────────────────────────────────────────────────
238342

239343
function ProfileSection() {
@@ -327,6 +431,12 @@ function NotificationsSection() {
327431
description="When you hit 25 %, 50 %, 75 %, or 100 % of a goal"
328432
defaultChecked
329433
/>
434+
<Toggle
435+
label="Insurance premium reminders"
436+
description="Receive alerts when insurance premiums are due or overdue."
437+
defaultChecked
438+
/>
439+
<InsuranceReminderPreview />
330440
<p className="px-6 pt-5 pb-2 text-xs font-medium uppercase tracking-widest text-gray-400 dark:text-gray-500">
331441
Channels
332442
</p>

0 commit comments

Comments
 (0)