diff --git a/.gitignore b/.gitignore index ffb43f1..d8a3e00 100644 --- a/.gitignore +++ b/.gitignore @@ -36,10 +36,6 @@ yarn-error.log* .env .env*.local .env*.production -.env*.vercel - -# vercel -.vercel # typescript *.tsbuildinfo diff --git a/src/components/forms/invoice-calendar-view.tsx b/src/components/forms/invoice-calendar-view.tsx index 2cc5580..94e7697 100644 --- a/src/components/forms/invoice-calendar-view.tsx +++ b/src/components/forms/invoice-calendar-view.tsx @@ -312,52 +312,60 @@ export function InvoiceCalendarView({ ) : (
{selectedDateItems.map(({ item, index }) => ( -
-
-
- +
+
+ {/* Description */} +
onUpdateItem(index, "description", e.target.value)} - placeholder="What did you work on?" - className="bg-muted/30 border-transparent focus:border-input focus:bg-background transition-all font-medium" + placeholder="Describe the work performed..." + className="w-full text-sm font-medium" />
-
-
-
- + + {/* Controls Row */} +
+ {/* Hours */} onUpdateItem(index, "hours", v)} step={0.25} min={0} - className="bg-muted/30" + width="auto" + className="h-9 flex-1 min-w-[100px] font-mono" + suffix="h" /> -
-
- + + {/* Rate */} onUpdateItem(index, "rate", v)} prefix="$" min={0} - className="bg-muted/30" + step={1} + width="auto" + className="h-9 flex-1 min-w-[100px] font-mono" /> + + {/* Amount */} +
+ + ${(item.hours * item.rate).toFixed(2)} + +
+ + {/* Actions */} +
-
- - ${(item.hours * item.rate).toFixed(2)} - -
-
))} diff --git a/src/components/forms/invoice-form.tsx b/src/components/forms/invoice-form.tsx index a57de08..2906667 100644 --- a/src/components/forms/invoice-form.tsx +++ b/src/components/forms/invoice-form.tsx @@ -105,7 +105,7 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { hours: item.hours, rate: item.rate, amount: item.amount, - })) || []; + })).sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) || []; setFormData({ invoiceNumber: existingInvoice.invoiceNumber, businessId: existingInvoice.businessId ?? "", @@ -134,10 +134,11 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { }, [formData.items, formData.taxRate]); // Handlers (addItem, updateItem etc. - same as before) - const addItem = (date?: Date) => { + const addItem = (date?: Date | unknown) => { + const validDate = date instanceof Date ? date : new Date(); setFormData((prev) => ({ ...prev, - items: [...prev.items, { id: crypto.randomUUID(), date: date ?? new Date(), description: "", hours: 1, rate: prev.defaultHourlyRate ?? 0, amount: prev.defaultHourlyRate ?? 0 }], + items: [...prev.items, { id: crypto.randomUUID(), date: validDate, description: "", hours: 1, rate: prev.defaultHourlyRate ?? 0, amount: prev.defaultHourlyRate ?? 0 }], })); }; const removeItem = (idx: number) => { if (formData.items.length > 1) setFormData((prev) => ({ ...prev, items: prev.items.filter((_, i) => i !== idx) })); }; @@ -232,7 +233,9 @@ export default function InvoiceForm({ invoiceId }: InvoiceFormProps) { status: formData.status, notes: formData.notes, taxRate: formData.taxRate, - items: formData.items.map(i => ({ date: i.date, description: i.description, hours: i.hours, rate: i.rate, amount: i.hours * i.rate })), + items: formData.items + .sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) + .map(i => ({ date: i.date, description: i.description, hours: i.hours, rate: i.rate, amount: i.hours * i.rate })), }; if (invoiceId && invoiceId !== "new" && invoiceId !== undefined) await updateInvoice.mutateAsync({ id: invoiceId, ...payload }); else await createInvoice.mutateAsync(payload); diff --git a/src/components/forms/invoice-line-items.tsx b/src/components/forms/invoice-line-items.tsx index e266317..28862e9 100644 --- a/src/components/forms/invoice-line-items.tsx +++ b/src/components/forms/invoice-line-items.tsx @@ -174,7 +174,7 @@ function SortableLineItem({ value={item.description} onChange={(e) => onUpdate(index, "description", e.target.value)} placeholder="Describe the work performed..." - className="w-full text-sm font-medium border-transparent bg-transparent hover:bg-muted/50 focus:bg-background focus:ring-1 focus:ring-ring transition-all rounded-md px-2 -ml-2" + className="w-full text-sm font-medium" />
@@ -187,7 +187,8 @@ function SortableLineItem({ onUpdate(index, "date", date ?? new Date()) } size="sm" - className="h-9 w-36" + className="w-full sm:w-[180px]" + inputClassName="h-9" /> {/* Hours */} @@ -197,7 +198,8 @@ function SortableLineItem({ min={0} step={0.25} width="auto" - className="h-9 w-32" + className="h-9 flex-1 min-w-[100px] font-mono" + suffix="h" /> {/* Rate */} @@ -208,7 +210,7 @@ function SortableLineItem({ step={1} prefix="$" width="auto" - className="h-9 w-32" + className="h-9 flex-1 min-w-[100px] font-mono" /> {/* Amount */} @@ -281,6 +283,7 @@ function MobileLineItem({ date={item.date} onDateChange={(date) => onUpdate(index, "date", date ?? new Date())} size="sm" + inputClassName="h-9" />
diff --git a/src/components/ui/date-picker.tsx b/src/components/ui/date-picker.tsx index 332c686..b38f39d 100644 --- a/src/components/ui/date-picker.tsx +++ b/src/components/ui/date-picker.tsx @@ -34,6 +34,7 @@ interface DatePickerProps { disabled?: boolean; id?: string; size?: "sm" | "md" | "lg"; + inputClassName?: string; } export function DatePicker({ @@ -41,6 +42,7 @@ export function DatePicker({ onDateChange, placeholder = "Tomorrow or next week", className, + inputClassName, disabled = false, id, size = "md", @@ -58,8 +60,8 @@ export function DatePicker({ const inputWidthClass = className?.includes("w-full") ? "w-full" : className?.includes("w-32") || - className?.includes("w-28") || - className?.includes("w-36") + className?.includes("w-28") || + className?.includes("w-36") ? className : "w-full md:w-32 md:min-w-32"; @@ -75,7 +77,7 @@ export function DatePicker({ value={value} placeholder={placeholder} disabled={disabled} - className={cn("bg-background pr-10", sizeClasses[size], "w-full")} + className={cn("bg-background pr-10", sizeClasses[size], "w-full", inputClassName)} onChange={(e) => { setValue(e.target.value); const parsedDate = parseDate(e.target.value); diff --git a/src/components/ui/number-input.tsx b/src/components/ui/number-input.tsx index cb1cba2..ae8f17b 100644 --- a/src/components/ui/number-input.tsx +++ b/src/components/ui/number-input.tsx @@ -36,8 +36,13 @@ export function NumberInput({ value ? value.toFixed(2) : "0.00", ); + const inputRef = React.useRef(null); + React.useEffect(() => { - setDisplayValue(value ? value.toFixed(2) : "0.00"); + // Only update display value if the input is NOT focused + if (document.activeElement !== inputRef.current) { + setDisplayValue(value ? value.toFixed(2) : "0.00"); + } }, [value]); const handleChange = (e: React.ChangeEvent) => { @@ -95,6 +100,7 @@ export function NumberInput({ {prefix} )}