Skip to content

Commit 2069b18

Browse files
committed
feat(produits): design of compact cells
1 parent 08e0adf commit 2069b18

4 files changed

Lines changed: 181 additions & 154 deletions

File tree

frontend/src/lib/components/admin/fournisseurCell.svelte

Lines changed: 75 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,78 +24,86 @@
2424

2525
<div class="relative">
2626
{#if !expanded}
27-
<div
28-
class="flex items-center gap-2 text-xs"
29-
role="button"
30-
tabindex="0"
27+
<button
28+
class="w-full text-left p-2 rounded-md border border-gray-200 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-950/30 transition-all group"
3129
on:click={() => (expanded = true)}
32-
on:keydown={(e) => e.key === 'Enter' && (expanded = true)}
3330
>
34-
<span class="dark:text-gray-300">{getFournisseurName(item.fournisseur)}</span>
35-
{#if item.amount_per_bundle || item.ref_bundle}
36-
<span class="text-gray-500 dark:text-gray-400">
37-
{item.amount_per_bundle ? `| B:${item.amount_per_bundle}` : ''}
38-
{item.ref_bundle ? ` | R:${item.ref_bundle}` : ''}
39-
</span>
40-
{/if}
41-
<button
42-
class="p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors"
43-
on:click|stopPropagation={() => (expanded = true)}
44-
>
45-
<iconify-icon icon="mdi:pencil" width="14" height="14"></iconify-icon>
46-
</button>
47-
</div>
31+
<div class="flex items-center justify-between gap-2">
32+
<div class="flex flex-col gap-1 text-xs flex-1 min-w-0">
33+
<div class="flex items-center gap-2">
34+
<span class="text-gray-500 dark:text-gray-400 font-medium">Fourn:</span>
35+
<span class="font-semibold dark:text-gray-200 truncate">{getFournisseurName(item.fournisseur)}</span>
36+
</div>
37+
{#if item.amount_per_bundle || item.ref_bundle}
38+
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-400">
39+
{#if item.amount_per_bundle}
40+
<span>Bundle: {item.amount_per_bundle}</span>
41+
{/if}
42+
{#if item.ref_bundle}
43+
<span>Ref: {item.ref_bundle}</span>
44+
{/if}
45+
</div>
46+
{/if}
47+
</div>
48+
<iconify-icon icon="mdi:chevron-down" width="16" height="16" class="text-gray-400 group-hover:text-blue-500 transition-colors flex-shrink-0"></iconify-icon>
49+
</div>
50+
</button>
4851
{:else}
49-
<div class="min-w-[160px] space-y-2 mt-8">
50-
<button
51-
class="absolute -top-8 right-2 px-2 py-1 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded z-10 flex items-center gap-1 text-xs"
52-
on:click={() => (expanded = false)}
53-
>
54-
<iconify-icon icon="mdi:close" width="14" height="14"></iconify-icon>
55-
<span>Fermer</span>
56-
</button>
57-
<div>
58-
<label class="text-xs block dark:text-gray-400 mb-1">Fournisseur</label>
59-
<select
60-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
61-
value={item.fournisseur ?? ''}
62-
on:change={(e) => {
63-
// @ts-ignore
64-
editItem(item.id, { fournisseur: e.target?.value || undefined }, item.category_id);
65-
}}
52+
<div class="border border-blue-500 dark:border-blue-400 rounded-md p-3 bg-blue-50/50 dark:bg-blue-950/20">
53+
<div class="flex items-center justify-between mb-3">
54+
<span class="text-xs font-semibold text-blue-700 dark:text-blue-300">Modifier Fournisseur</span>
55+
<button
56+
class="px-2 py-1 text-xs bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded flex items-center gap-1 transition-colors"
57+
on:click={() => (expanded = false)}
6658
>
67-
<option value="">-</option>
68-
{#each fournisseurIterator as [val, name]}
69-
<option value={val}>{name}</option>
70-
{/each}
71-
</select>
72-
</div>
73-
<div>
74-
<label class="text-xs block dark:text-gray-400 mb-1">Bundle/Qté</label>
75-
<input
76-
type="number"
77-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
78-
value={item.amount_per_bundle ?? ''}
79-
placeholder="0"
80-
on:input={(e) => {
81-
// @ts-ignore
82-
let val = e.target?.value;
83-
editItem(item.id, { amount_per_bundle: val ? parseInt(val) : undefined }, item.category_id);
84-
}}
85-
/>
59+
<iconify-icon icon="mdi:check" width="14" height="14"></iconify-icon>
60+
<span>Fermer</span>
61+
</button>
8662
</div>
87-
<div>
88-
<label class="text-xs block dark:text-gray-400 mb-1">Ref Code</label>
89-
<input
90-
type="text"
91-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
92-
value={item.ref_bundle ?? ''}
93-
placeholder="-"
94-
on:input={(e) => {
95-
// @ts-ignore
96-
editItem(item.id, { ref_bundle: e.target?.value || undefined }, item.category_id);
97-
}}
98-
/>
63+
<div class="space-y-2">
64+
<div>
65+
<label class="text-xs block font-medium dark:text-gray-300 mb-1">Fournisseur</label>
66+
<select
67+
class="w-full text-xs px-2 py-1.5 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
68+
value={item.fournisseur ?? ''}
69+
on:change={(e) => {
70+
// @ts-ignore
71+
editItem(item.id, { fournisseur: e.target?.value || undefined }, item.category_id);
72+
}}
73+
>
74+
<option value="">-</option>
75+
{#each fournisseurIterator as [val, name]}
76+
<option value={val}>{name}</option>
77+
{/each}
78+
</select>
79+
</div>
80+
<div>
81+
<label class="text-xs block font-medium dark:text-gray-300 mb-1">Bundle/Quantité</label>
82+
<input
83+
type="number"
84+
class="w-full text-xs px-2 py-1.5 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
85+
value={item.amount_per_bundle ?? ''}
86+
placeholder="0"
87+
on:input={(e) => {
88+
// @ts-ignore
89+
let val = e.target?.value;
90+
editItem(item.id, { amount_per_bundle: val ? parseInt(val) : undefined }, item.category_id);
91+
}}
92+
/>
93+
</div>
94+
<div>
95+
<label class="text-xs block font-medium dark:text-gray-300 mb-1">Code Référence</label>
96+
<input
97+
type="text"
98+
class="w-full text-xs px-2 py-1.5 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
99+
value={item.ref_bundle ?? ''}
100+
placeholder="-"
101+
on:input={(e) => {
102+
// @ts-ignore
103+
editItem(item.id, { ref_bundle: e.target?.value || undefined }, item.category_id);
104+
}}
105+
/>
106+
</div>
99107
</div>
100108
</div>
101109
{/if}

frontend/src/lib/components/admin/priceCell.svelte

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,46 +41,55 @@
4141

4242
<div class="relative">
4343
{#if !expanded}
44-
<div
45-
class="flex items-center gap-1 flex-wrap text-xs"
46-
role="button"
47-
tabindex="0"
44+
<button
45+
class="w-full text-left p-2 rounded-md border border-gray-200 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-950/30 transition-all group"
4846
on:click={() => (expanded = true)}
49-
on:keydown={(e) => e.key === 'Enter' && (expanded = true)}
5047
>
51-
{#each priceRoles as role}
52-
<span class="bg-gray-100 dark:bg-gray-700 px-1 rounded">{getCompactPrice(role)}</span>
53-
{/each}
54-
<button
55-
class="ml-1 p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors"
56-
on:click|stopPropagation={() => (expanded = true)}
57-
>
58-
<iconify-icon icon="mdi:pencil" width="14" height="14"></iconify-icon>
59-
</button>
60-
</div>
61-
{:else}
62-
<div class="min-w-[180px] space-y-1 mt-8">
63-
<button
64-
class="absolute -top-8 right-2 px-2 py-1 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded z-10 flex items-center gap-1 text-xs"
65-
on:click={() => (expanded = false)}
66-
>
67-
<iconify-icon icon="mdi:close" width="14" height="14"></iconify-icon>
68-
<span>Fermer</span>
69-
</button>
70-
{#each priceRoles as role}
71-
{@const price = item.prices[role]}
72-
{@const label = role === 'coutant' ? 'Coutant' : role === 'externe' ? 'Externe' : role === 'ceten' ? 'Ceten' : role === 'staff_bar' ? 'Staff' : role === 'privilegies' ? 'Privil.' : 'Menu'}
73-
<div class="flex items-center gap-2">
74-
<span class="text-xs w-12 shrink-0 dark:text-gray-300">{label}:</span>
75-
<input
76-
type="number"
77-
id="price-{item.id}-{role}"
78-
placeholder={formatPrice(price)}
79-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
80-
on:input={(e) => handlePriceChange(role, e)}
81-
/>
48+
<div class="flex items-center justify-between gap-2">
49+
<div class="grid grid-cols-2 gap-x-3 gap-y-1 text-xs flex-1 min-w-0">
50+
{#each priceRoles as role}
51+
{@const label = priceLabels[role]}
52+
{@const price = formatPrice(item.prices[role])}
53+
<div class="flex items-center gap-1.5">
54+
<span class="text-gray-500 dark:text-gray-400 font-medium">{label}:</span>
55+
<span class="font-semibold dark:text-gray-200">{price}</span>
56+
</div>
57+
{/each}
8258
</div>
83-
{/each}
59+
<iconify-icon icon="mdi:chevron-down" width="16" height="16" class="text-gray-400 group-hover:text-blue-500 transition-colors flex-shrink-0"></iconify-icon>
60+
</div>
61+
</button>
62+
{:else}
63+
<div class="border border-blue-500 dark:border-blue-400 rounded-md p-3 bg-blue-50/50 dark:bg-blue-950/20">
64+
<div class="flex items-center justify-between mb-3">
65+
<span class="text-xs font-semibold text-blue-700 dark:text-blue-300">Modifier Prix</span>
66+
<button
67+
class="px-2 py-1 text-xs bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded flex items-center gap-1 transition-colors"
68+
on:click={() => (expanded = false)}
69+
>
70+
<iconify-icon icon="mdi:check" width="14" height="14"></iconify-icon>
71+
<span>Fermer</span>
72+
</button>
73+
</div>
74+
<div class="space-y-2">
75+
{#each priceRoles as role}
76+
{@const price = item.prices[role]}
77+
{@const label = role === 'coutant' ? 'Coutant' : role === 'externe' ? 'Externe' : role === 'ceten' ? 'Ceten' : role === 'staff_bar' ? 'Staff' : role === 'privilegies' ? 'Privil.' : 'Menu'}
78+
<div class="flex items-center gap-2">
79+
<label class="text-xs w-16 shrink-0 font-medium dark:text-gray-300">{label}:</label>
80+
<div class="flex-1 relative">
81+
<input
82+
type="number"
83+
id="price-{item.id}-{role}"
84+
placeholder={formatPrice(price)}
85+
class="w-full text-xs px-2 py-1.5 pr-6 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
86+
on:input={(e) => handlePriceChange(role, e)}
87+
/>
88+
<span class="absolute right-2 top-1/2 -translate-y-1/2 text-xs text-gray-400">€</span>
89+
</div>
90+
</div>
91+
{/each}
92+
</div>
8493
</div>
8594
{/if}
8695
</div>

frontend/src/lib/components/admin/stockCell.svelte

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,58 +35,68 @@
3535

3636
<div class="relative">
3737
{#if !expanded}
38-
<div
39-
class="flex items-center gap-1 flex-wrap text-xs"
40-
role="button"
41-
tabindex="0"
38+
<button
39+
class="w-full text-left p-2 rounded-md border border-gray-200 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 hover:bg-blue-50 dark:hover:bg-blue-950/30 transition-all group"
4240
on:click={() => (expanded = true)}
43-
on:keydown={(e) => e.key === 'Enter' && (expanded = true)}
4441
>
45-
<span class="bg-gray-100 dark:bg-gray-700 px-1 rounded">Stock:{item.amount_left}</span>
46-
<span class="bg-gray-100 dark:bg-gray-700 px-1 rounded">Lim:{item.buy_limit ?? '-'}</span>
47-
<span class="bg-gray-100 dark:bg-gray-700 px-1 rounded">Opt:{item.optimal_amount}</span>
48-
<button
49-
class="ml-1 p-1 hover:bg-gray-200 dark:hover:bg-gray-600 rounded transition-colors"
50-
on:click|stopPropagation={() => (expanded = true)}
51-
>
52-
<iconify-icon icon="mdi:pencil" width="14" height="14"></iconify-icon>
53-
</button>
54-
</div>
55-
{:else}
56-
<div class="min-w-[140px] space-y-1 mt-8">
57-
<button
58-
class="absolute -top-8 right-2 px-2 py-1 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded z-10 flex items-center gap-1 text-xs"
59-
on:click={() => (expanded = false)}
60-
>
61-
<iconify-icon icon="mdi:close" width="14" height="14"></iconify-icon>
62-
<span>Fermer</span>
63-
</button>
64-
<div class="flex items-center gap-2">
65-
<span class="text-xs w-14 shrink-0 dark:text-gray-300">Stock:</span>
66-
<input
67-
type="number"
68-
value={item.amount_left}
69-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
70-
on:input={handleStockChange}
71-
/>
42+
<div class="flex items-center justify-between gap-2">
43+
<div class="flex flex-col gap-1 text-xs flex-1 min-w-0">
44+
<div class="flex items-center gap-2">
45+
<span class="text-gray-500 dark:text-gray-400 font-medium w-14">Stock:</span>
46+
<span class="font-semibold dark:text-gray-200">{item.amount_left}</span>
47+
</div>
48+
<div class="flex items-center gap-2">
49+
<span class="text-gray-500 dark:text-gray-400 font-medium w-14">Limite:</span>
50+
<span class="font-semibold dark:text-gray-200">{item.buy_limit ?? '-'}</span>
51+
</div>
52+
<div class="flex items-center gap-2">
53+
<span class="text-gray-500 dark:text-gray-400 font-medium w-14">Optimal:</span>
54+
<span class="font-semibold dark:text-gray-200">{item.optimal_amount}</span>
55+
</div>
56+
</div>
57+
<iconify-icon icon="mdi:chevron-down" width="16" height="16" class="text-gray-400 group-hover:text-blue-500 transition-colors flex-shrink-0"></iconify-icon>
7258
</div>
73-
<div class="flex items-center gap-2">
74-
<span class="text-xs w-14 shrink-0 dark:text-gray-300">Limite:</span>
75-
<input
76-
type="number"
77-
value={item.buy_limit}
78-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
79-
on:input={handleLimitChange}
80-
/>
59+
</button>
60+
{:else}
61+
<div class="border border-blue-500 dark:border-blue-400 rounded-md p-3 bg-blue-50/50 dark:bg-blue-950/20">
62+
<div class="flex items-center justify-between mb-3">
63+
<span class="text-xs font-semibold text-blue-700 dark:text-blue-300">Modifier Stocks</span>
64+
<button
65+
class="px-2 py-1 text-xs bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded flex items-center gap-1 transition-colors"
66+
on:click={() => (expanded = false)}
67+
>
68+
<iconify-icon icon="mdi:check" width="14" height="14"></iconify-icon>
69+
<span>Fermer</span>
70+
</button>
8171
</div>
82-
<div class="flex items-center gap-2">
83-
<span class="text-xs w-14 shrink-0 dark:text-gray-300">Optimal:</span>
84-
<input
85-
type="number"
86-
value={item.optimal_amount}
87-
class="w-full text-xs px-2 py-1 border border-gray-200 dark:border-gray-600 rounded dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:outline-none"
88-
on:input={handleOptimalChange}
89-
/>
72+
<div class="space-y-2">
73+
<div class="flex items-center gap-2">
74+
<label class="text-xs w-16 shrink-0 font-medium dark:text-gray-300">Stock:</label>
75+
<input
76+
type="number"
77+
value={item.amount_left}
78+
class="flex-1 text-xs px-2 py-1.5 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
79+
on:input={handleStockChange}
80+
/>
81+
</div>
82+
<div class="flex items-center gap-2">
83+
<label class="text-xs w-16 shrink-0 font-medium dark:text-gray-300">Limite:</label>
84+
<input
85+
type="number"
86+
value={item.buy_limit}
87+
class="flex-1 text-xs px-2 py-1.5 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
88+
on:input={handleLimitChange}
89+
/>
90+
</div>
91+
<div class="flex items-center gap-2">
92+
<label class="text-xs w-16 shrink-0 font-medium dark:text-gray-300">Optimal:</label>
93+
<input
94+
type="number"
95+
value={item.optimal_amount}
96+
class="flex-1 text-xs px-2 py-1.5 border border-gray-300 dark:border-gray-500 rounded bg-white dark:bg-gray-700 dark:text-white focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-800 outline-none transition-all"
97+
on:input={handleOptimalChange}
98+
/>
99+
</div>
90100
</div>
91101
</div>
92102
{/if}

frontend/src/routes/admin/produits/+page.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
reloadItems();
6969
}
7070
};
71-
let itemsPerPage = 7;
71+
let itemsPerPage = 6;
7272
7373
let searchState: ItemState | undefined = undefined;
7474
let searchCategory: string | undefined = undefined;
@@ -739,7 +739,7 @@
739739
Achetable
740740
</th>
741741
<th class="px-6 py-4 text-left text-sm font-semibold text-gray-900 dark:text-gray-300 border-r border-gray-200 dark:border-gray-600">
742-
Stock / Limite / Optimal
742+
Stocks
743743
</th>
744744
<th class="px-6 py-4 text-left text-sm font-semibold text-gray-900 dark:text-gray-300 border-r border-gray-200 dark:border-gray-600">
745745
Fournisseur

0 commit comments

Comments
 (0)