B2: TypeScript + React 19 migration (T1–B18) #87

Closed
null wants to merge 69 commits from branch/b2-typescript into branch/b1-react-query
4 changed files with 61 additions and 9 deletions
Showing only changes of commit dc675fbecd - Show all commits

View File

@ -1,6 +1,13 @@
import type { ReactNode } from 'react';
import { cn } from '@/lib/utils';
export function FilterChip({ active, children, onClick }) {
interface FilterChipProps {
active?: boolean;
children?: ReactNode;
onClick?: () => void;
}
export function FilterChip({ active, children, onClick }: FilterChipProps) {
return (
<button
type="button"

View File

@ -1,7 +1,18 @@
import { cn, fmt } from '@/lib/utils';
import { paymentSummary } from '@/lib/trackerUtils';
import type { Dollars } from '@/lib/money';
import type { TrackerRow } from '@/types';
export function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, compact = false, className }) {
interface PaymentProgressProps {
row: TrackerRow;
threshold: Dollars;
onOpen?: () => void;
onMarkFullAmount?: () => void;
compact?: boolean;
className?: string;
}
export function PaymentProgress({ row, threshold, onOpen, onMarkFullAmount, compact = false, className }: PaymentProgressProps) {
const summary = paymentSummary(row, threshold);
const barTone = summary.remaining === 0
? 'bg-emerald-500'

View File

@ -2,8 +2,16 @@ import React, { useMemo } from 'react';
import { Loader2, AlertCircle } from 'lucide-react';
import { cn } from '@/lib/utils';
import { STATUS_META, isPaidStatus } from '@/lib/trackerUtils';
import type { TrackerStatus } from '@/types';
export const StatusBadge = React.memo(function StatusBadge({ status, clickable, onClick, loading }) {
interface StatusBadgeProps {
status: TrackerStatus;
clickable?: boolean;
onClick?: () => void;
loading?: boolean;
}
export const StatusBadge = React.memo(function StatusBadge({ status, clickable, onClick, loading }: StatusBadgeProps) {
const meta = useMemo(() => STATUS_META[status] || STATUS_META.upcoming, [status]);
const isSkipped = status === 'skipped';

View File

@ -1,7 +1,20 @@
import { TrendingUp, AlertCircle, Clock, CheckCircle2, Settings2 } from 'lucide-react';
import { TrendingUp, AlertCircle, Clock, CheckCircle2, Settings2, type LucideIcon } from 'lucide-react';
import { cn, fmt } from '@/lib/utils';
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,
@ -38,12 +51,17 @@ const CARD_DEFS = {
},
};
export function TrendIndicator({ trend }) {
interface TrendInfo {
direction: string;
percent_change: number;
}
export function TrendIndicator({ trend }: { trend?: TrendInfo | null }) {
if (!trend) return null;
const { direction, percent_change } = trend;
let icon, color, text;
let icon: string, color: string, text: string;
switch (direction) {
case 'up':
icon = '↑';
@ -73,7 +91,15 @@ export function TrendIndicator({ trend }) {
);
}
export function SummaryCard({ type, value, onEdit, hint, label }) {
interface SummaryCardProps {
type: CardType;
value?: Dollars;
onEdit?: () => void;
hint?: string;
label?: string;
}
export function SummaryCard({ type, value, onEdit, hint, label }: SummaryCardProps) {
const def = CARD_DEFS[type];
const isActive = def.activateWhen(value || 0);
const Icon = def.icon;
@ -118,7 +144,7 @@ export function SummaryCard({ type, value, onEdit, hint, label }) {
);
}
export function TrendCard({ trend }) {
export function TrendCard({ trend }: { trend?: TrendInfo | null }) {
if (!trend) return null;
return (