el-contador 1.2.5 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,8 +31,9 @@ export function useBankImportPreview() {
31
31
  export function useConfirmBankImport() {
32
32
  const queryClient = useQueryClient();
33
33
  return useMutation({
34
- mutationFn: async (rows: BankImportPreviewRow[]) => {
35
- const { data } = await api.post('/bank-transactions/import/confirm', { rows });
34
+ mutationFn: async (payload: { rows: BankImportPreviewRow[]; accountId?: string | null }) => {
35
+ const { rows, accountId } = payload;
36
+ const { data } = await api.post('/bank-transactions/import/confirm', { rows, accountId: accountId || undefined });
36
37
  return data;
37
38
  },
38
39
  onSuccess: () => {
@@ -45,7 +46,7 @@ export function useConfirmBankImport() {
45
46
  export function useUpdateBankTransaction() {
46
47
  const queryClient = useQueryClient();
47
48
  return useMutation({
48
- mutationFn: async ({ id, ...payload }: { id: string; date?: string; type?: string; amount?: number; reference?: string; description?: string }) => {
49
+ mutationFn: async ({ id, ...payload }: { id: string; date?: string; type?: string; amount?: number; reference?: string; description?: string; accountId?: string | null }) => {
49
50
  const { data } = await api.patch(`/bank-transactions/${encodeURIComponent(id)}`, payload);
50
51
  return data;
51
52
  },
@@ -21,6 +21,9 @@ export type Payee = ContactBase;
21
21
  export type Supplier = ContactBase & {
22
22
  categoryId?: string | null;
23
23
  categoryName?: string | null;
24
+ accountId?: string | null;
25
+ accountCode?: number | null;
26
+ accountName?: string | null;
24
27
  };
25
28
 
26
29
  // Customers
@@ -21,6 +21,19 @@ export function useExpenseCategories() {
21
21
  });
22
22
  }
23
23
 
24
+ /** Expense accounts (chart of accounts 400-799) for expense dropdowns */
25
+ export function useExpenseAccounts() {
26
+ return useQuery({
27
+ queryKey: ['accounts', 'expense'],
28
+ queryFn: async () => {
29
+ const { data } = await api.get('/accounts?type=expense');
30
+ return data;
31
+ },
32
+ refetchOnMount: true,
33
+ staleTime: 0,
34
+ });
35
+ }
36
+
24
37
  export function useCreateExpense() {
25
38
  const queryClient = useQueryClient();
26
39
  return useMutation({
@@ -23,6 +23,21 @@ export function useSaveInvoiceConfig() {
23
23
  });
24
24
  }
25
25
 
26
+ export function useUploadLogo() {
27
+ const queryClient = useQueryClient();
28
+ return useMutation({
29
+ mutationFn: async (file: File) => {
30
+ const formData = new FormData();
31
+ formData.append('file', file);
32
+ const { data } = await api.post<{ logoPath: string }>('/invoice-config/logo', formData, {
33
+ headers: { 'Content-Type': 'multipart/form-data' },
34
+ });
35
+ return data;
36
+ },
37
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['invoice-config'] }),
38
+ });
39
+ }
40
+
26
41
  // Integrations
27
42
  export function useIntegrationsSettings() {
28
43
  return useQuery({
@@ -136,3 +151,72 @@ export function useDeleteExpenseCategory() {
136
151
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['expense-categories'] }),
137
152
  });
138
153
  }
154
+
155
+ // Chart of accounts: account groups and accounts
156
+ export function useAccountGroups() {
157
+ return useQuery({
158
+ queryKey: ['account-groups'],
159
+ queryFn: async () => {
160
+ const { data } = await api.get('/account-groups');
161
+ return data;
162
+ },
163
+ });
164
+ }
165
+
166
+ export function useAccounts(params?: { groupId?: string; type?: string }) {
167
+ return useQuery({
168
+ queryKey: ['accounts', params?.groupId, params?.type],
169
+ queryFn: async () => {
170
+ const sp = new URLSearchParams();
171
+ if (params?.groupId) sp.set('group_id', params.groupId);
172
+ if (params?.type) sp.set('type', params.type);
173
+ const { data } = await api.get('/accounts' + (sp.toString() ? '?' + sp.toString() : ''));
174
+ return data;
175
+ },
176
+ });
177
+ }
178
+
179
+ export function useAccountsAll() {
180
+ return useQuery({
181
+ queryKey: ['accounts', 'all'],
182
+ queryFn: async () => {
183
+ const { data } = await api.get('/accounts/all');
184
+ return data;
185
+ },
186
+ });
187
+ }
188
+
189
+ export function useSaveAccount() {
190
+ const queryClient = useQueryClient();
191
+ return useMutation({
192
+ mutationFn: async (account: any) => {
193
+ if (account.id) {
194
+ const { data } = await api.put(`/accounts/${account.id}`, account);
195
+ return data;
196
+ } else {
197
+ const { data } = await api.post('/accounts', account);
198
+ return data;
199
+ }
200
+ },
201
+ onSuccess: () => {
202
+ queryClient.invalidateQueries({ queryKey: ['accounts'] });
203
+ queryClient.invalidateQueries({ queryKey: ['account-groups'] });
204
+ queryClient.invalidateQueries({ queryKey: ['expense-categories'] });
205
+ queryClient.invalidateQueries({ queryKey: ['dashboard'] });
206
+ },
207
+ });
208
+ }
209
+
210
+ export function useDeleteAccount() {
211
+ const queryClient = useQueryClient();
212
+ return useMutation({
213
+ mutationFn: async (id: string) => {
214
+ await api.delete(`/accounts/${id}`);
215
+ },
216
+ onSuccess: () => {
217
+ queryClient.invalidateQueries({ queryKey: ['accounts'] });
218
+ queryClient.invalidateQueries({ queryKey: ['account-groups'] });
219
+ queryClient.invalidateQueries({ queryKey: ['dashboard'] });
220
+ },
221
+ });
222
+ }
@@ -34,8 +34,8 @@
34
34
  --chart-6: oklch(0.72 0.19 165);
35
35
  --chart-7: oklch(0.6 0.22 25);
36
36
  --chart-8: oklch(0.45 0.2 265);
37
- --chart-revenue: lch(73.2% 44.87 177.66)
38
- --chart-expenses: lch(52.4% 74.12 16.07);
37
+ --chart-revenue: #42d4a6;
38
+ --chart-expenses: #f13f66;
39
39
  /* Reconciliation match block */
40
40
  --reconciliation-match-bg: #eff6ff;
41
41
  --reconciliation-match-border: lch(88.74% 26.13 221.7);
@@ -79,8 +79,8 @@
79
79
  --chart-6: oklch(0.72 0.19 165);
80
80
  --chart-7: oklch(0.6 0.22 25);
81
81
  --chart-8: oklch(0.45 0.2 265);
82
- --chart-revenue: oklch(0.732 0.4487 177.66);
83
- --chart-expenses: oklch(0.527 0.861 1.16);
82
+ --chart-revenue: #10b981;
83
+ --chart-expenses: #f87171;
84
84
  --reconciliation-match-bg: #1e3a5f;
85
85
  --reconciliation-match-border: #2563eb;
86
86
  --reconciliation-match-muted-bg: rgba(30, 58, 95, 0.4);
@@ -63,11 +63,12 @@ export default function ProtectedLayout() {
63
63
 
64
64
  const sidebarContent = (
65
65
  <>
66
- <div className="p-4 flex items-center gap-2 min-h-[3rem]">
66
+ <div className="p-4 flex items-center min-h-[7rem]">
67
67
  {logoUrl ? (
68
- <img src={logoUrl} alt="" className="h-8 w-auto object-contain flex-shrink-0" />
68
+ <div className="rounded-lg bg-white p-2 w-full h-20 flex items-center justify-center">
69
+ <img src={logoUrl} alt="" className="max-w-full max-h-full w-auto h-auto object-contain" />
70
+ </div>
69
71
  ) : null}
70
- <h1 className="text-xl font-bold text-white truncate">{companyName}</h1>
71
72
  </div>
72
73
  <nav className="flex-1 px-2 py-4 space-y-1">
73
74
  {navLinks}
@@ -106,9 +107,10 @@ export default function ProtectedLayout() {
106
107
  </SheetContent>
107
108
  </Sheet>
108
109
  {logoUrl ? (
109
- <img src={logoUrl} alt="" className="h-7 w-auto object-contain flex-shrink-0" />
110
+ <div className="rounded-lg bg-white p-2 h-10 w-24 flex items-center justify-center shrink-0">
111
+ <img src={logoUrl} alt="" className="max-w-full max-h-full w-auto h-auto object-contain" />
112
+ </div>
110
113
  ) : null}
111
- <h1 className="text-lg font-bold truncate">{companyName}</h1>
112
114
  </header>
113
115
 
114
116
  {/* Desktop: sidebar */}
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect } from 'react';
2
2
  import { Link } from 'react-router-dom';
3
3
  import { useBankTransactions, useBankImportPreview, useConfirmBankImport, useUpdateBankTransaction, useDeleteBankTransaction, type BankImportPreviewRow } from '../hooks/useBank';
4
+ import { useAccountsAll } from '../hooks/useSettings';
4
5
  import { Card, CardContent, CardHeader } from '@/components/ui/card';
5
6
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
6
7
  import { Button } from '@/components/ui/button';
@@ -61,26 +62,41 @@ function SortableTh({
61
62
 
62
63
  type ReviewRow = BankImportPreviewRow & { selected: boolean };
63
64
 
65
+ const ASSET_CODE_MIN = 100;
66
+ const ASSET_CODE_MAX = 199;
67
+
64
68
  export default function Bank() {
65
69
  const { data: transactions, isLoading } = useBankTransactions();
70
+ const { data: allAccounts } = useAccountsAll();
66
71
  const previewMutation = useBankImportPreview();
67
72
  const confirmImportMutation = useConfirmBankImport();
68
73
  const updateMutation = useUpdateBankTransaction();
69
74
  const deleteMutation = useDeleteBankTransaction();
70
75
 
76
+ const assetAccounts = (allAccounts ?? []).filter(
77
+ (a: { code: number }) => a.code >= ASSET_CODE_MIN && a.code <= ASSET_CODE_MAX
78
+ ).sort((a: { code: number }, b: { code: number }) => a.code - b.code);
79
+ const defaultBankAccountId = assetAccounts.length > 0 ? assetAccounts[0].id : null;
80
+
71
81
  const [searchTerm, setSearchTerm] = useState('');
72
82
  const [filterType, setFilterType] = useState('all');
83
+ const [filterAccountId, setFilterAccountId] = useState<string>('all');
73
84
  const [page, setPage] = useState(0);
74
85
  const [editingTx, setEditingTx] = useState<any>(null);
75
86
  const [deletingTx, setDeletingTx] = useState<any>(null);
76
87
  const [deleteError, setDeleteError] = useState<string | null>(null);
77
- const [editForm, setEditForm] = useState({ date: '', type: 'out' as 'in' | 'out', amount: '', reference: '', description: '' });
88
+ const [editForm, setEditForm] = useState({ date: '', type: 'out' as 'in' | 'out', amount: '', reference: '', description: '', accountId: '' as string });
78
89
  const [importReviewOpen, setImportReviewOpen] = useState(false);
79
90
  const [reviewRows, setReviewRows] = useState<ReviewRow[]>([]);
91
+ const [importAccountId, setImportAccountId] = useState<string | null>(null);
80
92
  const [importError, setImportError] = useState<string | null>(null);
81
93
  const [sortBy, setSortBy] = useState<SortKey>('date');
82
94
  const [sortDir, setSortDir] = useState<SortDir>('desc');
83
95
 
96
+ useEffect(() => {
97
+ if (defaultBankAccountId && importAccountId === null) setImportAccountId(defaultBankAccountId);
98
+ }, [defaultBankAccountId, importAccountId]);
99
+
84
100
  const handleSort = (key: SortKey) => {
85
101
  if (sortBy === key) {
86
102
  setSortDir((d) => (d === 'asc' ? 'desc' : 'asc'));
@@ -98,6 +114,7 @@ export default function Bank() {
98
114
  amount: tx.amount != null ? String(Math.abs(Number(tx.amount))) : '',
99
115
  reference: tx.reference ?? '',
100
116
  description: tx.description ?? '',
117
+ accountId: tx.accountId ?? '',
101
118
  });
102
119
  };
103
120
 
@@ -111,6 +128,7 @@ export default function Bank() {
111
128
  amount,
112
129
  reference: editForm.reference,
113
130
  description: editForm.description,
131
+ accountId: editForm.accountId || undefined,
114
132
  });
115
133
  setEditingTx(null);
116
134
  };
@@ -164,7 +182,7 @@ export default function Bank() {
164
182
  if (toImport.length === 0) return;
165
183
  setImportError(null);
166
184
  try {
167
- await confirmImportMutation.mutateAsync(toImport);
185
+ await confirmImportMutation.mutateAsync({ rows: toImport, accountId: importAccountId || defaultBankAccountId });
168
186
  setImportReviewOpen(false);
169
187
  setReviewRows([]);
170
188
  } catch (err: any) {
@@ -178,12 +196,11 @@ export default function Bank() {
178
196
  const amountStr = String(Math.abs(Number(tx.amount)) ?? '');
179
197
  const term = searchTerm.toLowerCase().trim();
180
198
  const matchesSearch = !term || desc.includes(term) || ref.includes(term) || amountStr.includes(term.replace(',', '.'));
181
-
199
+ if (filterAccountId !== 'all' && tx.accountId !== filterAccountId) return false;
182
200
  if (filterType === 'unreconciled' && tx.reconciled) return false;
183
201
  if (filterType === 'reconciled' && !tx.reconciled) return false;
184
202
  if (filterType === 'in' && tx.type !== 'in') return false;
185
203
  if (filterType === 'out' && tx.type !== 'out') return false;
186
-
187
204
  return matchesSearch;
188
205
  });
189
206
 
@@ -258,6 +275,22 @@ export default function Bank() {
258
275
  <SelectItem value="out">Money Out</SelectItem>
259
276
  </SelectContent>
260
277
  </Select>
278
+ <Select value={filterAccountId} onValueChange={(val) => setFilterAccountId(val || 'all')}>
279
+ <SelectTrigger className="w-[180px]">
280
+ <span className="truncate">
281
+ {filterAccountId === 'all' ? 'All accounts' : (() => {
282
+ const sel = assetAccounts.find((a: { id: string }) => a.id === filterAccountId);
283
+ return sel ? `${sel.code} ${sel.name}` : 'Account...';
284
+ })()}
285
+ </span>
286
+ </SelectTrigger>
287
+ <SelectContent>
288
+ <SelectItem value="all">All accounts</SelectItem>
289
+ {assetAccounts.map((a: { id: string; code: number; name: string }) => (
290
+ <SelectItem key={a.id} value={a.id}>{a.code} {a.name}</SelectItem>
291
+ ))}
292
+ </SelectContent>
293
+ </Select>
261
294
  </div>
262
295
  </CardHeader>
263
296
  <CardContent>
@@ -275,6 +308,7 @@ export default function Bank() {
275
308
  <SortableTh label="Date" sortKey="date" currentSortKey={sortBy} sortDir={sortDir} onSort={handleSort} className="w-24" />
276
309
  <SortableTh label="Reference" sortKey="reference" currentSortKey={sortBy} sortDir={sortDir} onSort={handleSort} className="w-32" />
277
310
  <SortableTh label="Description" sortKey="description" currentSortKey={sortBy} sortDir={sortDir} onSort={handleSort} className="w-32" />
311
+ <TableHead className="w-28">Account</TableHead>
278
312
  <SortableTh label="Amount" sortKey="amount" currentSortKey={sortBy} sortDir={sortDir} onSort={handleSort} className="w-24 text-right" />
279
313
  <SortableTh label="Status" sortKey="status" currentSortKey={sortBy} sortDir={sortDir} onSort={handleSort} className="w-24" />
280
314
  <TableHead className="w-[200px] whitespace-nowrap">Actions</TableHead>
@@ -283,7 +317,7 @@ export default function Bank() {
283
317
  <TableBody>
284
318
  {totalCount === 0 ? (
285
319
  <TableRow>
286
- <TableCell colSpan={6} className="text-center py-4 text-muted-foreground">
320
+ <TableCell colSpan={7} className="text-center py-4 text-muted-foreground">
287
321
  No transactions found.
288
322
  </TableCell>
289
323
  </TableRow>
@@ -297,6 +331,10 @@ export default function Bank() {
297
331
  <TableCell className="w-32 truncate" title={tx.description || undefined}>
298
332
  {truncate(tx.description)}
299
333
  </TableCell>
334
+ <TableCell className="w-28 truncate" title={tx.accountName || (tx.accountCode != null ? String(tx.accountCode) : undefined)}>
335
+ {tx.accountCode != null ? `${tx.accountCode}` : '\u2014'}
336
+ {tx.accountName ? ` ${truncate(tx.accountName, 12)}` : ''}
337
+ </TableCell>
300
338
  <TableCell className={`w-24 text-right font-medium whitespace-nowrap ${tx.type === 'in' ? 'text-emerald-600' : 'text-slate-900'}`}>
301
339
  {tx.type === 'in' ? '+' : '-'}€{Math.abs(Number(tx.amount)).toFixed(2)}
302
340
  </TableCell>
@@ -416,6 +454,26 @@ export default function Bank() {
416
454
  onChange={(e) => setEditForm((f) => ({ ...f, description: e.target.value }))}
417
455
  />
418
456
  </div>
457
+ {assetAccounts.length > 0 && (
458
+ <div className="grid grid-cols-4 items-center gap-2">
459
+ <Label>Account</Label>
460
+ <Select value={editForm.accountId || defaultBankAccountId || ''} onValueChange={(v) => setEditForm((f) => ({ ...f, accountId: v }))}>
461
+ <SelectTrigger className="col-span-3">
462
+ <span className="truncate">
463
+ {(() => {
464
+ const sel = assetAccounts.find((a: { id: string }) => a.id === (editForm.accountId || defaultBankAccountId));
465
+ return sel ? `${sel.code} ${sel.name}` : 'Select account...';
466
+ })()}
467
+ </span>
468
+ </SelectTrigger>
469
+ <SelectContent>
470
+ {assetAccounts.map((a: { id: string; code: number; name: string }) => (
471
+ <SelectItem key={a.id} value={a.id}>{a.code} {a.name}</SelectItem>
472
+ ))}
473
+ </SelectContent>
474
+ </Select>
475
+ </div>
476
+ )}
419
477
  </div>
420
478
  <DialogFooter showCloseButton>
421
479
  <Button onClick={submitEdit} disabled={updateMutation.isPending || !editForm.date || !editForm.amount}>
@@ -456,9 +514,29 @@ export default function Bank() {
456
514
  <DialogHeader>
457
515
  <DialogTitle>Review import</DialogTitle>
458
516
  <DialogDescription>
459
- Uncheck rows to exclude from import. Edit any field as needed, then click Import selected.
517
+ Uncheck rows to exclude from import. Choose the account to import into, then click Import selected.
460
518
  </DialogDescription>
461
519
  </DialogHeader>
520
+ {assetAccounts.length > 0 && (
521
+ <div className="flex items-center gap-2 py-2 border-b">
522
+ <Label className="shrink-0">Import to account</Label>
523
+ <Select value={importAccountId || defaultBankAccountId || ''} onValueChange={(v) => setImportAccountId(v)}>
524
+ <SelectTrigger className="w-[280px]">
525
+ <span className="truncate">
526
+ {(() => {
527
+ const sel = assetAccounts.find((a: { id: string }) => a.id === (importAccountId || defaultBankAccountId));
528
+ return sel ? `${sel.code} ${sel.name}` : 'Select account...';
529
+ })()}
530
+ </span>
531
+ </SelectTrigger>
532
+ <SelectContent>
533
+ {assetAccounts.map((a: { id: string; code: number; name: string }) => (
534
+ <SelectItem key={a.id} value={a.id}>{a.code} {a.name}</SelectItem>
535
+ ))}
536
+ </SelectContent>
537
+ </Select>
538
+ </div>
539
+ )}
462
540
  <div className="flex items-center gap-2 py-2 border-b">
463
541
  <Button variant="outline" size="sm" onClick={() => setReviewRows((prev) => prev.map((r) => ({ ...r, selected: true })))}>
464
542
  Select all
@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
2
2
  import { useCustomers, useCreateCustomer, useUpdateCustomer, useDeleteCustomer, type Customer } from '../hooks/useContacts';
3
3
  import { useSuppliers, useCreateSupplier, useUpdateSupplier, useDeleteSupplier, type Supplier } from '../hooks/useContacts';
4
4
  import { usePayees, useCreatePayee, useUpdatePayee, useDeletePayee, type Payee } from '../hooks/useContacts';
5
- import { useExpenseCategories as useExpenseCategoriesHook } from '../hooks/useExpenses';
5
+ import { useExpenseCategories as useExpenseCategoriesHook, useExpenseAccounts } from '../hooks/useExpenses';
6
6
  import { Card, CardContent, CardHeader } from '@/components/ui/card';
7
7
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
8
8
  import { Button } from '@/components/ui/button';
@@ -32,6 +32,7 @@ export default function Contacts() {
32
32
  const [editing, setEditing] = useState<Customer | Supplier | Payee | null>(null);
33
33
  const [form, setForm] = useState(emptyContact);
34
34
  const [supplierCategoryId, setSupplierCategoryId] = useState<string | null>(null);
35
+ const [supplierAccountId, setSupplierAccountId] = useState<string | null>(null);
35
36
 
36
37
  const { data: customers, isLoading: loadingCustomers } = useCustomers();
37
38
  const createCustomer = useCreateCustomer();
@@ -40,6 +41,8 @@ export default function Contacts() {
40
41
 
41
42
  const { data: suppliers, isLoading: loadingSuppliers } = useSuppliers();
42
43
  const { data: categories } = useExpenseCategoriesHook();
44
+ const { data: expenseAccounts } = useExpenseAccounts();
45
+ const useAccountForSupplier = expenseAccounts && expenseAccounts.length > 0;
43
46
  const createSupplier = useCreateSupplier();
44
47
  const updateSupplier = useUpdateSupplier();
45
48
  const deleteSupplier = useDeleteSupplier();
@@ -62,9 +65,11 @@ export default function Contacts() {
62
65
  notes: editing.notes ?? '',
63
66
  });
64
67
  setSupplierCategoryId((editing as Supplier).categoryId ?? null);
68
+ setSupplierAccountId((editing as Supplier).accountId ?? (editing as Supplier).categoryId ?? null);
65
69
  } else {
66
70
  setForm(emptyContact);
67
71
  setSupplierCategoryId(null);
72
+ setSupplierAccountId(null);
68
73
  }
69
74
  }, [editing]);
70
75
 
@@ -72,6 +77,7 @@ export default function Contacts() {
72
77
  setEditing(null);
73
78
  setForm(emptyContact);
74
79
  setSupplierCategoryId(null);
80
+ setSupplierAccountId(null);
75
81
  setDialogOpen(true);
76
82
  };
77
83
 
@@ -95,7 +101,9 @@ export default function Contacts() {
95
101
  await createCustomer.mutateAsync(form);
96
102
  }
97
103
  } else if (activeTab === 'supplier') {
98
- const body = { ...form, categoryId: supplierCategoryId || undefined };
104
+ const body = useAccountForSupplier
105
+ ? { ...form, accountId: supplierAccountId || undefined }
106
+ : { ...form, categoryId: supplierCategoryId || undefined };
99
107
  if (editing?.id) {
100
108
  await updateSupplier.mutateAsync({ id: editing.id, ...body });
101
109
  } else {
@@ -228,7 +236,7 @@ export default function Contacts() {
228
236
  <TableRow key={row.id}>
229
237
  <TableCell className="font-medium">{row.name}</TableCell>
230
238
  <TableCell>{row.accountNumber || '-'}</TableCell>
231
- <TableCell>{(row as Supplier).categoryName || '-'}</TableCell>
239
+ <TableCell>{(row as Supplier).accountName || (row as Supplier).categoryName || '-'}</TableCell>
232
240
  <TableCell>{row.email || '-'}</TableCell>
233
241
  <TableCell>{row.phone || '-'}</TableCell>
234
242
  <TableCell>
@@ -348,20 +356,34 @@ export default function Contacts() {
348
356
  </div>
349
357
  {activeTab === 'supplier' && (
350
358
  <div className="space-y-2">
351
- <label className="text-sm font-medium">Default category</label>
352
- <Select value={supplierCategoryId ?? ''} onValueChange={(v) => setSupplierCategoryId(v || null)}>
353
- <SelectTrigger>
354
- <SelectValue placeholder="Optional" />
355
- </SelectTrigger>
356
- <SelectContent>
357
- <SelectItem value="">None</SelectItem>
358
- {(categories ?? []).sort((a: { sortOrder?: number }, b: { sortOrder?: number }) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)).map((cat: { id: string; name: string }) => (
359
- <SelectItem key={cat.id} value={cat.id}>
360
- {cat.name}
361
- </SelectItem>
362
- ))}
363
- </SelectContent>
364
- </Select>
359
+ <label className="text-sm font-medium">{useAccountForSupplier ? 'Default account' : 'Default category'}</label>
360
+ {useAccountForSupplier ? (
361
+ <Select value={supplierAccountId ?? ''} onValueChange={(v) => setSupplierAccountId(v || null)}>
362
+ <SelectTrigger>
363
+ <SelectValue placeholder="Optional" />
364
+ </SelectTrigger>
365
+ <SelectContent>
366
+ <SelectItem value="">None</SelectItem>
367
+ {(expenseAccounts ?? []).sort((a: any, b: any) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0) || (a.code - b.code)).map((acc: any) => (
368
+ <SelectItem key={acc.id} value={acc.id}>{acc.code} - {acc.name}</SelectItem>
369
+ ))}
370
+ </SelectContent>
371
+ </Select>
372
+ ) : (
373
+ <Select value={supplierCategoryId ?? ''} onValueChange={(v) => setSupplierCategoryId(v || null)}>
374
+ <SelectTrigger>
375
+ <SelectValue placeholder="Optional" />
376
+ </SelectTrigger>
377
+ <SelectContent>
378
+ <SelectItem value="">None</SelectItem>
379
+ {(categories ?? []).sort((a: { sortOrder?: number }, b: { sortOrder?: number }) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0)).map((cat: { id: string; name: string }) => (
380
+ <SelectItem key={cat.id} value={cat.id}>
381
+ {cat.name}
382
+ </SelectItem>
383
+ ))}
384
+ </SelectContent>
385
+ </Select>
386
+ )}
365
387
  </div>
366
388
  )}
367
389
  <div className="space-y-2">