includio-cms 0.14.6 → 0.15.0
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.
- package/CHANGELOG.md +25 -0
- package/DOCS.md +45 -1
- package/ROADMAP.md +23 -2
- package/dist/admin/auth-client.d.ts +42 -42
- package/dist/admin/client/entry/entry.svelte +1 -0
- package/dist/admin/client/index.d.ts +6 -0
- package/dist/admin/client/index.js +6 -0
- package/dist/admin/client/shop/shipping-method-edit-page.svelte +113 -0
- package/dist/admin/client/shop/shipping-method-edit-page.svelte.d.ts +3 -0
- package/dist/admin/client/shop/shipping-method-form.svelte +244 -0
- package/dist/admin/client/shop/shipping-method-form.svelte.d.ts +37 -0
- package/dist/admin/client/shop/shipping-method-new-page.svelte +47 -0
- package/dist/admin/client/shop/shipping-method-new-page.svelte.d.ts +3 -0
- package/dist/admin/client/shop/shipping-methods-list-page.svelte +172 -0
- package/dist/admin/client/shop/shipping-methods-list-page.svelte.d.ts +3 -0
- package/dist/admin/client/shop/shop-order-detail-page.svelte +332 -0
- package/dist/admin/client/shop/shop-order-detail-page.svelte.d.ts +3 -0
- package/dist/admin/client/shop/shop-orders-list-page.svelte +150 -0
- package/dist/admin/client/shop/shop-orders-list-page.svelte.d.ts +3 -0
- package/dist/admin/client/shop/shop-products-list-page.svelte +157 -0
- package/dist/admin/client/shop/shop-products-list-page.svelte.d.ts +3 -0
- package/dist/admin/components/fields/field-renderer.svelte +4 -2
- package/dist/admin/components/fields/shop-field.svelte +298 -0
- package/dist/admin/components/fields/shop-field.svelte.d.ts +7 -0
- package/dist/admin/components/layout/app-sidebar.svelte +2 -0
- package/dist/admin/components/layout/lang.d.ts +6 -0
- package/dist/admin/components/layout/lang.js +12 -0
- package/dist/admin/components/layout/nav-shop.svelte +55 -0
- package/dist/admin/components/layout/nav-shop.svelte.d.ts +3 -0
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/shop.remote.d.ts +244 -0
- package/dist/admin/remote/shop.remote.js +153 -0
- package/dist/cli/scaffold/admin.js +84 -0
- package/dist/core/cms.d.ts +2 -0
- package/dist/core/cms.js +2 -0
- package/dist/core/fields/fieldSchemaToTs.js +5 -0
- package/dist/core/server/entries/operations/get.js +3 -3
- package/dist/core/server/fields/populateEntry.d.ts +1 -1
- package/dist/core/server/fields/populateEntry.js +3 -1
- package/dist/core/server/generator/fields.js +14 -0
- package/dist/core/server/generator/generator.js +13 -0
- package/dist/db-postgres/schema/index.d.ts +1 -0
- package/dist/db-postgres/schema/index.js +1 -0
- package/dist/db-postgres/schema/shop/index.d.ts +8 -0
- package/dist/db-postgres/schema/shop/index.js +8 -0
- package/dist/db-postgres/schema/shop/order.d.ts +396 -0
- package/dist/db-postgres/schema/shop/order.js +28 -0
- package/dist/db-postgres/schema/shop/orderItem.d.ts +179 -0
- package/dist/db-postgres/schema/shop/orderItem.js +20 -0
- package/dist/db-postgres/schema/shop/orderStatusHistory.d.ts +112 -0
- package/dist/db-postgres/schema/shop/orderStatusHistory.js +12 -0
- package/dist/db-postgres/schema/shop/payment.d.ts +180 -0
- package/dist/db-postgres/schema/shop/payment.js +16 -0
- package/dist/db-postgres/schema/shop/product.d.ts +143 -0
- package/dist/db-postgres/schema/shop/product.js +15 -0
- package/dist/db-postgres/schema/shop/productVariant.d.ts +164 -0
- package/dist/db-postgres/schema/shop/productVariant.js +15 -0
- package/dist/db-postgres/schema/shop/shippingMethod.d.ts +190 -0
- package/dist/db-postgres/schema/shop/shippingMethod.js +13 -0
- package/dist/db-postgres/schema/shop/stockReservation.d.ts +109 -0
- package/dist/db-postgres/schema/shop/stockReservation.js +13 -0
- package/dist/db-postgres/schema-core.d.ts +9 -0
- package/dist/db-postgres/schema-core.js +9 -0
- package/dist/db-postgres/schema-shop.d.ts +1 -0
- package/dist/db-postgres/schema-shop.js +1 -0
- package/dist/email-nodemailer/index.d.ts +2 -9
- package/dist/shop/adapters/manual/index.d.ts +10 -0
- package/dist/shop/adapters/manual/index.js +16 -0
- package/dist/shop/cart/cookie.d.ts +8 -0
- package/dist/shop/cart/cookie.js +84 -0
- package/dist/shop/cart/types.d.ts +42 -0
- package/dist/shop/cart/types.js +1 -0
- package/dist/shop/client/index.d.ts +59 -0
- package/dist/shop/client/index.js +40 -0
- package/dist/shop/http/cart-handler.d.ts +7 -0
- package/dist/shop/http/cart-handler.js +88 -0
- package/dist/shop/http/checkout-handler.d.ts +4 -0
- package/dist/shop/http/checkout-handler.js +100 -0
- package/dist/shop/http/index.d.ts +3 -0
- package/dist/shop/http/index.js +3 -0
- package/dist/shop/http/shipping-handler.d.ts +4 -0
- package/dist/shop/http/shipping-handler.js +31 -0
- package/dist/shop/index.d.ts +4 -0
- package/dist/shop/index.js +17 -0
- package/dist/shop/pricing.d.ts +15 -0
- package/dist/shop/pricing.js +31 -0
- package/dist/shop/rate-limit.d.ts +9 -0
- package/dist/shop/rate-limit.js +28 -0
- package/dist/shop/server/cart-hydrate.d.ts +4 -0
- package/dist/shop/server/cart-hydrate.js +172 -0
- package/dist/shop/server/db.d.ts +4 -0
- package/dist/shop/server/db.js +16 -0
- package/dist/shop/server/email.d.ts +2 -0
- package/dist/shop/server/email.js +138 -0
- package/dist/shop/server/order-number.d.ts +5 -0
- package/dist/shop/server/order-number.js +15 -0
- package/dist/shop/server/orders.d.ts +45 -0
- package/dist/shop/server/orders.js +293 -0
- package/dist/shop/server/populate.d.ts +15 -0
- package/dist/shop/server/populate.js +39 -0
- package/dist/shop/server/shipping.d.ts +37 -0
- package/dist/shop/server/shipping.js +111 -0
- package/dist/shop/server/shop-data.d.ts +51 -0
- package/dist/shop/server/shop-data.js +186 -0
- package/dist/shop/services/cart.service.d.ts +38 -0
- package/dist/shop/services/cart.service.js +1 -0
- package/dist/shop/services/email.service.d.ts +6 -0
- package/dist/shop/services/email.service.js +1 -0
- package/dist/shop/services/index.d.ts +6 -0
- package/dist/shop/services/index.js +1 -0
- package/dist/shop/services/orders.service.d.ts +34 -0
- package/dist/shop/services/orders.service.js +1 -0
- package/dist/shop/services/payment.service.d.ts +7 -0
- package/dist/shop/services/payment.service.js +1 -0
- package/dist/shop/services/products.service.d.ts +31 -0
- package/dist/shop/services/products.service.js +1 -0
- package/dist/shop/services/shipping.service.d.ts +23 -0
- package/dist/shop/services/shipping.service.js +1 -0
- package/dist/shop/types.d.ts +72 -0
- package/dist/shop/types.js +1 -0
- package/dist/types/cms.d.ts +3 -0
- package/dist/types/fields.d.ts +18 -2
- package/dist/updates/0.15.0/index.d.ts +2 -0
- package/dist/updates/0.15.0/index.js +25 -0
- package/dist/updates/index.js +2 -1
- package/package.json +27 -1
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import { Button } from '../../../components/ui/button/index.js';
|
|
4
|
+
import { Switch } from '../../../components/ui/switch/index.js';
|
|
5
|
+
import TrashIcon from '@tabler/icons-svelte/icons/trash';
|
|
6
|
+
import PlusIcon from '@tabler/icons-svelte/icons/plus';
|
|
7
|
+
import BuildingStoreIcon from '@tabler/icons-svelte/icons/building-store';
|
|
8
|
+
import { getRemotes } from '../../../sveltekit/index.js';
|
|
9
|
+
import type { ShopField } from '../../../types/fields.js';
|
|
10
|
+
|
|
11
|
+
type Props = { field: ShopField };
|
|
12
|
+
const { field }: Props = $props();
|
|
13
|
+
|
|
14
|
+
const entryId = getContext<string>('cms-entry-id');
|
|
15
|
+
const remotes = getRemotes();
|
|
16
|
+
const dataQuery = $derived(remotes.getShopDataForEntry(entryId));
|
|
17
|
+
const configQuery = $derived(remotes.getShopConfig());
|
|
18
|
+
|
|
19
|
+
type InputMode = 'net' | 'gross';
|
|
20
|
+
let inputMode = $state<InputMode>('gross');
|
|
21
|
+
let inputPrice = $state('0.00');
|
|
22
|
+
let vatRate = $state<number | string>(23);
|
|
23
|
+
let isActive = $state(true);
|
|
24
|
+
let variants = $state<
|
|
25
|
+
Array<{
|
|
26
|
+
id?: string;
|
|
27
|
+
sku: string;
|
|
28
|
+
name: string;
|
|
29
|
+
priceDelta: string;
|
|
30
|
+
stock: string;
|
|
31
|
+
}>
|
|
32
|
+
>([]);
|
|
33
|
+
|
|
34
|
+
let loaded = $state(false);
|
|
35
|
+
let saving = $state(false);
|
|
36
|
+
let errorMessage = $state<string | null>(null);
|
|
37
|
+
let successMessage = $state<string | null>(null);
|
|
38
|
+
|
|
39
|
+
const inputPriceCents = $derived(Math.round(parseFloat(inputPrice || '0') * 100));
|
|
40
|
+
const vat = $derived(Number(vatRate) || 0);
|
|
41
|
+
const netCents = $derived(
|
|
42
|
+
inputMode === 'net' ? inputPriceCents : Math.round(inputPriceCents / (1 + vat / 100))
|
|
43
|
+
);
|
|
44
|
+
const grossCents = $derived(
|
|
45
|
+
inputMode === 'gross' ? inputPriceCents : Math.round(inputPriceCents * (1 + vat / 100))
|
|
46
|
+
);
|
|
47
|
+
const vatCents = $derived(grossCents - netCents);
|
|
48
|
+
|
|
49
|
+
function formatCents(cents: number) {
|
|
50
|
+
return (cents / 100).toFixed(2);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function switchMode(newMode: InputMode) {
|
|
54
|
+
if (newMode === inputMode) return;
|
|
55
|
+
// Preserve economic value — if user typed gross, switch shows net (computed) in the input, and vice versa.
|
|
56
|
+
const preservedCents = newMode === 'net' ? netCents : grossCents;
|
|
57
|
+
inputMode = newMode;
|
|
58
|
+
inputPrice = formatCents(preservedCents);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
$effect(() => {
|
|
62
|
+
if (!loaded && dataQuery.ready && configQuery.ready) {
|
|
63
|
+
const shop = dataQuery.current;
|
|
64
|
+
if (shop) {
|
|
65
|
+
// Shop storage is always net — default UI to net when hydrating existing data.
|
|
66
|
+
inputMode = 'net';
|
|
67
|
+
inputPrice = (shop.basePrice / 100).toFixed(2);
|
|
68
|
+
vatRate = shop.vatRate;
|
|
69
|
+
isActive = shop.isActive;
|
|
70
|
+
variants = shop.variants.map((v) => ({
|
|
71
|
+
id: v.id,
|
|
72
|
+
sku: v.sku ?? '',
|
|
73
|
+
name:
|
|
74
|
+
typeof v.name === 'object' && v.name !== null
|
|
75
|
+
? String(
|
|
76
|
+
Object.values(v.name as Record<string, string>)[0] ?? ''
|
|
77
|
+
)
|
|
78
|
+
: '',
|
|
79
|
+
priceDelta: (v.priceDelta / 100).toFixed(2),
|
|
80
|
+
stock: v.stock == null ? '' : String(v.stock)
|
|
81
|
+
}));
|
|
82
|
+
} else {
|
|
83
|
+
const defaultVat = configQuery.current?.vatRates[0] ?? 23;
|
|
84
|
+
vatRate = defaultVat;
|
|
85
|
+
}
|
|
86
|
+
loaded = true;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
function addVariant() {
|
|
91
|
+
variants.push({
|
|
92
|
+
id: undefined,
|
|
93
|
+
sku: '',
|
|
94
|
+
name: '',
|
|
95
|
+
priceDelta: '0.00',
|
|
96
|
+
stock: ''
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function removeVariant(i: number) {
|
|
101
|
+
variants.splice(i, 1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function save() {
|
|
105
|
+
if (!entryId) {
|
|
106
|
+
errorMessage = 'Brak ID wpisu — zapisz najpierw wpis, potem dane sklepu.';
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
saving = true;
|
|
110
|
+
errorMessage = null;
|
|
111
|
+
successMessage = null;
|
|
112
|
+
try {
|
|
113
|
+
const stockEnabled = configQuery.current?.features.stock ?? false;
|
|
114
|
+
const variantsEnabled = configQuery.current?.features.variants ?? false;
|
|
115
|
+
await remotes.upsertShopDataForEntry({
|
|
116
|
+
entryId,
|
|
117
|
+
data: {
|
|
118
|
+
basePrice: netCents,
|
|
119
|
+
vatRate: Number(vatRate),
|
|
120
|
+
isActive
|
|
121
|
+
},
|
|
122
|
+
variants: variantsEnabled
|
|
123
|
+
? variants.map((v) => ({
|
|
124
|
+
id: v.id,
|
|
125
|
+
sku: v.sku || null,
|
|
126
|
+
name: v.name ? { pl: v.name } : null,
|
|
127
|
+
priceDelta: Math.round(parseFloat(v.priceDelta || '0') * 100),
|
|
128
|
+
stock: stockEnabled && v.stock !== '' ? parseInt(v.stock, 10) : null,
|
|
129
|
+
attributes: null
|
|
130
|
+
}))
|
|
131
|
+
: []
|
|
132
|
+
});
|
|
133
|
+
await dataQuery.refresh();
|
|
134
|
+
successMessage = 'Zapisano dane sklepu.';
|
|
135
|
+
} catch (err) {
|
|
136
|
+
errorMessage = err instanceof Error ? err.message : 'Nie udało się zapisać';
|
|
137
|
+
} finally {
|
|
138
|
+
saving = false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<section class="border-border bg-card space-y-4 rounded-xl border p-5">
|
|
144
|
+
<header class="flex items-center justify-between">
|
|
145
|
+
<div class="flex items-center gap-2">
|
|
146
|
+
<BuildingStoreIcon class="text-primary size-5" />
|
|
147
|
+
<h3 class="text-base font-bold">Sklep</h3>
|
|
148
|
+
</div>
|
|
149
|
+
<label class="flex items-center gap-2 text-sm">
|
|
150
|
+
<Switch bind:checked={isActive} />
|
|
151
|
+
<span>Aktywny</span>
|
|
152
|
+
</label>
|
|
153
|
+
</header>
|
|
154
|
+
|
|
155
|
+
{#if !configQuery.ready || !dataQuery.ready}
|
|
156
|
+
<p class="text-muted-foreground text-sm">Ładowanie…</p>
|
|
157
|
+
{:else if !configQuery.current}
|
|
158
|
+
<p class="text-muted-foreground text-sm">Sklep nie jest skonfigurowany.</p>
|
|
159
|
+
{:else}
|
|
160
|
+
{@const config = configQuery.current}
|
|
161
|
+
|
|
162
|
+
<div class="space-y-3">
|
|
163
|
+
<div class="grid grid-cols-[1fr_auto] gap-3">
|
|
164
|
+
<label class="block">
|
|
165
|
+
<div class="mb-1 flex items-center justify-between">
|
|
166
|
+
<span class="text-muted-foreground text-xs font-semibold">
|
|
167
|
+
Cena bazowa ({inputMode === 'net' ? 'netto' : 'brutto'}, PLN)
|
|
168
|
+
</span>
|
|
169
|
+
<div class="bg-muted inline-flex rounded-md p-0.5 text-xs">
|
|
170
|
+
<button
|
|
171
|
+
type="button"
|
|
172
|
+
class="rounded px-2 py-0.5 {inputMode === 'net'
|
|
173
|
+
? 'bg-background text-primary font-semibold shadow-sm'
|
|
174
|
+
: 'text-muted-foreground'}"
|
|
175
|
+
onclick={() => switchMode('net')}
|
|
176
|
+
>
|
|
177
|
+
Netto
|
|
178
|
+
</button>
|
|
179
|
+
<button
|
|
180
|
+
type="button"
|
|
181
|
+
class="rounded px-2 py-0.5 {inputMode === 'gross'
|
|
182
|
+
? 'bg-background text-primary font-semibold shadow-sm'
|
|
183
|
+
: 'text-muted-foreground'}"
|
|
184
|
+
onclick={() => switchMode('gross')}
|
|
185
|
+
>
|
|
186
|
+
Brutto
|
|
187
|
+
</button>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
<input
|
|
191
|
+
type="number"
|
|
192
|
+
step="0.01"
|
|
193
|
+
min="0"
|
|
194
|
+
bind:value={inputPrice}
|
|
195
|
+
class="border-border w-full rounded-lg border px-3 py-2"
|
|
196
|
+
/>
|
|
197
|
+
</label>
|
|
198
|
+
<label class="block">
|
|
199
|
+
<span class="text-muted-foreground mb-1 block text-xs font-semibold">VAT</span>
|
|
200
|
+
<select
|
|
201
|
+
bind:value={vatRate}
|
|
202
|
+
class="border-border w-full rounded-lg border px-3 py-2"
|
|
203
|
+
>
|
|
204
|
+
{#each config.vatRates as r (r)}
|
|
205
|
+
<option value={r}>{r}%</option>
|
|
206
|
+
{/each}
|
|
207
|
+
</select>
|
|
208
|
+
</label>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="bg-muted/40 border-border grid grid-cols-3 gap-2 rounded-lg border p-2.5 text-center text-xs">
|
|
211
|
+
<div>
|
|
212
|
+
<div class="text-muted-foreground font-semibold uppercase tracking-wide">Netto</div>
|
|
213
|
+
<div class="text-sm font-bold tabular-nums">{formatCents(netCents)} zł</div>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="border-border border-x">
|
|
216
|
+
<div class="text-muted-foreground font-semibold uppercase tracking-wide">VAT</div>
|
|
217
|
+
<div class="text-sm font-bold tabular-nums">{formatCents(vatCents)} zł</div>
|
|
218
|
+
</div>
|
|
219
|
+
<div>
|
|
220
|
+
<div class="text-muted-foreground font-semibold uppercase tracking-wide">Brutto</div>
|
|
221
|
+
<div class="text-primary text-sm font-bold tabular-nums">{formatCents(grossCents)} zł</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{#if config.features.variants}
|
|
227
|
+
<div class="border-border space-y-3 rounded-lg border-t pt-4">
|
|
228
|
+
<div class="flex items-center justify-between">
|
|
229
|
+
<h4 class="text-sm font-bold">Warianty</h4>
|
|
230
|
+
<Button type="button" variant="outline" size="sm" onclick={addVariant}>
|
|
231
|
+
<PlusIcon class="mr-1 size-4" /> Dodaj wariant
|
|
232
|
+
</Button>
|
|
233
|
+
</div>
|
|
234
|
+
{#if variants.length === 0}
|
|
235
|
+
<p class="text-muted-foreground text-xs">Brak wariantów — zostanie użyta cena bazowa.</p>
|
|
236
|
+
{/if}
|
|
237
|
+
{#each variants as v, i (i)}
|
|
238
|
+
<div class="border-border grid grid-cols-2 gap-2 rounded-lg border p-3">
|
|
239
|
+
<label class="block">
|
|
240
|
+
<span class="text-muted-foreground mb-0.5 block text-xs">Nazwa</span>
|
|
241
|
+
<input
|
|
242
|
+
type="text"
|
|
243
|
+
bind:value={v.name}
|
|
244
|
+
class="border-border w-full rounded border px-2 py-1 text-sm"
|
|
245
|
+
/>
|
|
246
|
+
</label>
|
|
247
|
+
<label class="block">
|
|
248
|
+
<span class="text-muted-foreground mb-0.5 block text-xs">SKU</span>
|
|
249
|
+
<input
|
|
250
|
+
type="text"
|
|
251
|
+
bind:value={v.sku}
|
|
252
|
+
class="border-border w-full rounded border px-2 py-1 font-mono text-sm"
|
|
253
|
+
/>
|
|
254
|
+
</label>
|
|
255
|
+
<label class="block">
|
|
256
|
+
<span class="text-muted-foreground mb-0.5 block text-xs">Zmiana ceny (PLN)</span>
|
|
257
|
+
<input
|
|
258
|
+
type="number"
|
|
259
|
+
step="0.01"
|
|
260
|
+
bind:value={v.priceDelta}
|
|
261
|
+
class="border-border w-full rounded border px-2 py-1 text-sm"
|
|
262
|
+
/>
|
|
263
|
+
</label>
|
|
264
|
+
{#if config.features.stock}
|
|
265
|
+
<label class="block">
|
|
266
|
+
<span class="text-muted-foreground mb-0.5 block text-xs">Stock (puste = ∞)</span>
|
|
267
|
+
<input
|
|
268
|
+
type="number"
|
|
269
|
+
min="0"
|
|
270
|
+
bind:value={v.stock}
|
|
271
|
+
class="border-border w-full rounded border px-2 py-1 text-sm"
|
|
272
|
+
/>
|
|
273
|
+
</label>
|
|
274
|
+
{/if}
|
|
275
|
+
<div class="col-span-2 flex justify-end">
|
|
276
|
+
<Button type="button" variant="ghost" size="sm" onclick={() => removeVariant(i)}>
|
|
277
|
+
<TrashIcon class="size-4" />
|
|
278
|
+
</Button>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
{/each}
|
|
282
|
+
</div>
|
|
283
|
+
{/if}
|
|
284
|
+
|
|
285
|
+
{#if errorMessage}
|
|
286
|
+
<div class="rounded-lg bg-red-50 p-2.5 text-xs text-red-800">{errorMessage}</div>
|
|
287
|
+
{/if}
|
|
288
|
+
{#if successMessage}
|
|
289
|
+
<div class="rounded-lg bg-green-50 p-2.5 text-xs text-green-800">{successMessage}</div>
|
|
290
|
+
{/if}
|
|
291
|
+
|
|
292
|
+
<div class="flex justify-end">
|
|
293
|
+
<Button type="button" onclick={save} disabled={saving}>
|
|
294
|
+
{saving ? 'Zapisywanie…' : 'Zapisz dane sklepu'}
|
|
295
|
+
</Button>
|
|
296
|
+
</div>
|
|
297
|
+
{/if}
|
|
298
|
+
</section>
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import NavCollections from './nav-collections.svelte';
|
|
7
7
|
import NavFooter from './nav-footer.svelte';
|
|
8
8
|
import NavForms from './nav-forms.svelte';
|
|
9
|
+
import NavShop from './nav-shop.svelte';
|
|
9
10
|
import NavSearch from './nav-search.svelte';
|
|
10
11
|
import { resolve } from '$app/paths';
|
|
11
12
|
let { ...restProps }: ComponentProps<typeof Sidebar.Root> = $props();
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
<NavSingletons />
|
|
35
36
|
<NavCollections />
|
|
36
37
|
<NavForms />
|
|
38
|
+
<NavShop />
|
|
37
39
|
</div>
|
|
38
40
|
</Sidebar.Content>
|
|
39
41
|
<Sidebar.Footer class="p-0">
|
|
@@ -20,6 +20,12 @@ export const sidebarLang = {
|
|
|
20
20
|
forms: {
|
|
21
21
|
title: 'Formularze'
|
|
22
22
|
},
|
|
23
|
+
shop: {
|
|
24
|
+
title: 'Sklep',
|
|
25
|
+
products: 'Produkty',
|
|
26
|
+
orders: 'Zamówienia',
|
|
27
|
+
shipping: 'Metody wysyłki'
|
|
28
|
+
},
|
|
23
29
|
footer: {
|
|
24
30
|
help: 'Pomoc'
|
|
25
31
|
},
|
|
@@ -53,6 +59,12 @@ export const sidebarLang = {
|
|
|
53
59
|
forms: {
|
|
54
60
|
title: 'Forms'
|
|
55
61
|
},
|
|
62
|
+
shop: {
|
|
63
|
+
title: 'Shop',
|
|
64
|
+
products: 'Products',
|
|
65
|
+
orders: 'Orders',
|
|
66
|
+
shipping: 'Shipping methods'
|
|
67
|
+
},
|
|
56
68
|
footer: {
|
|
57
69
|
help: 'Help'
|
|
58
70
|
},
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
3
|
+
import * as Sidebar from '../../../components/ui/sidebar/index.js';
|
|
4
|
+
import { sidebarLang } from './lang.js';
|
|
5
|
+
import { getRemotes } from '../../../sveltekit/index.js';
|
|
6
|
+
import PackageIcon from '@tabler/icons-svelte/icons/package';
|
|
7
|
+
import ShoppingCartIcon from '@tabler/icons-svelte/icons/shopping-cart';
|
|
8
|
+
import TruckIcon from '@tabler/icons-svelte/icons/truck';
|
|
9
|
+
import BuildingStoreIcon from '@tabler/icons-svelte/icons/building-store';
|
|
10
|
+
import { page } from '$app/state';
|
|
11
|
+
|
|
12
|
+
const interfaceLanguage = useInterfaceLanguage();
|
|
13
|
+
const lang = $derived(sidebarLang[interfaceLanguage.current].shop);
|
|
14
|
+
const remotes = getRemotes();
|
|
15
|
+
const enabledQuery = $derived(remotes.getShopEnabled());
|
|
16
|
+
|
|
17
|
+
function isActive(url: string) {
|
|
18
|
+
const pathname = page.url.pathname;
|
|
19
|
+
return pathname === url || pathname.startsWith(url + '/');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const items = $derived([
|
|
23
|
+
{ title: lang.products, url: '/admin/shop/products', icon: PackageIcon },
|
|
24
|
+
{ title: lang.orders, url: '/admin/shop/orders', icon: ShoppingCartIcon },
|
|
25
|
+
{ title: lang.shipping, url: '/admin/shop/shipping-methods', icon: TruckIcon }
|
|
26
|
+
]);
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
{#if enabledQuery.ready && enabledQuery.current}
|
|
30
|
+
<Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
|
|
31
|
+
<Sidebar.GroupLabel class="group-data-[collapsible=icon]:hidden">
|
|
32
|
+
<BuildingStoreIcon class="mr-1 size-3.5" />
|
|
33
|
+
{lang.title}
|
|
34
|
+
</Sidebar.GroupLabel>
|
|
35
|
+
<Sidebar.Menu>
|
|
36
|
+
{#each items as item (item.url)}
|
|
37
|
+
{@const active = isActive(item.url)}
|
|
38
|
+
<Sidebar.MenuItem>
|
|
39
|
+
<Sidebar.MenuButton isActive={active} tooltipContent={item.title}>
|
|
40
|
+
{#snippet child({ props })}
|
|
41
|
+
<a {...props} href={item.url}>
|
|
42
|
+
<item.icon
|
|
43
|
+
class="size-5! shrink-0 {active ? 'text-primary' : 'text-muted-foreground'}"
|
|
44
|
+
/>
|
|
45
|
+
<span class="flex-1 truncate {active ? 'text-primary' : 'text-muted-foreground'}">
|
|
46
|
+
{item.title}
|
|
47
|
+
</span>
|
|
48
|
+
</a>
|
|
49
|
+
{/snippet}
|
|
50
|
+
</Sidebar.MenuButton>
|
|
51
|
+
</Sidebar.MenuItem>
|
|
52
|
+
{/each}
|
|
53
|
+
</Sidebar.Menu>
|
|
54
|
+
</Sidebar.Group>
|
|
55
|
+
{/if}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
export declare const getShopEnabled: import("@sveltejs/kit").RemoteQueryFunction<void, boolean>;
|
|
2
|
+
export declare const getShopConfig: import("@sveltejs/kit").RemoteQueryFunction<void, {
|
|
3
|
+
currency: "PLN";
|
|
4
|
+
vatRates: number[];
|
|
5
|
+
features: Required<import("../../shop/types.js").ShopFeatures>;
|
|
6
|
+
languages: import("../../types/languages.js").Language[];
|
|
7
|
+
} | null>;
|
|
8
|
+
export declare const listShopProductEntries: import("@sveltejs/kit").RemoteQueryFunction<void, import("../../shop/server/shop-data.js").ShopEntryListItem[]>;
|
|
9
|
+
export declare const getShopDataForEntry: import("@sveltejs/kit").RemoteQueryFunction<string, import("../../shop/server/shop-data.js").ShopDataWithVariants | null>;
|
|
10
|
+
export declare const upsertShopDataForEntry: import("@sveltejs/kit").RemoteCommand<{
|
|
11
|
+
entryId: string;
|
|
12
|
+
data: {
|
|
13
|
+
basePrice: number;
|
|
14
|
+
vatRate: number;
|
|
15
|
+
isActive?: boolean | undefined;
|
|
16
|
+
sortOrder?: number | null | undefined;
|
|
17
|
+
};
|
|
18
|
+
variants?: {
|
|
19
|
+
id?: string | undefined;
|
|
20
|
+
sku?: string | null | undefined;
|
|
21
|
+
name?: Record<string, string> | null | undefined;
|
|
22
|
+
priceDelta?: number | undefined;
|
|
23
|
+
stock?: number | null | undefined;
|
|
24
|
+
attributes?: Record<string, string> | null | undefined;
|
|
25
|
+
}[] | undefined;
|
|
26
|
+
}, Promise<import("../../shop/server/shop-data.js").ShopDataWithVariants>>;
|
|
27
|
+
export declare const deleteShopDataForEntry: import("@sveltejs/kit").RemoteCommand<string, Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
}>>;
|
|
30
|
+
export declare const listShippingMethodsAdmin: import("@sveltejs/kit").RemoteQueryFunction<void, {
|
|
31
|
+
id: string;
|
|
32
|
+
name: Record<string, string>;
|
|
33
|
+
createdAt: Date;
|
|
34
|
+
sortOrder: number | null;
|
|
35
|
+
description: Record<string, string> | null;
|
|
36
|
+
vatRate: number;
|
|
37
|
+
isActive: boolean;
|
|
38
|
+
price: number;
|
|
39
|
+
carrierType: string;
|
|
40
|
+
conditions: {
|
|
41
|
+
freeAbove?: number;
|
|
42
|
+
} | null;
|
|
43
|
+
}[]>;
|
|
44
|
+
export declare const getShippingMethodForAdmin: import("@sveltejs/kit").RemoteQueryFunction<string, {
|
|
45
|
+
id: string;
|
|
46
|
+
name: Record<string, string>;
|
|
47
|
+
createdAt: Date;
|
|
48
|
+
sortOrder: number | null;
|
|
49
|
+
description: Record<string, string> | null;
|
|
50
|
+
vatRate: number;
|
|
51
|
+
isActive: boolean;
|
|
52
|
+
price: number;
|
|
53
|
+
carrierType: string;
|
|
54
|
+
conditions: {
|
|
55
|
+
freeAbove?: number;
|
|
56
|
+
} | null;
|
|
57
|
+
} | null>;
|
|
58
|
+
export declare const createShippingMethodCmd: import("@sveltejs/kit").RemoteCommand<{
|
|
59
|
+
name: Record<string, string>;
|
|
60
|
+
price: number;
|
|
61
|
+
vatRate: number;
|
|
62
|
+
description?: Record<string, string> | null | undefined;
|
|
63
|
+
carrierType?: string | undefined;
|
|
64
|
+
conditions?: {
|
|
65
|
+
freeAbove?: number | undefined;
|
|
66
|
+
} | null | undefined;
|
|
67
|
+
isActive?: boolean | undefined;
|
|
68
|
+
sortOrder?: number | null | undefined;
|
|
69
|
+
}, Promise<{
|
|
70
|
+
id: string;
|
|
71
|
+
name: Record<string, string>;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
sortOrder: number | null;
|
|
74
|
+
description: Record<string, string> | null;
|
|
75
|
+
vatRate: number;
|
|
76
|
+
isActive: boolean;
|
|
77
|
+
price: number;
|
|
78
|
+
carrierType: string;
|
|
79
|
+
conditions: {
|
|
80
|
+
freeAbove?: number;
|
|
81
|
+
} | null;
|
|
82
|
+
}>>;
|
|
83
|
+
export declare const updateShippingMethodCmd: import("@sveltejs/kit").RemoteCommand<{
|
|
84
|
+
id: string;
|
|
85
|
+
input: {
|
|
86
|
+
name?: Record<string, string> | undefined;
|
|
87
|
+
description?: Record<string, string> | null | undefined;
|
|
88
|
+
price?: number | undefined;
|
|
89
|
+
vatRate?: number | undefined;
|
|
90
|
+
carrierType?: string | undefined;
|
|
91
|
+
conditions?: {
|
|
92
|
+
freeAbove?: number | undefined;
|
|
93
|
+
} | null | undefined;
|
|
94
|
+
isActive?: boolean | undefined;
|
|
95
|
+
sortOrder?: number | null | undefined;
|
|
96
|
+
};
|
|
97
|
+
}, Promise<{
|
|
98
|
+
id: string;
|
|
99
|
+
name: Record<string, string>;
|
|
100
|
+
createdAt: Date;
|
|
101
|
+
sortOrder: number | null;
|
|
102
|
+
description: Record<string, string> | null;
|
|
103
|
+
vatRate: number;
|
|
104
|
+
isActive: boolean;
|
|
105
|
+
price: number;
|
|
106
|
+
carrierType: string;
|
|
107
|
+
conditions: {
|
|
108
|
+
freeAbove?: number;
|
|
109
|
+
} | null;
|
|
110
|
+
}>>;
|
|
111
|
+
export declare const deleteShippingMethodCmd: import("@sveltejs/kit").RemoteCommand<string, Promise<{
|
|
112
|
+
success: boolean;
|
|
113
|
+
}>>;
|
|
114
|
+
export declare const reorderShippingMethodsCmd: import("@sveltejs/kit").RemoteCommand<string[], Promise<{
|
|
115
|
+
success: boolean;
|
|
116
|
+
}>>;
|
|
117
|
+
export declare const listOrdersAdmin: import("@sveltejs/kit").RemoteQueryFunction<{
|
|
118
|
+
status?: "done" | "new" | "awaitingPayment" | "paid" | "preparing" | "sent" | "cancelled" | "paymentRejected" | undefined;
|
|
119
|
+
email?: string | undefined;
|
|
120
|
+
limit?: number | undefined;
|
|
121
|
+
offset?: number | undefined;
|
|
122
|
+
} | undefined, {
|
|
123
|
+
number: string;
|
|
124
|
+
id: string;
|
|
125
|
+
status: import("../../shop/types.js").OrderStatus;
|
|
126
|
+
createdAt: Date;
|
|
127
|
+
updatedAt: Date;
|
|
128
|
+
language: string | null;
|
|
129
|
+
consents: {
|
|
130
|
+
id: string;
|
|
131
|
+
accepted: boolean;
|
|
132
|
+
label: string;
|
|
133
|
+
}[] | null;
|
|
134
|
+
carrierType: string | null;
|
|
135
|
+
currency: string;
|
|
136
|
+
customerEmail: string;
|
|
137
|
+
customerName: string | null;
|
|
138
|
+
customerPhone: string | null;
|
|
139
|
+
shippingAddress: Record<string, string> | null;
|
|
140
|
+
totalNet: number;
|
|
141
|
+
totalGross: number;
|
|
142
|
+
vatAmount: number;
|
|
143
|
+
shippingNet: number;
|
|
144
|
+
shippingGross: number;
|
|
145
|
+
shippingMethodId: string | null;
|
|
146
|
+
carrierRef: string | null;
|
|
147
|
+
paymentMethod: string | null;
|
|
148
|
+
notes: string | null;
|
|
149
|
+
}[]>;
|
|
150
|
+
export declare const getOrderForAdmin: import("@sveltejs/kit").RemoteQueryFunction<string, {
|
|
151
|
+
order: {
|
|
152
|
+
number: string;
|
|
153
|
+
id: string;
|
|
154
|
+
status: import("../../shop/types.js").OrderStatus;
|
|
155
|
+
createdAt: Date;
|
|
156
|
+
updatedAt: Date;
|
|
157
|
+
language: string | null;
|
|
158
|
+
consents: {
|
|
159
|
+
id: string;
|
|
160
|
+
accepted: boolean;
|
|
161
|
+
label: string;
|
|
162
|
+
}[] | null;
|
|
163
|
+
carrierType: string | null;
|
|
164
|
+
currency: string;
|
|
165
|
+
customerEmail: string;
|
|
166
|
+
customerName: string | null;
|
|
167
|
+
customerPhone: string | null;
|
|
168
|
+
shippingAddress: Record<string, string> | null;
|
|
169
|
+
totalNet: number;
|
|
170
|
+
totalGross: number;
|
|
171
|
+
vatAmount: number;
|
|
172
|
+
shippingNet: number;
|
|
173
|
+
shippingGross: number;
|
|
174
|
+
shippingMethodId: string | null;
|
|
175
|
+
carrierRef: string | null;
|
|
176
|
+
paymentMethod: string | null;
|
|
177
|
+
notes: string | null;
|
|
178
|
+
};
|
|
179
|
+
items: {
|
|
180
|
+
id: string;
|
|
181
|
+
vatRate: number;
|
|
182
|
+
productId: string | null;
|
|
183
|
+
orderId: string;
|
|
184
|
+
variantId: string | null;
|
|
185
|
+
nameSnapshot: Record<string, string>;
|
|
186
|
+
skuSnapshot: string | null;
|
|
187
|
+
priceNetSnapshot: number;
|
|
188
|
+
priceGrossSnapshot: number;
|
|
189
|
+
qty: number;
|
|
190
|
+
}[];
|
|
191
|
+
history: {
|
|
192
|
+
id: string;
|
|
193
|
+
note: string | null;
|
|
194
|
+
status: import("../../shop/types.js").OrderStatus;
|
|
195
|
+
orderId: string;
|
|
196
|
+
changedBy: string | null;
|
|
197
|
+
changedAt: Date;
|
|
198
|
+
}[];
|
|
199
|
+
} | null>;
|
|
200
|
+
export declare const updateOrderStatusCmd: import("@sveltejs/kit").RemoteCommand<{
|
|
201
|
+
orderId: string;
|
|
202
|
+
status: "done" | "new" | "awaitingPayment" | "paid" | "preparing" | "sent" | "cancelled" | "paymentRejected";
|
|
203
|
+
note?: string | undefined;
|
|
204
|
+
}, Promise<{
|
|
205
|
+
number: string;
|
|
206
|
+
id: string;
|
|
207
|
+
status: import("../../shop/types.js").OrderStatus;
|
|
208
|
+
createdAt: Date;
|
|
209
|
+
updatedAt: Date;
|
|
210
|
+
language: string | null;
|
|
211
|
+
consents: {
|
|
212
|
+
id: string;
|
|
213
|
+
accepted: boolean;
|
|
214
|
+
label: string;
|
|
215
|
+
}[] | null;
|
|
216
|
+
carrierType: string | null;
|
|
217
|
+
currency: string;
|
|
218
|
+
customerEmail: string;
|
|
219
|
+
customerName: string | null;
|
|
220
|
+
customerPhone: string | null;
|
|
221
|
+
shippingAddress: Record<string, string> | null;
|
|
222
|
+
totalNet: number;
|
|
223
|
+
totalGross: number;
|
|
224
|
+
vatAmount: number;
|
|
225
|
+
shippingNet: number;
|
|
226
|
+
shippingGross: number;
|
|
227
|
+
shippingMethodId: string | null;
|
|
228
|
+
carrierRef: string | null;
|
|
229
|
+
paymentMethod: string | null;
|
|
230
|
+
notes: string | null;
|
|
231
|
+
}>>;
|
|
232
|
+
export declare const resendOrderEmailCmd: import("@sveltejs/kit").RemoteCommand<{
|
|
233
|
+
orderId: string;
|
|
234
|
+
status: "done" | "new" | "awaitingPayment" | "paid" | "preparing" | "sent" | "cancelled" | "paymentRejected";
|
|
235
|
+
}, Promise<{
|
|
236
|
+
success: boolean;
|
|
237
|
+
}>>;
|
|
238
|
+
export declare const listShopableCollections: import("@sveltejs/kit").RemoteQueryFunction<void, {
|
|
239
|
+
slug: string;
|
|
240
|
+
labels: {
|
|
241
|
+
singular?: import("../../types/languages.js").Localized;
|
|
242
|
+
plural?: import("../../types/languages.js").Localized;
|
|
243
|
+
} | undefined;
|
|
244
|
+
}[]>;
|