diff --git a/app/(dashboard)/invoices/[id]/page.tsx b/app/(dashboard)/invoices/[id]/page.tsx index f5356b70..b27ab7ee 100644 --- a/app/(dashboard)/invoices/[id]/page.tsx +++ b/app/(dashboard)/invoices/[id]/page.tsx @@ -613,15 +613,27 @@ export default function InvoiceDetailPage({ params }: { params: Promise<{ id: st {invoice.your_reference && ( -
+
Er referens - {invoice.your_reference} +
+ {invoice.your_reference.split(',').map((ref, i) => ( + + {ref.trim()} + + ))} +
)} {invoice.our_reference && ( -
+
Vår referens - {invoice.our_reference} +
+ {invoice.our_reference.split(',').map((ref, i) => ( + + {ref.trim()} + + ))} +
)} diff --git a/app/(dashboard)/invoices/new/page.tsx b/app/(dashboard)/invoices/new/page.tsx index 47c73aba..16d263d8 100644 --- a/app/(dashboard)/invoices/new/page.tsx +++ b/app/(dashboard)/invoices/new/page.tsx @@ -10,6 +10,7 @@ import { addDays, format } from 'date-fns' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' +import { TagInput } from '@/components/ui/tag-input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' @@ -653,15 +654,32 @@ export default function NewInvoicePage() {
- ( + + )} />
- + ( + + )} + />
diff --git a/components/invoices/InvoiceReviewContent.tsx b/components/invoices/InvoiceReviewContent.tsx index 09bf9c8c..cb399e10 100644 --- a/components/invoices/InvoiceReviewContent.tsx +++ b/components/invoices/InvoiceReviewContent.tsx @@ -159,9 +159,31 @@ export function InvoiceReviewContent({ {/* References/notes */} {(yourReference || ourReference || notes) && ( -
- {yourReference &&

Er referens: {yourReference}

} - {ourReference &&

Vår referens: {ourReference}

} +
+ {yourReference && ( +
+ Er referens: +
+ {yourReference.split(',').map((ref, i) => ( + + {ref.trim()} + + ))} +
+
+ )} + {ourReference && ( +
+ Vår referens: +
+ {ourReference.split(',').map((ref, i) => ( + + {ref.trim()} + + ))} +
+
+ )} {notes &&

Anteckning: {notes}

}
)} diff --git a/components/ui/tag-input.tsx b/components/ui/tag-input.tsx new file mode 100644 index 00000000..584ca221 --- /dev/null +++ b/components/ui/tag-input.tsx @@ -0,0 +1,125 @@ +'use client' + +import * as React from 'react' +import { X } from 'lucide-react' +import { cn } from '@/lib/utils' + +interface TagInputProps { + value?: string + onChange?: (value: string) => void + placeholder?: string + className?: string + disabled?: boolean +} + +const TagInput = React.forwardRef( + ({ value = '', onChange, placeholder, className, disabled }, ref) => { + const [inputValue, setInputValue] = React.useState('') + const inputRef = React.useRef(null) + + React.useImperativeHandle(ref, () => inputRef.current!) + + const tags = React.useMemo( + () => + value + .split(',') + .map((t) => t.trim()) + .filter(Boolean), + [value] + ) + + function commitTag(raw: string) { + const trimmed = raw.trim() + if (!trimmed) return + const next = [...tags, trimmed].join(', ') + onChange?.(next) + setInputValue('') + } + + function removeTag(index: number) { + const next = tags.filter((_, i) => i !== index).join(', ') + onChange?.(next) + } + + function handleKeyDown(e: React.KeyboardEvent) { + if (e.key === 'Enter' || e.key === ',') { + e.preventDefault() + commitTag(inputValue) + } else if ( + e.key === 'Backspace' && + inputValue === '' && + tags.length > 0 + ) { + removeTag(tags.length - 1) + } + } + + function handleBlur() { + if (inputValue.trim()) { + commitTag(inputValue) + } + } + + function handlePaste(e: React.ClipboardEvent) { + const pasted = e.clipboardData.getData('text') + if (pasted.includes(',')) { + e.preventDefault() + const newTags = pasted + .split(',') + .map((t) => t.trim()) + .filter(Boolean) + const next = [...tags, ...newTags].join(', ') + onChange?.(next) + setInputValue('') + } + } + + return ( +
inputRef.current?.focus()} + > + {tags.map((tag, i) => ( + + {tag} + {!disabled && ( + + )} + + ))} + setInputValue(e.target.value)} + onKeyDown={handleKeyDown} + onBlur={handleBlur} + onPaste={handlePaste} + placeholder={tags.length === 0 ? placeholder : undefined} + disabled={disabled} + className="min-w-[80px] flex-1 bg-transparent outline-none placeholder:text-muted-foreground" + /> +
+ ) + } +) +TagInput.displayName = 'TagInput' + +export { TagInput } diff --git a/lib/invoices/pdf-template.tsx b/lib/invoices/pdf-template.tsx index 9cdfd5c2..c52fed62 100644 --- a/lib/invoices/pdf-template.tsx +++ b/lib/invoices/pdf-template.tsx @@ -348,15 +348,27 @@ export function InvoicePDF({ invoice, customer, items, company, originalInvoiceN {formatDate(invoice.due_date)} {invoice.your_reference && ( - + Er referens: - {invoice.your_reference} + + {invoice.your_reference.split(',').map((ref, i) => ( + + {ref.trim()} + + ))} + )} {invoice.our_reference && ( - + Vår referens: - {invoice.our_reference} + + {invoice.our_reference.split(',').map((ref, i) => ( + + {ref.trim()} + + ))} + )}