refactor(ts): convert SummaryCard + ReleaseNotesDialog to TSX (B9)
SummaryCard (top-level; CardType/CardDef, value: Dollars) and ReleaseNotesDialog (the version-ack modal we hardened; typed useAuth, HTMLElement-cast for activeElement focus). typecheck 0, build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
8937dc5e25
commit
8a4b51915f
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { APP_VERSION, RELEASE_NOTES } from '@/lib/version';
|
||||
import { RELEASE_NOTES } from '@/lib/version';
|
||||
import { Sparkles } from 'lucide-react';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { api } from '@/api';
|
||||
|
|
@ -9,7 +9,7 @@ import { api } from '@/api';
|
|||
export function ReleaseNotesDialog() {
|
||||
const { hasNewVersion, setHasNewVersion } = useAuth();
|
||||
const [open, setOpen] = useState(false);
|
||||
const titleRef = useRef(null);
|
||||
const titleRef = useRef<HTMLHeadingElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasNewVersion) setOpen(true);
|
||||
|
|
@ -19,7 +19,7 @@ export function ReleaseNotesDialog() {
|
|||
setOpen(false);
|
||||
setHasNewVersion(false); // optimistic — don't wait for the server
|
||||
api.acknowledgeVersion().catch(err => console.error('[ReleaseNotesDialog] failed to acknowledge version', err)); // backend stores the seen release version
|
||||
const prev = document.activeElement;
|
||||
const prev = document.activeElement as HTMLElement | null;
|
||||
if (prev?.focus) setTimeout(() => prev.focus(), 0);
|
||||
};
|
||||
|
||||
|
|
@ -1,10 +1,21 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { cn, fmt } from '@/lib/utils';
|
||||
import { AlertCircle, CheckCircle2, Clock, TrendingUp } from 'lucide-react';
|
||||
import { Settings2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { AlertCircle, CheckCircle2, Clock, TrendingUp, Settings2, type LucideIcon } from 'lucide-react';
|
||||
import type { Dollars } from '@/lib/money';
|
||||
|
||||
const CARD_DEFS = {
|
||||
type CardType = 'starting' | 'paid' | 'remaining' | 'overdue';
|
||||
|
||||
interface CardDef {
|
||||
label: string;
|
||||
icon: LucideIcon;
|
||||
bar: string;
|
||||
glow: string;
|
||||
borderActive?: string;
|
||||
valueClass: string;
|
||||
activateWhen: (v: number) => boolean;
|
||||
}
|
||||
|
||||
const CARD_DEFS: Record<CardType, CardDef> = {
|
||||
starting: {
|
||||
label: 'Starting',
|
||||
icon: TrendingUp,
|
||||
|
|
@ -41,7 +52,14 @@ const CARD_DEFS = {
|
|||
},
|
||||
};
|
||||
|
||||
export const SummaryCard = React.memo(function SummaryCard({ type, value, onEdit, hint }) {
|
||||
interface SummaryCardProps {
|
||||
type: CardType;
|
||||
value?: Dollars;
|
||||
onEdit?: () => void;
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
export const SummaryCard = React.memo(function SummaryCard({ type, value, onEdit, hint }: SummaryCardProps) {
|
||||
const def = useMemo(() => CARD_DEFS[type], [type]);
|
||||
const isActive = useMemo(() => def.activateWhen(value || 0), [def, value]);
|
||||
const Icon = useMemo(() => def.icon, [def]);
|
||||
Loading…
Reference in New Issue