Skip to content

Commit accfb50

Browse files
committed
feat: sort by most used customers
Signed-off-by: Jan Lauber <[email protected]>
1 parent 9f609cf commit accfb50

File tree

2 files changed

+164
-38
lines changed

2 files changed

+164
-38
lines changed

sk/src/routes/app/+layout.svelte

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
import { toast } from "svelte-sonner";
2424
import { updateDataStores, UpdateFilterEnum } from "$lib/stores/data/load";
2525
import * as Dialog from "$lib/components/ui/dialog";
26+
import { expenses } from "$lib/stores/data/expenses";
27+
28+
interface ParsedItem {
29+
value: string;
30+
label: string;
31+
frequency: number;
32+
contextFrequency: number;
33+
}
2634
2735
let open = false;
2836
let dateOpen = false;
@@ -38,14 +46,73 @@
3846
let customerValue = "";
3947
4048
// map $customers id to value and name to label
41-
let parsedCustomers = [];
49+
let parsedCustomers: ParsedItem[] = [];
50+
let parsedExpenseTypes: ParsedItem[] = [];
51+
52+
$: {
53+
// Count frequency of customers in expenses
54+
const customerFrequency = $expenses.reduce((acc: Record<string, number>, expense) => {
55+
const customerId = expense.customer;
56+
acc[customerId] = (acc[customerId] || 0) + 1;
57+
return acc;
58+
}, {});
59+
60+
// Count frequency of expense types per customer
61+
const customerTypeFrequency = $expenses.reduce((acc: Record<string, Record<string, number>>, expense) => {
62+
const customerId = expense.customer;
63+
const typeId = expense.expense_type;
64+
if (!acc[customerId]) {
65+
acc[customerId] = {};
66+
}
67+
acc[customerId][typeId] = (acc[customerId][typeId] || 0) + 1;
68+
return acc;
69+
}, {});
4270
43-
$: parsedCustomers = $customers.map((customer) => {
44-
return {
71+
// Count frequency of customers per type
72+
const typeCustomerFrequency = $expenses.reduce((acc: Record<string, Record<string, number>>, expense) => {
73+
const typeId = expense.expense_type;
74+
const customerId = expense.customer;
75+
if (!acc[typeId]) {
76+
acc[typeId] = {};
77+
}
78+
acc[typeId][customerId] = (acc[typeId][customerId] || 0) + 1;
79+
return acc;
80+
}, {});
81+
82+
// Map and sort customers based on context
83+
parsedCustomers = $customers.map((customer) => ({
4584
value: customer.id,
46-
label: customer.name
47-
};
48-
});
85+
label: customer.name,
86+
frequency: customerFrequency[customer.id] || 0,
87+
// If a type is selected, use the frequency of this customer with that type
88+
contextFrequency: expenseTypeValue ? (typeCustomerFrequency[expenseTypeValue]?.[customer.id] || 0) : 0
89+
})).sort((a, b) => {
90+
// If we have a type selected, sort by context frequency first
91+
if (expenseTypeValue) {
92+
const contextDiff = b.contextFrequency - a.contextFrequency;
93+
if (contextDiff !== 0) return contextDiff;
94+
}
95+
// Fall back to overall frequency
96+
return b.frequency - a.frequency;
97+
});
98+
99+
// Map and sort expense types based on context
100+
parsedExpenseTypes = $expenseTypes.map((expenseType) => ({
101+
value: expenseType.id,
102+
label: expenseType.name,
103+
frequency: customerFrequency[expenseType.id] || 0,
104+
// If a customer is selected, use the frequency of this type with that customer
105+
contextFrequency: customerValue ? (customerTypeFrequency[customerValue]?.[expenseType.id] || 0) : 0
106+
})).sort((a, b) => {
107+
// If we have a customer selected, sort by context frequency first
108+
if (customerValue) {
109+
const contextDiff = b.contextFrequency - a.contextFrequency;
110+
if (contextDiff !== 0) return contextDiff;
111+
}
112+
// Fall back to overall frequency
113+
return b.frequency - a.frequency;
114+
});
115+
}
49116
50117
$: selectedCustomerValue =
51118
parsedCustomers.find((f) => f.value === customerValue)?.label ?? "Select a customer...";
@@ -60,16 +127,6 @@
60127
let expenseTypeOpen = false;
61128
let expenseTypeValue = "";
62129
63-
// map $expenseTypes value to value and label to label
64-
let parsedExpenseTypes = [];
65-
66-
$: parsedExpenseTypes = $expenseTypes.map((expenseType) => {
67-
return {
68-
value: expenseType.id,
69-
label: expenseType.name
70-
};
71-
});
72-
73130
$: selectedExpenseTypeValue =
74131
parsedExpenseTypes.find((f) => f.value === expenseTypeValue)?.label ??
75132
"Select an expense type...";
@@ -245,7 +302,10 @@
245302
customerValue !== customer.value && "text-transparent"
246303
)}
247304
/>
248-
{customer.label}
305+
<span class="flex-1">{customer.label}</span>
306+
{#if customer.frequency > 0}
307+
<span class="text-xs text-muted-foreground ml-2">{customer.frequency}x</span>
308+
{/if}
249309
</Command.Item>
250310
{/each}
251311
</Command.Group>
@@ -285,7 +345,10 @@
285345
expenseTypeValue !== expenseType.value && "text-transparent"
286346
)}
287347
/>
288-
{expenseType.label}
348+
<span class="flex-1">{expenseType.label}</span>
349+
{#if expenseType.frequency > 0}
350+
<span class="text-xs text-muted-foreground ml-2">{expenseType.frequency}x</span>
351+
{/if}
289352
</Command.Item>
290353
{/each}
291354
</Command.Group>

sk/src/routes/app/+page.svelte

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,34 +74,91 @@
7474
let editDateValue: DateValue | undefined = undefined;
7575
7676
// For edit form
77-
let parsedCustomers: Array<{ value: string, label: string }> = [];
78-
let parsedExpenseTypes: Array<{ value: string, label: string }> = [];
77+
let parsedCustomers: Array<{ value: string, label: string, frequency: number }> = [];
78+
let parsedExpenseTypes: Array<{ value: string, label: string, frequency: number }> = [];
7979
let editCustomerOpen = false;
8080
let editExpenseTypeOpen = false;
8181
82-
$: parsedCustomers = $customers.map((customer) => {
83-
return {
82+
let customerOpen = false;
83+
let customerValue = "";
84+
let expenseTypeOpen = false;
85+
let expenseTypeValue = "";
86+
87+
// For frequency tracking
88+
$: {
89+
// Count frequency of customers in expenses
90+
const customerFrequency = $expenses.reduce((acc, expense) => {
91+
const customerId = expense.customer;
92+
acc[customerId] = (acc[customerId] || 0) + 1;
93+
return acc;
94+
}, {} as Record<string, number>);
95+
96+
// Count frequency of expense types per customer
97+
const customerTypeFrequency = $expenses.reduce((acc, expense) => {
98+
const customerId = expense.customer;
99+
const typeId = expense.expense_type;
100+
if (!acc[customerId]) {
101+
acc[customerId] = {};
102+
}
103+
acc[customerId][typeId] = (acc[customerId][typeId] || 0) + 1;
104+
return acc;
105+
}, {} as Record<string, Record<string, number>>);
106+
107+
// Count frequency of customers per type
108+
const typeCustomerFrequency = $expenses.reduce((acc, expense) => {
109+
const typeId = expense.expense_type;
110+
const customerId = expense.customer;
111+
if (!acc[typeId]) {
112+
acc[typeId] = {};
113+
}
114+
acc[typeId][customerId] = (acc[typeId][customerId] || 0) + 1;
115+
return acc;
116+
}, {} as Record<string, Record<string, number>>);
117+
118+
// Map and sort customers based on context
119+
parsedCustomers = $customers.map((customer) => ({
84120
value: customer.id,
85-
label: customer.name
86-
};
87-
});
121+
label: customer.name,
122+
frequency: customerFrequency[customer.id] || 0,
123+
// If a type is selected, use the frequency of this customer with that type
124+
contextFrequency: expenseTypeValue ? (typeCustomerFrequency[expenseTypeValue]?.[customer.id] || 0) : 0
125+
})).sort((a, b) => {
126+
// If we have a type selected, sort by context frequency first
127+
if (expenseTypeValue) {
128+
const contextDiff = b.contextFrequency - a.contextFrequency;
129+
if (contextDiff !== 0) return contextDiff;
130+
}
131+
// Fall back to overall frequency
132+
return b.frequency - a.frequency;
133+
});
88134
89-
$: parsedExpenseTypes = $expenseTypes.map((expenseType) => {
90-
return {
135+
// Map and sort expense types based on context
136+
parsedExpenseTypes = $expenseTypes.map((expenseType) => ({
91137
value: expenseType.id,
92-
label: expenseType.name
93-
};
94-
});
138+
label: expenseType.name,
139+
frequency: customerFrequency[expenseType.id] || 0,
140+
// If a customer is selected, use the frequency of this type with that customer
141+
contextFrequency: customerValue ? (customerTypeFrequency[customerValue]?.[expenseType.id] || 0) : 0
142+
})).sort((a, b) => {
143+
// If we have a customer selected, sort by context frequency first
144+
if (customerValue) {
145+
const contextDiff = b.contextFrequency - a.contextFrequency;
146+
if (contextDiff !== 0) return contextDiff;
147+
}
148+
// Fall back to overall frequency
149+
return b.frequency - a.frequency;
150+
});
151+
}
95152
96-
function closeEditCustomerCombobox(triggerId: string) {
97-
editCustomerOpen = false;
153+
function closeCustomerCombobox(triggerId: string) {
154+
customerOpen = false;
98155
tick().then(() => {
99156
document.getElementById(triggerId)?.focus();
100157
});
101158
}
102159
103-
function closeEditExpenseTypeCombobox(triggerId: string) {
104-
editExpenseTypeOpen = false;
160+
function closeExpenseTypeCombobox(triggerId: string) {
161+
expenseTypeOpen = false;
105162
tick().then(() => {
106163
document.getElementById(triggerId)?.focus();
107164
});
@@ -570,7 +627,7 @@
570627
if (editingExpense) {
571628
editingExpense.customer_id = customer.value;
572629
editingExpense.customer = customer.label;
573-
closeEditCustomerCombobox(ids.trigger);
630+
closeCustomerCombobox(ids.trigger);
574631
}
575632
}}
576633
>
@@ -580,7 +637,10 @@
580637
editingExpense.customer_id !== customer.value && "text-transparent"
581638
)}
582639
/>
583-
{customer.label}
640+
<span class="flex-1">{customer.label}</span>
641+
{#if customer.frequency > 0}
642+
<span class="text-xs text-muted-foreground ml-2">{customer.frequency}x</span>
643+
{/if}
584644
</Command.Item>
585645
{/each}
586646
</Command.Group>
@@ -613,7 +673,7 @@
613673
if (editingExpense) {
614674
editingExpense.expense_type = expenseType.value;
615675
editingExpense.type = expenseType.label;
616-
closeEditExpenseTypeCombobox(ids.trigger);
676+
closeExpenseTypeCombobox(ids.trigger);
617677
}
618678
}}
619679
>
@@ -623,7 +683,10 @@
623683
editingExpense.expense_type !== expenseType.value && "text-transparent"
624684
)}
625685
/>
626-
{expenseType.label}
686+
<span class="flex-1">{expenseType.label}</span>
687+
{#if expenseType.frequency > 0}
688+
<span class="text-xs text-muted-foreground ml-2">{expenseType.frequency}x</span>
689+
{/if}
627690
</Command.Item>
628691
{/each}
629692
</Command.Group>

0 commit comments

Comments
 (0)