@@ -234,6 +234,110 @@ function SaveButton({ label = "Save changes" }: { label?: string }) {
234234 </ div >
235235 < / d i v >
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
239343function 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