+
+ {request.optionIds.map((optionId) => {
+ const label = request.labels[optionId] ?? optionId;
+ return (
+
+ );
+ })}
+
+ {request.hasDurableRule && request.durableRuleNote !== null ? (
+
+ ⚠ {request.durableRuleNote}
+
+ ) : null}
+
+ );
+}
+
+/**
+ * Countdown display for a pending card. Updates every second until expiry.
+ * Returns null when already expired (buttons handle that state).
+ */
+function ExpiryCountdown({ expiresAt }: { expiresAt: number }) {
+ const [secsLeft, setSecsLeft] = React.useState(() =>
+ Math.max(0, Math.round(expiresAt - Date.now() / 1000)),
+ );
+
+ React.useEffect(() => {
+ if (secsLeft <= 0) return;
+ const id = setInterval(() => {
+ const remaining = Math.max(0, Math.round(expiresAt - Date.now() / 1000));
+ setSecsLeft(remaining);
+ if (remaining <= 0) clearInterval(id);
+ }, 1000);
+ return () => clearInterval(id);
+ }, [expiresAt, secsLeft]);
+
+ if (secsLeft <= 0) return null;
+ const mins = Math.floor(secsLeft / 60);
+ const secs = secsLeft % 60;
+ const label = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
+ return (
+