2026-05-03 19:51:57 -05:00
|
|
|
/**
|
|
|
|
|
* InputDialog — replaces window.prompt().
|
|
|
|
|
* Usage:
|
|
|
|
|
* <InputDialog
|
|
|
|
|
* open={open} onOpenChange={setOpen}
|
|
|
|
|
* title="Rename Category"
|
|
|
|
|
* description="Enter a new name."
|
|
|
|
|
* label="Name" defaultValue={current} placeholder="e.g. Utilities"
|
|
|
|
|
* onConfirm={(value) => handleSave(value)}
|
|
|
|
|
* />
|
|
|
|
|
*/
|
|
|
|
|
import { useState, useEffect, useRef } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
|
|
|
|
|
} from '@/components/ui/dialog';
|
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
import { Input } from '@/components/ui/input';
|
|
|
|
|
import { Label } from '@/components/ui/label';
|
|
|
|
|
|
refactor(ts): convert all UI primitives + ThemeContext to TSX (B2)
Foundational for phase B: the ui/* primitives were untyped .jsx, and TS infers
React-19 ref-as-prop components as *requiring* a ref, so every consumer errored.
Converted all 22 ui primitives (button, input, card, dialog, select, table,
dropdown-menu, tabs, badge, checkbox, switch, tooltip, separator, label,
Skeleton, collapsible, alert-dialog, sonner, save-status, theme-toggle,
input-dialog, confirm-dialog) using ComponentProps<'el'> / ComponentProps<typeof
Radix.X> + VariantProps for cva components — refs now optional, event params
typed. Also converted ThemeContext.tsx (createContext(null) made useContext's
post-guard return `never`, so setTheme was uncallable — fixed with a typed
context value) and three tracker cells (EditableCell/NotesCell/
LowerThisMonthButton) that consume the branded TrackerRow.
Added a shared errMessage(unknown) helper to utils (strict catch → unknown);
usePaymentActions reuses it. TrackerRow gains monthly_notes + amount_suggestion
(real server fields).
typecheck 0, lint 0 errors (42 warns), build green, 48 client tests. NOTE: the
e2e auth-setup probe is currently red on an unrelated, pre-existing
release-notes-modal dismissal race (reproduces on the last known-good commit;
the a11y snapshot shows these converted primitives rendering correctly) —
investigating separately.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:57:22 -05:00
|
|
|
interface InputDialogProps {
|
|
|
|
|
open: boolean;
|
|
|
|
|
onOpenChange?: (open: boolean) => void;
|
|
|
|
|
title?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
label?: string;
|
|
|
|
|
defaultValue?: string;
|
|
|
|
|
placeholder?: string;
|
|
|
|
|
confirmLabel?: string;
|
|
|
|
|
cancelLabel?: string;
|
|
|
|
|
validate?: (value: string) => string | null;
|
|
|
|
|
onConfirm?: (value: string) => void;
|
|
|
|
|
loading?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-03 19:51:57 -05:00
|
|
|
export function InputDialog({
|
|
|
|
|
open,
|
|
|
|
|
onOpenChange,
|
|
|
|
|
title,
|
|
|
|
|
description,
|
|
|
|
|
label,
|
|
|
|
|
defaultValue = '',
|
|
|
|
|
placeholder = '',
|
|
|
|
|
confirmLabel = 'Save',
|
|
|
|
|
cancelLabel = 'Cancel',
|
|
|
|
|
validate, // optional (value: string) => string | null (null = valid)
|
|
|
|
|
onConfirm,
|
|
|
|
|
loading = false,
|
refactor(ts): convert all UI primitives + ThemeContext to TSX (B2)
Foundational for phase B: the ui/* primitives were untyped .jsx, and TS infers
React-19 ref-as-prop components as *requiring* a ref, so every consumer errored.
Converted all 22 ui primitives (button, input, card, dialog, select, table,
dropdown-menu, tabs, badge, checkbox, switch, tooltip, separator, label,
Skeleton, collapsible, alert-dialog, sonner, save-status, theme-toggle,
input-dialog, confirm-dialog) using ComponentProps<'el'> / ComponentProps<typeof
Radix.X> + VariantProps for cva components — refs now optional, event params
typed. Also converted ThemeContext.tsx (createContext(null) made useContext's
post-guard return `never`, so setTheme was uncallable — fixed with a typed
context value) and three tracker cells (EditableCell/NotesCell/
LowerThisMonthButton) that consume the branded TrackerRow.
Added a shared errMessage(unknown) helper to utils (strict catch → unknown);
usePaymentActions reuses it. TrackerRow gains monthly_notes + amount_suggestion
(real server fields).
typecheck 0, lint 0 errors (42 warns), build green, 48 client tests. NOTE: the
e2e auth-setup probe is currently red on an unrelated, pre-existing
release-notes-modal dismissal race (reproduces on the last known-good commit;
the a11y snapshot shows these converted primitives rendering correctly) —
investigating separately.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:57:22 -05:00
|
|
|
}: InputDialogProps) {
|
2026-05-03 19:51:57 -05:00
|
|
|
const [value, setValue] = useState(defaultValue);
|
|
|
|
|
const [error, setError] = useState('');
|
refactor(ts): convert all UI primitives + ThemeContext to TSX (B2)
Foundational for phase B: the ui/* primitives were untyped .jsx, and TS infers
React-19 ref-as-prop components as *requiring* a ref, so every consumer errored.
Converted all 22 ui primitives (button, input, card, dialog, select, table,
dropdown-menu, tabs, badge, checkbox, switch, tooltip, separator, label,
Skeleton, collapsible, alert-dialog, sonner, save-status, theme-toggle,
input-dialog, confirm-dialog) using ComponentProps<'el'> / ComponentProps<typeof
Radix.X> + VariantProps for cva components — refs now optional, event params
typed. Also converted ThemeContext.tsx (createContext(null) made useContext's
post-guard return `never`, so setTheme was uncallable — fixed with a typed
context value) and three tracker cells (EditableCell/NotesCell/
LowerThisMonthButton) that consume the branded TrackerRow.
Added a shared errMessage(unknown) helper to utils (strict catch → unknown);
usePaymentActions reuses it. TrackerRow gains monthly_notes + amount_suggestion
(real server fields).
typecheck 0, lint 0 errors (42 warns), build green, 48 client tests. NOTE: the
e2e auth-setup probe is currently red on an unrelated, pre-existing
release-notes-modal dismissal race (reproduces on the last known-good commit;
the a11y snapshot shows these converted primitives rendering correctly) —
investigating separately.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:57:22 -05:00
|
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
2026-05-03 19:51:57 -05:00
|
|
|
|
|
|
|
|
// Reset value when dialog opens
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (open) {
|
|
|
|
|
setValue(defaultValue);
|
|
|
|
|
setError('');
|
|
|
|
|
// autofocus + select all after animation frame
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
inputRef.current?.focus();
|
|
|
|
|
inputRef.current?.select();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}, [open, defaultValue]);
|
|
|
|
|
|
|
|
|
|
const handleConfirm = () => {
|
|
|
|
|
const trimmed = value.trim();
|
|
|
|
|
if (!trimmed) { setError('This field is required.'); return; }
|
|
|
|
|
if (validate) {
|
|
|
|
|
const msg = validate(trimmed);
|
|
|
|
|
if (msg) { setError(msg); return; }
|
|
|
|
|
}
|
|
|
|
|
onConfirm?.(trimmed);
|
|
|
|
|
};
|
|
|
|
|
|
refactor(ts): convert all UI primitives + ThemeContext to TSX (B2)
Foundational for phase B: the ui/* primitives were untyped .jsx, and TS infers
React-19 ref-as-prop components as *requiring* a ref, so every consumer errored.
Converted all 22 ui primitives (button, input, card, dialog, select, table,
dropdown-menu, tabs, badge, checkbox, switch, tooltip, separator, label,
Skeleton, collapsible, alert-dialog, sonner, save-status, theme-toggle,
input-dialog, confirm-dialog) using ComponentProps<'el'> / ComponentProps<typeof
Radix.X> + VariantProps for cva components — refs now optional, event params
typed. Also converted ThemeContext.tsx (createContext(null) made useContext's
post-guard return `never`, so setTheme was uncallable — fixed with a typed
context value) and three tracker cells (EditableCell/NotesCell/
LowerThisMonthButton) that consume the branded TrackerRow.
Added a shared errMessage(unknown) helper to utils (strict catch → unknown);
usePaymentActions reuses it. TrackerRow gains monthly_notes + amount_suggestion
(real server fields).
typecheck 0, lint 0 errors (42 warns), build green, 48 client tests. NOTE: the
e2e auth-setup probe is currently red on an unrelated, pre-existing
release-notes-modal dismissal race (reproduces on the last known-good commit;
the a11y snapshot shows these converted primitives rendering correctly) —
investigating separately.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:57:22 -05:00
|
|
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
2026-05-03 19:51:57 -05:00
|
|
|
if (e.key === 'Enter') { e.preventDefault(); handleConfirm(); }
|
|
|
|
|
if (e.key === 'Escape') onOpenChange?.(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
|
|
|
<DialogContent className="max-w-sm">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>{title}</DialogTitle>
|
|
|
|
|
{description && <DialogDescription>{description}</DialogDescription>}
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-1.5">
|
|
|
|
|
{label && <Label htmlFor="input-dialog-field" className="text-xs font-medium text-muted-foreground uppercase tracking-wide">{label}</Label>}
|
|
|
|
|
<Input
|
|
|
|
|
id="input-dialog-field"
|
|
|
|
|
ref={inputRef}
|
|
|
|
|
value={value}
|
|
|
|
|
onChange={(e) => { setValue(e.target.value); setError(''); }}
|
|
|
|
|
onKeyDown={handleKeyDown}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
className={error ? 'border-destructive focus-visible:ring-destructive' : ''}
|
|
|
|
|
/>
|
|
|
|
|
{error && <p className="text-xs text-destructive">{error}</p>}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<DialogFooter>
|
|
|
|
|
<Button variant="ghost" onClick={() => onOpenChange?.(false)} disabled={loading}>
|
|
|
|
|
{cancelLabel}
|
|
|
|
|
</Button>
|
|
|
|
|
<Button onClick={handleConfirm} disabled={loading || !value.trim()}>
|
|
|
|
|
{loading ? 'Saving…' : confirmLabel}
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
|
|
|
|
}
|