From c7f78cf2c9bf69af91941b00e310a2a2c6da5e33 Mon Sep 17 00:00:00 2001 From: Martin Berka Date: Thu, 7 May 2026 09:50:51 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20vylep=C5=A1en=C3=AD=20objedn=C3=A1vek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 15 +- .../components/modals/EditGroupFeesModal.tsx | 192 ++++++ .../components/modals/PayForGroupModal.tsx | 182 ++---- client/src/context/socket.js | 1 + client/src/pages/OrderGroupsPage.tsx | 148 ++++- server/src/groups.ts | 14 + server/src/routes/groupRoutes.ts | 28 +- server/src/routes/qrRoutes.ts | 9 +- server/src/websocket.ts | 13 +- types/api.yml | 2 + types/package-lock.json | 594 ++++++++++++++++++ types/paths/groups/updateFees.yml | 34 + types/schemas/_index.yml | 16 + types/yarn.lock | 101 +-- 14 files changed, 1163 insertions(+), 186 deletions(-) create mode 100644 client/src/components/modals/EditGroupFeesModal.tsx create mode 100644 types/package-lock.json create mode 100644 types/paths/groups/updateFees.yml diff --git a/client/src/App.tsx b/client/src/App.tsx index 6117bac..f803895 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,6 +1,6 @@ import React, { useContext, useEffect, useMemo, useRef, useState, useCallback } from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; -import { EVENT_DISCONNECT, EVENT_MESSAGE, SocketContext } from './context/socket'; +import { EVENT_DISCONNECT, EVENT_MESSAGE, EVENT_PENDING_QR, SocketContext } from './context/socket'; import { useAuth } from './context/auth'; import Login from './Login'; import { Alert, Button, Col, Form, Row, Table } from 'react-bootstrap'; @@ -19,7 +19,7 @@ import { getHumanDateTime, isInTheFuture, formatDateString } from './Utils'; import NoteModal from './components/modals/NoteModal'; import PayForAllModal from './components/modals/PayForAllModal'; import { useEasterEgg } from './context/eggs'; -import { ClientData, Food, MealSlot, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, LocationLunchChoicesMap, UserLunchChoice, PizzaVariant, getData, getEasterEggImage, addPizza, removePizza, updatePizzaDayNote, createPizzaDay, deletePizzaDay, lockPizzaDay, unlockPizzaDay, finishOrder, finishDelivery, addChoice, jdemeObed, removeChoices, removeChoice, updateNote, changeDepartureTime, setBuyer, dismissQr, generateQr } from '../../types'; +import { ClientData, Food, MealSlot, PendingQr, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, LocationLunchChoicesMap, UserLunchChoice, PizzaVariant, getData, getEasterEggImage, addPizza, removePizza, updatePizzaDayNote, createPizzaDay, deletePizzaDay, lockPizzaDay, unlockPizzaDay, finishOrder, finishDelivery, addChoice, jdemeObed, removeChoices, removeChoice, updateNote, changeDepartureTime, setBuyer, dismissQr, generateQr } from '../../types'; import { getLunchChoiceName } from './enums'; // import FallingLeaves, { LEAF_PRESETS, LEAF_COLOR_THEMES } from './FallingLeaves'; // import './FallingLeaves.scss'; @@ -132,14 +132,25 @@ function App() { setData(newData); } }); + socket.on(EVENT_PENDING_QR, (pendingQr: PendingQr) => { + setData(prev => prev ? { ...prev, pendingQrs: [...(prev.pendingQrs ?? []), pendingQr] } : prev); + }); return () => { socket.off(EVENT_CONNECT); socket.off(EVENT_DISCONNECT); socket.off(EVENT_MESSAGE); + socket.off(EVENT_PENDING_QR); } }, [socket]); + // Připojení do osobní socket místnosti po přihlášení + useEffect(() => { + if (auth?.login) { + socket.emit('join', auth.login); + } + }, [auth?.login, socket]); + useEffect(() => { if (!auth?.login || !data?.choices) { return diff --git a/client/src/components/modals/EditGroupFeesModal.tsx b/client/src/components/modals/EditGroupFeesModal.tsx new file mode 100644 index 0000000..7bdcf48 --- /dev/null +++ b/client/src/components/modals/EditGroupFeesModal.tsx @@ -0,0 +1,192 @@ +import { useState, useEffect } from "react"; +import { Modal, Button, Form, Table, Alert } from "react-bootstrap"; +import { updateGroupFees, OrderGroup, OrderGroupMember } from "../../../../types"; + +type Props = { + isOpen: boolean; + onClose: () => void; + group: OrderGroup; + onSaved: (data: any) => void; +}; + +function parseNum(s: string): number { + const n = parseFloat(s.replace(',', '.')); + return isNaN(n) || n < 0 ? 0 : Math.round(n * 100) / 100; +} + +function computeMemberTotal(member: OrderGroupMember, feeShare: number, discountType: string, discountValue: number, memberCount: number): number { + const base = member.amount ?? 0; + const surcharge = member.surchargeAmount ?? 0; + const discount = discountType === 'percent' + ? Math.round((base + surcharge) * discountValue / 100 * 100) / 100 + : Math.round(discountValue / memberCount * 100) / 100; + return Math.round((base + surcharge + feeShare - discount) * 100) / 100; +} + +export default function EditGroupFeesModal({ isOpen, onClose, group, onSaved }: Readonly) { + const [fees, setFees] = useState(''); + const [shipping, setShipping] = useState(''); + const [tip, setTip] = useState(''); + const [discountType, setDiscountType] = useState<'percent' | 'fixed'>('percent'); + const [discountValue, setDiscountValue] = useState(''); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (!isOpen) return; + setFees(group.fees ? String(group.fees) : ''); + setShipping(group.shipping ? String(group.shipping) : ''); + setTip(group.tip ? String(group.tip) : ''); + setDiscountType((group.discountType as 'percent' | 'fixed') ?? 'percent'); + setDiscountValue(group.discountValue ? String(group.discountValue) : ''); + setError(null); + }, [isOpen, group]); + + const memberEntries = Object.entries(group.members) as [string, OrderGroupMember][]; + const memberCount = memberEntries.length; + + const feesNum = parseNum(fees); + const shippingNum = parseNum(shipping); + const tipNum = parseNum(tip); + const discountNum = parseNum(discountValue); + const totalFees = feesNum + shippingNum + tipNum; + const feeShare = memberCount > 0 ? Math.round(totalFees / memberCount * 100) / 100 : 0; + + const handleSave = async () => { + setError(null); + setLoading(true); + try { + const body: Record = { id: group.id }; + body.fees = feesNum; + body.shipping = shippingNum; + body.tip = tipNum; + if (discountNum > 0) { + body.discountType = discountType; + body.discountValue = discountNum; + } else { + body.discountType = ''; + body.discountValue = 0; + } + const res = await updateGroupFees({ body }); + if (res.error) { + setError((res.error as any).error || 'Nastala chyba'); + } else { + onSaved(res.data); + onClose(); + } + } catch (e: any) { + setError(e.message || 'Nastala chyba'); + } finally { + setLoading(false); + } + }; + + return ( + + +

Poplatky skupiny — {group.name}

+
+ + {error && ( + setError(null)} dismissible>{error} + )} + +
+ + Poplatky (Kč) + setFees(e.target.value)} + placeholder="0" style={{ width: 110 }} + onKeyDown={e => e.stopPropagation()} + /> + + + Doprava (Kč) + setShipping(e.target.value)} + placeholder="0" style={{ width: 110 }} + onKeyDown={e => e.stopPropagation()} + /> + + + Spropitné (Kč) + setTip(e.target.value)} + placeholder="0" style={{ width: 110 }} + onKeyDown={e => e.stopPropagation()} + /> + +
+ +
+ + Sleva +
+ setDiscountType(e.target.value as 'percent' | 'fixed')} + style={{ width: 160 }} + > + + + + setDiscountValue(e.target.value)} + placeholder="0" style={{ width: 100 }} + onKeyDown={e => e.stopPropagation()} + /> + {discountType === 'percent' ? '%' : 'Kč'} +
+
+
+ +
+
Náhled celkových částek ({memberCount} členů, {feeShare > 0 ? `poplatek ${feeShare} Kč/os.` : 'bez poplatku'})
+ + + + + + + + + + + + + {memberEntries.map(([login, member]) => { + const base = member.amount ?? 0; + const surcharge = member.surchargeAmount ?? 0; + const discount = discountNum > 0 + ? (discountType === 'percent' + ? Math.round((base + surcharge) * discountNum / 100 * 100) / 100 + : Math.round(discountNum / memberCount * 100) / 100) + : 0; + const total = computeMemberTotal(member, feeShare, discountType, discountNum, memberCount); + return ( + + + + + + + + + ); + })} + +
ČlenZákladPříplatekPoplatekSlevaCelkem
{login}{base > 0 ? `${base} Kč` : '—'}{surcharge > 0 ? `${surcharge} Kč` : '—'}{feeShare > 0 ? `${feeShare} Kč` : '—'}{discount > 0 ? `-${discount} Kč` : '—'}{total > 0 ? `${total} Kč` : '—'}
+
+ + + + +
+ ); +} diff --git a/client/src/components/modals/PayForGroupModal.tsx b/client/src/components/modals/PayForGroupModal.tsx index 99f4a0e..92e0c63 100644 --- a/client/src/components/modals/PayForGroupModal.tsx +++ b/client/src/components/modals/PayForGroupModal.tsx @@ -1,15 +1,7 @@ -import { useState, useEffect, useCallback } from "react"; +import { useState, useEffect } from "react"; import { Modal, Button, Form, Table, Alert } from "react-bootstrap"; import { generateQr, OrderGroup, OrderGroupMember, QrRecipient } from "../../../../types"; -type DinerEntry = { - login: string; - baseAmount: number; - surchargeText: string; - surchargeAmount: string; - included: boolean; -}; - type Props = { isOpen: boolean; onClose: () => void; @@ -20,22 +12,14 @@ type Props = { groupId?: string; }; -function sanitizeAmount(value: string): string { - return value.replace(/[^0-9.,]/g, '').replace(',', '.'); -} - -function parseAmount(s: string): number | null { - if (!s || s.trim().length === 0) return null; - const n = parseFloat(s); - if (isNaN(n) || n < 0) return null; - const parts = s.split('.'); - if (parts.length === 2 && parts[1].length > 2) return null; - return Math.round(n * 100) / 100; -} +type DinerEntry = { + login: string; + member: OrderGroupMember; + included: boolean; +}; export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, bankAccount, bankAccountHolder, groupId }: Readonly) { const [diners, setDiners] = useState([]); - const [tipTotal, setTipTotal] = useState(''); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const [success, setSuccess] = useState(false); @@ -44,49 +28,39 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b if (!isOpen) return; const entries: DinerEntry[] = (Object.entries(group.members) as [string, OrderGroupMember][]).map(([login, member]) => ({ login, - baseAmount: member.amount ?? 0, - surchargeText: member.surchargeText ?? '', - surchargeAmount: member.surchargeAmount != null ? String(member.surchargeAmount) : '', + member, included: login !== payerLogin, })); setDiners(entries); - setTipTotal(''); setError(null); setSuccess(false); }, [isOpen, group, payerLogin]); - const includedNonPayers = diners.filter(d => d.included && d.login !== payerLogin); + const memberCount = diners.length; + const fees = group.fees ?? 0; + const shipping = group.shipping ?? 0; + const tip = group.tip ?? 0; + const totalFees = fees + shipping + tip; + const feeShare = memberCount > 0 ? Math.round(totalFees / memberCount * 100) / 100 : 0; - const tipPerPerson = (() => { - if (includedNonPayers.length === 0) return 0; - const tip = parseAmount(tipTotal); - if (tip === null || tip === 0) return 0; - const totalPeople = includedNonPayers.length + 1; // +1 for payer - return Math.round((tip / totalPeople) * 100) / 100; - })(); - const payerTipShare = (() => { - const tip = parseAmount(tipTotal); - if (!tip) return 0; - return Math.round((tip - tipPerPerson * includedNonPayers.length) * 100) / 100; - })(); - - const getTotal = (d: DinerEntry): number => { - const surcharge = parseAmount(d.surchargeAmount) ?? 0; - const tip = d.login === payerLogin ? payerTipShare : tipPerPerson; - return Math.round((d.baseAmount + surcharge + tip) * 100) / 100; + const getMemberTotal = (entry: DinerEntry): number => { + const base = entry.member.amount ?? 0; + const surcharge = entry.member.surchargeAmount ?? 0; + const discountType = group.discountType; + const discountValue = group.discountValue ?? 0; + const discount = discountValue > 0 + ? (discountType === 'percent' + ? Math.round((base + surcharge) * discountValue / 100 * 100) / 100 + : Math.round(discountValue / memberCount * 100) / 100) + : 0; + return Math.round((base + surcharge + feeShare - discount) * 100) / 100; }; - const handleInclude = useCallback((login: string, checked: boolean) => { + const includedNonPayers = diners.filter(d => d.included && d.login !== payerLogin); + + const handleInclude = (login: string, checked: boolean) => { setDiners(prev => prev.map(d => d.login === login ? { ...d, included: checked } : d)); - }, []); - - const handleSurchargeText = useCallback((login: string, value: string) => { - setDiners(prev => prev.map(d => d.login === login ? { ...d, surchargeText: value } : d)); - }, []); - - const handleSurchargeAmount = useCallback((login: string, value: string) => { - setDiners(prev => prev.map(d => d.login === login ? { ...d, surchargeAmount: sanitizeAmount(value) } : d)); - }, []); + }; const handleGenerate = async () => { setError(null); @@ -94,7 +68,7 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b for (const d of diners) { if (!d.included || d.login === payerLogin) continue; - const total = getTotal(d); + const total = getMemberTotal(d); if (total <= 0) { setError(`Celková částka pro ${d.login} musí být kladná`); return; @@ -134,10 +108,12 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b } }; + const hasFees = totalFees > 0; + return ( -

Zaplatit za skupinu — {group.name}

+

Generovat QR — {group.name}

{success ? ( @@ -146,7 +122,7 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b ) : ( <> -

Zaplatili jste za skupinu. Nastavte příplatky a společné poplatky, poté vygenerujte QR kódy pro ostatní.

+

Zaplatili jste za skupinu. Vyberte, komu vygenerovat QR kód k úhradě.

{error && ( setError(null)} dismissible> @@ -154,21 +130,36 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b )} + {hasFees && ( +
+ {fees > 0 && Poplatky: {fees} Kč} + {shipping > 0 && Doprava: {shipping} Kč} + {tip > 0 && Spropitné: {tip} Kč} + → {feeShare} Kč/os. +
+ )} + {group.discountValue != null && group.discountValue > 0 && ( +
+ Sleva: {group.discountType === 'percent' ? `${group.discountValue}%` : `${group.discountValue} Kč`} +
+ )} + - - - - + + + {hasFees && } + {diners.map(d => { const isPayer = d.login === payerLogin; - const total = getTotal(d); + const total = getMemberTotal(d); + const surcharge = d.member.surchargeAmount ?? 0; return ( - - + + {hasFees && ( + + )} ); })}
ČlenZáklad (Kč)PříplatekPoplatekCelkemZákladPříplatekPoplatekCelkem
@@ -182,74 +173,39 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b /> )} {d.login} - {d.baseAmount > 0 ? `${d.baseAmount} Kč` : } - -
- handleSurchargeText(d.login, e.target.value)} - disabled={!isPayer && !d.included} - size="sm" - onKeyDown={e => e.stopPropagation()} - /> - handleSurchargeAmount(d.login, e.target.value)} - disabled={!isPayer && !d.included} - size="sm" - style={{ width: 70 }} - onKeyDown={e => e.stopPropagation()} - /> -
+ {d.login} + {d.member.surchargeText && ( + ({d.member.surchargeText}) + )}
- {(() => { const s = isPayer ? payerTipShare : tipPerPerson; return s > 0 ? `${s} Kč` : '—'; })()} + {(d.member.amount ?? 0) > 0 ? `${d.member.amount} Kč` : } + {surcharge > 0 ? `${surcharge} Kč` : } + + {feeShare > 0 ? `${feeShare} Kč` : '—'} + - {`${total} Kč`} + {total > 0 ? `${total} Kč` : }
- -
- - setTipTotal(sanitizeAmount(e.target.value))} - size="sm" - style={{ width: 100 }} - onKeyDown={e => e.stopPropagation()} - /> - - {includedNonPayers.length > 0 && tipPerPerson > 0 - ? `(${tipPerPerson} Kč / osoba)` - : ''} - -
)}
{!success && ( <> - - Příjemci: {includedNonPayers.length} - - + Příjemci: {includedNonPayers.length} + @@ -286,28 +320,35 @@ export default function OrderGroupsPage() { Člen - Částka (Kč) + Částka (Kč) + Příplatek Poznámka + Celkem {memberEntries.map(([memberLogin, member]) => { - const amountKey = `${group.id}:${memberLogin}`; - const noteKey = `${group.id}:${memberLogin}`; - const editingAmount = amountKey in editAmounts; - const editingNote = noteKey in editNotes; + const key = `${group.id}:${memberLogin}`; + const editingAmount = key in editAmounts; + const editingNote = key in editNotes; + const editingSurcharge = key in editSurcharges; const canEdit = canEditMember(group, memberLogin); + const memberTotal = getMemberTotal(member); return ( {memberLogin} {memberLogin === group.creatorLogin && ( - + Zakladatel / objednávající}> + + )} {member.paid && ( - + Zaplaceno}> + + )} @@ -318,10 +359,10 @@ export default function OrderGroupsPage() { ref={memberLogin === login ? inputRef : undefined} type="number" size="sm" - value={editAmounts[amountKey]} - onChange={e => setEditAmounts(prev => ({ ...prev, [amountKey]: e.target.value }))} - onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') handleSaveAmount(group.id, memberLogin); if (e.key === 'Escape') setEditAmounts(prev => { const n = { ...prev }; delete n[amountKey]; return n; }); }} - style={{ width: 80 }} + value={editAmounts[key]} + onChange={e => setEditAmounts(prev => ({ ...prev, [key]: e.target.value }))} + onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') handleSaveAmount(group.id, memberLogin); if (e.key === 'Escape') setEditAmounts(prev => { const n = { ...prev }; delete n[key]; return n; }); }} + style={{ width: 75 }} autoFocus={memberLogin === login} /> @@ -329,22 +370,60 @@ export default function OrderGroupsPage() { ) : ( canEdit && setEditAmounts(prev => ({ ...prev, [amountKey]: String(member.amount ?? '') }))} + onClick={() => canEdit && setEditAmounts(prev => ({ ...prev, [key]: String(member.amount ?? '') }))} title={canEdit ? 'Klikněte pro úpravu' : undefined} > {member.amount != null ? `${member.amount} Kč` : } )} + + {canEdit && editingSurcharge ? ( +
+ setEditSurcharges(prev => ({ ...prev, [key]: { ...prev[key], text: e.target.value } }))} + onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') handleSaveSurcharge(group.id, memberLogin); if (e.key === 'Escape') setEditSurcharges(prev => { const n = { ...prev }; delete n[key]; return n; }); }} + style={{ width: 80 }} + autoFocus + /> + setEditSurcharges(prev => ({ ...prev, [key]: { ...prev[key], amount: e.target.value } }))} + onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') handleSaveSurcharge(group.id, memberLogin); if (e.key === 'Escape') setEditSurcharges(prev => { const n = { ...prev }; delete n[key]; return n; }); }} + style={{ width: 60 }} + /> + +
+ ) : ( + canEdit && setEditSurcharges(prev => ({ ...prev, [key]: { text: member.surchargeText ?? '', amount: member.surchargeAmount != null ? String(member.surchargeAmount) : '' } }))} + title={canEdit ? 'Klikněte pro úpravu příplatku' : undefined} + > + {member.surchargeAmount != null && member.surchargeAmount > 0 ? ( + {member.surchargeText ? `${member.surchargeText}: ` : ''}{member.surchargeAmount} Kč + ) : ( + + )} + + )} + {canEdit && editingNote ? (
setEditNotes(prev => ({ ...prev, [noteKey]: e.target.value }))} - onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') handleSaveNote(group.id, memberLogin); if (e.key === 'Escape') setEditNotes(prev => { const n = { ...prev }; delete n[noteKey]; return n; }); }} + value={editNotes[key]} + onChange={e => setEditNotes(prev => ({ ...prev, [key]: e.target.value }))} + onKeyDown={e => { e.stopPropagation(); if (e.key === 'Enter') handleSaveNote(group.id, memberLogin); if (e.key === 'Escape') setEditNotes(prev => { const n = { ...prev }; delete n[key]; return n; }); }} autoFocus /> @@ -352,13 +431,18 @@ export default function OrderGroupsPage() { ) : ( canEdit && setEditNotes(prev => ({ ...prev, [noteKey]: member.note ?? '' }))} + onClick={() => canEdit && setEditNotes(prev => ({ ...prev, [key]: member.note ?? '' }))} title={canEdit ? 'Klikněte pro úpravu poznámky' : undefined} > {member.note || '—'} )} + + 0 ? 'fw-bold' : 'text-muted'}> + {memberTotal > 0 ? `${memberTotal} Kč` : '—'} + +
{canManageMembers(group) && (isCreator || memberLogin === login) && (memberLogin !== group.creatorLogin) && ( @@ -377,6 +461,21 @@ export default function OrderGroupsPage() { + {/* Souhrn poplatků a slevy */} + {(totalFees > 0 || (group.discountValue != null && group.discountValue > 0)) && ( +
+ {group.fees != null && group.fees > 0 && Poplatky: {group.fees} Kč} + {group.shipping != null && group.shipping > 0 && Doprava: {group.shipping} Kč} + {group.tip != null && group.tip > 0 && Spropitné: {group.tip} Kč} + {feeShare > 0 && {feeShare} Kč/os.} + {group.discountValue != null && group.discountValue > 0 && ( + + Sleva: {group.discountType === 'percent' ? `${group.discountValue}%` : `${group.discountValue} Kč`} + + )} +
+ )} + {/* Časy objednání a doručení */} {isOrdered && (
@@ -472,6 +571,21 @@ export default function OrderGroupsPage() { bankAccountHolder={settings.holderName} /> )} + + {feesModal && ( + setFeesModal(null)} + group={feesModal} + onSaved={newData => { + if (newData) { + setData(newData); + socket.emit?.('message', newData as ClientData); + } + setFeesModal(null); + }} + /> + )}
); } diff --git a/server/src/groups.ts b/server/src/groups.ts index 18495a0..196a336 100644 --- a/server/src/groups.ts +++ b/server/src/groups.ts @@ -147,6 +147,20 @@ export async function markGroupMemberPaid(login: string, groupId: string, date?: return saveExtraData(data, date); } +export async function updateGroupFees(login: string, groupId: string, fees?: number, shipping?: number, tip?: number, discountType?: string, discountValue?: number, date?: Date): Promise { + const data = await getExtraData(date); + const group = findGroup(data, groupId); + if (!group) throw new Error('Skupina nebyla nalezena'); + if (group.creatorLogin !== login) throw new Error('Poplatky může měnit pouze zakladatel'); + if (group.state === GroupState.ORDERED) throw new Error('Skupinu ve stavu "objednáno" nelze upravovat'); + if (fees !== undefined) group.fees = fees > 0 ? fees : undefined; + if (shipping !== undefined) group.shipping = shipping > 0 ? shipping : undefined; + if (tip !== undefined) group.tip = tip > 0 ? tip : undefined; + if (discountType !== undefined) group.discountType = (discountType as any) || undefined; + if (discountValue !== undefined) group.discountValue = discountValue > 0 ? discountValue : undefined; + return saveExtraData(data, date); +} + export async function updateGroupTimes(login: string, groupId: string, orderedAt?: string, deliveryAt?: string, date?: Date): Promise { const data = await getExtraData(date); const group = findGroup(data, groupId); diff --git a/server/src/routes/groupRoutes.ts b/server/src/routes/groupRoutes.ts index 4918ac2..ec67fc5 100644 --- a/server/src/routes/groupRoutes.ts +++ b/server/src/routes/groupRoutes.ts @@ -2,7 +2,7 @@ import express, { Request } from "express"; import { getLogin } from "../auth"; import { parseToken } from "../utils"; import { getWebsocket } from "../websocket"; -import { createGroup, deleteGroup, addGroupMember, removeGroupMember, updateGroupMember, setGroupState, updateGroupTimes } from "../groups"; +import { createGroup, deleteGroup, addGroupMember, removeGroupMember, updateGroupMember, setGroupState, updateGroupTimes, updateGroupFees } from "../groups"; import { GroupState } from "../../../types/gen/types.gen"; const router = express.Router(); @@ -109,6 +109,32 @@ router.post("/setState", async (req: Request, res, next) => { } catch (e: any) { next(e); } }); +router.post("/updateFees", async (req: Request, res, next) => { + const login = getLogin(parseToken(req)); + const { id, fees, shipping, tip, discountType, discountValue } = req.body ?? {}; + if (!id) return res.status(400).json({ error: 'Nebylo předáno ID skupiny' }); + if (fees !== undefined && (typeof fees !== 'number' || !Number.isFinite(fees) || fees < 0)) { + return res.status(400).json({ error: 'Neplatná výše poplatků' }); + } + if (shipping !== undefined && (typeof shipping !== 'number' || !Number.isFinite(shipping) || shipping < 0)) { + return res.status(400).json({ error: 'Neplatná výše dopravy' }); + } + if (tip !== undefined && (typeof tip !== 'number' || !Number.isFinite(tip) || tip < 0)) { + return res.status(400).json({ error: 'Neplatná výše spropitného' }); + } + if (discountType !== undefined && discountType !== '' && !['percent', 'fixed'].includes(discountType)) { + return res.status(400).json({ error: 'Neplatný typ slevy' }); + } + if (discountValue !== undefined && (typeof discountValue !== 'number' || !Number.isFinite(discountValue) || discountValue < 0)) { + return res.status(400).json({ error: 'Neplatná výše slevy' }); + } + try { + const data = await updateGroupFees(login, id, fees, shipping, tip, discountType, discountValue); + broadcastExtra(data); + res.status(200).json(data); + } catch (e: any) { next(e); } +}); + router.post("/updateTimes", async (req: Request, res, next) => { const login = getLogin(parseToken(req)); const { id, orderedAt, deliveryAt } = req.body ?? {}; diff --git a/server/src/routes/qrRoutes.ts b/server/src/routes/qrRoutes.ts index 684718b..7de62e9 100644 --- a/server/src/routes/qrRoutes.ts +++ b/server/src/routes/qrRoutes.ts @@ -3,6 +3,7 @@ import { getLogin } from "../auth"; import { parseToken, formatDate } from "../utils"; import { generateQr } from "../qr"; import { addPendingQr } from "../pizza"; +import { emitToUser } from "../websocket"; import { GenerateQrData } from "../../../types"; import crypto from "crypto"; @@ -48,15 +49,17 @@ router.post("/generate", async (req: Request<{}, any, GenerateQrData["body"]>, r const id = crypto.randomUUID(); await generateQr(recipient.login, bankAccount, bankAccountHolder, recipient.amount, recipient.purpose, id); - // Uložit jako nevyřízený QR kód - await addPendingQr(recipient.login, { + // Uložit jako nevyřízený QR kód a okamžitě doručit příjemci + const pendingQr = { id, date: today, creator: login, totalPrice: recipient.amount, purpose: recipient.purpose, ...(groupId ? { groupId } : {}), - }); + }; + await addPendingQr(recipient.login, pendingQr); + emitToUser(recipient.login, 'pendingQr', pendingQr); } res.status(200).json({ success: true, count: recipients.length }); diff --git a/server/src/websocket.ts b/server/src/websocket.ts index c4bf99e..2257881 100644 --- a/server/src/websocket.ts +++ b/server/src/websocket.ts @@ -11,6 +11,12 @@ export const initWebsocket = (server: any) => { io.on("connection", (socket) => { console.log(`New client connected: ${socket.id}`); + socket.on("join", (login: string) => { + if (login && typeof login === "string") { + socket.join(`user:${login}`); + } + }); + socket.on("message", (message) => { io.emit("message", message); }); @@ -22,6 +28,9 @@ export const initWebsocket = (server: any) => { return io; } -export const getWebsocket = () => { - return io; +export const getWebsocket = () => io; + +/** Pošle event konkrétnímu přihlášenému uživateli (pokud je připojen). */ +export const emitToUser = (login: string, event: string, data: unknown) => { + io.to(`user:${login}`).emit(event, data); } \ No newline at end of file diff --git a/types/api.yml b/types/api.yml index 1aba664..652c0c5 100644 --- a/types/api.yml +++ b/types/api.yml @@ -96,6 +96,8 @@ paths: $ref: "./paths/groups/setState.yml" /groups/updateTimes: $ref: "./paths/groups/updateTimes.yml" + /groups/updateFees: + $ref: "./paths/groups/updateFees.yml" # Správa obchodů (/api/stores) /stores: diff --git a/types/package-lock.json b/types/package-lock.json new file mode 100644 index 0000000..60e5a66 --- /dev/null +++ b/types/package-lock.json @@ -0,0 +1,594 @@ +{ + "name": "@luncher/types", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@luncher/types", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@hey-api/client-fetch": "^0.8.2", + "@hey-api/openapi-ts": "^0.64.7", + "typescript": "^5.9.3" + } + }, + "node_modules/@hey-api/client-fetch": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.8.2.tgz", + "integrity": "sha512-61T4UGfAzY5345vMxWDX8qnSTNRJcOpWuZyvNu3vNebCTLPwMQAM85mhEuBoACdWeRtLhNoUjU0UR5liRyD1bA==", + "deprecated": "Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts.", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/json-schema-ref-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.2.tgz", + "integrity": "sha512-F6LSkttZcT/XiX3ydeDqTY3uRN3BLJMwyMTk4kg/ichZlKUp3+3Odv0WokSmXGSoZGTW/N66FROMYAm5NPdJlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/openapi-ts": { + "version": "0.64.7", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.64.7.tgz", + "integrity": "sha512-xpaBzdGAKz7cPuGah1GZWl3zTZquOXRnwmpVCQKEUpvDtSYWBLjSxtAYIqDBjwJkuNHcakZBIWLcappx7slc2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/json-schema-ref-parser": "1.0.2", + "c12": "2.0.1", + "commander": "13.0.0", + "handlebars": "4.7.8" + }, + "bin": { + "openapi-ts": "bin/index.cjs" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=22.11.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + }, + "peerDependencies": { + "typescript": "^5.5.3" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/c12": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/c12/-/c12-2.0.1.tgz", + "integrity": "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.1", + "confbox": "^0.1.7", + "defu": "^6.1.4", + "dotenv": "^16.4.5", + "giget": "^1.2.3", + "jiti": "^2.3.0", + "mlly": "^1.7.1", + "ohash": "^1.1.4", + "pathe": "^1.1.2", + "perfect-debounce": "^1.0.0", + "pkg-types": "^1.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/commander": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz", + "integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz", + "integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz", + "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/giget": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.5.tgz", + "integrity": "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.5.4", + "pathe": "^2.0.3", + "tar": "^6.2.1" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/giget/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nypm": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz", + "integrity": "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "tinyexec": "^0.3.2", + "ufo": "^1.5.4" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/nypm/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ohash": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz", + "integrity": "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/types/paths/groups/updateFees.yml b/types/paths/groups/updateFees.yml new file mode 100644 index 0000000..4dfcf5a --- /dev/null +++ b/types/paths/groups/updateFees.yml @@ -0,0 +1,34 @@ +post: + operationId: updateGroupFees + summary: Aktualizuje skupinové poplatky a slevu (pouze zakladatel, pouze otevřená skupina). + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - id + properties: + id: + description: ID skupiny + type: string + fees: + description: Poplatky (Kč) + type: number + shipping: + description: Doprava (Kč) + type: number + tip: + description: Spropitné (Kč) + type: number + discountType: + description: Typ slevy + type: string + enum: [percent, fixed] + discountValue: + description: Hodnota slevy + type: number + responses: + "200": + $ref: "../../api.yml#/components/responses/ClientDataResponse" diff --git a/types/schemas/_index.yml b/types/schemas/_index.yml index bd077ec..fa8f1db 100644 --- a/types/schemas/_index.yml +++ b/types/schemas/_index.yml @@ -752,6 +752,22 @@ OrderGroup: deliveryAt: description: Očekávaný čas doručení ve formátu HH:MM type: string + fees: + description: Poplatky (balení apod.) celkem v Kč + type: number + shipping: + description: Doprava v Kč + type: number + tip: + description: Spropitné v Kč + type: number + discountType: + description: Typ slevy aplikované na objednávku + type: string + enum: [percent, fixed] + discountValue: + description: Hodnota slevy (procenta nebo Kč) + type: number # --- NEVYŘÍZENÉ QR KÓDY --- PendingQr: diff --git a/types/yarn.lock b/types/yarn.lock index 9fc8d63..6caa77d 100644 --- a/types/yarn.lock +++ b/types/yarn.lock @@ -4,12 +4,12 @@ "@hey-api/client-fetch@^0.8.2": version "0.8.2" - resolved "https://registry.yarnpkg.com/@hey-api/client-fetch/-/client-fetch-0.8.2.tgz#675aadfbc9478bb8eef5679f11a9334258dff4c8" + resolved "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.8.2.tgz" integrity sha512-61T4UGfAzY5345vMxWDX8qnSTNRJcOpWuZyvNu3vNebCTLPwMQAM85mhEuBoACdWeRtLhNoUjU0UR5liRyD1bA== "@hey-api/json-schema-ref-parser@1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.2.tgz#c3824c5d9d531eeb5c2b2557857a8ad20b5c75a7" + resolved "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.2.tgz" integrity sha512-F6LSkttZcT/XiX3ydeDqTY3uRN3BLJMwyMTk4kg/ichZlKUp3+3Odv0WokSmXGSoZGTW/N66FROMYAm5NPdJlA== dependencies: "@jsdevtools/ono" "^7.1.3" @@ -18,7 +18,7 @@ "@hey-api/openapi-ts@^0.64.7": version "0.64.7" - resolved "https://registry.yarnpkg.com/@hey-api/openapi-ts/-/openapi-ts-0.64.7.tgz#f239d268b4a35b91f5ff25479d15578feb01f365" + resolved "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.64.7.tgz" integrity sha512-xpaBzdGAKz7cPuGah1GZWl3zTZquOXRnwmpVCQKEUpvDtSYWBLjSxtAYIqDBjwJkuNHcakZBIWLcappx7slc2g== dependencies: "@hey-api/json-schema-ref-parser" "1.0.2" @@ -28,27 +28,27 @@ "@jsdevtools/ono@^7.1.3": version "7.1.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== "@types/json-schema@^7.0.15": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== acorn@^8.14.0: version "8.14.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== c12@2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/c12/-/c12-2.0.1.tgz#5702d280b31a08abba39833494c9b1202f0f5aec" + resolved "https://registry.npmjs.org/c12/-/c12-2.0.1.tgz" integrity sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A== dependencies: chokidar "^4.0.1" @@ -66,63 +66,63 @@ c12@2.0.1: chokidar@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz" integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== dependencies: readdirp "^4.0.1" chownr@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== citty@^0.1.6: version "0.1.6" - resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.6.tgz#0f7904da1ed4625e1a9ea7e0fa780981aab7c5e4" + resolved "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz" integrity sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== dependencies: consola "^3.2.3" commander@13.0.0: version "13.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-13.0.0.tgz#1b161f60ee3ceb8074583a0f95359a4f8701845c" + resolved "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz" integrity sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ== confbox@^0.1.7, confbox@^0.1.8: version "0.1.8" - resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + resolved "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz" integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== consola@^3.2.3, consola@^3.4.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.0.tgz#4cfc9348fd85ed16a17940b3032765e31061ab88" + resolved "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz" integrity sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA== defu@^6.1.4: version "6.1.4" - resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + resolved "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz" integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== destr@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449" + resolved "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz" integrity sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ== dotenv@^16.4.5: version "16.4.7" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz" integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== fs-minipass@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" giget@^1.2.3: version "1.2.5" - resolved "https://registry.yarnpkg.com/giget/-/giget-1.2.5.tgz#0bd4909356a0da75cc1f2b33538f93adec0d202f" + resolved "https://registry.npmjs.org/giget/-/giget-1.2.5.tgz" integrity sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug== dependencies: citty "^0.1.6" @@ -135,7 +135,7 @@ giget@^1.2.3: handlebars@4.7.8: version "4.7.8" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz" integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== dependencies: minimist "^1.2.5" @@ -147,36 +147,36 @@ handlebars@4.7.8: jiti@^2.3.0: version "2.4.2" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.4.2.tgz#d19b7732ebb6116b06e2038da74a55366faef560" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz" integrity sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A== js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" minimist@^1.2.5: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minipass@^3.0.0: version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" minipass@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== minizlib@^2.1.1: version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" @@ -184,12 +184,12 @@ minizlib@^2.1.1: mkdirp@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mlly@^1.7.1, mlly@^1.7.4: version "1.7.4" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.4.tgz#3d7295ea2358ec7a271eaa5d000a0f84febe100f" + resolved "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz" integrity sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw== dependencies: acorn "^8.14.0" @@ -199,17 +199,17 @@ mlly@^1.7.1, mlly@^1.7.4: neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== node-fetch-native@^1.6.6: version "1.6.6" - resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz#ae1d0e537af35c2c0b0de81cbff37eedd410aa37" + resolved "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz" integrity sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ== nypm@^0.5.4: version "0.5.4" - resolved "https://registry.yarnpkg.com/nypm/-/nypm-0.5.4.tgz#a5ab0d8d37f96342328479f88ef58699f29b3051" + resolved "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz" integrity sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA== dependencies: citty "^0.1.6" @@ -221,27 +221,32 @@ nypm@^0.5.4: ohash@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.4.tgz#ae8d83014ab81157d2c285abf7792e2995fadd72" + resolved "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz" integrity sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g== pathe@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -pathe@^2.0.1, pathe@^2.0.3: +pathe@^2.0.1: version "2.0.3" - resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== perfect-debounce@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + resolved "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz" integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== pkg-types@^1.2.0, pkg-types@^1.3.0, pkg-types@^1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + resolved "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz" integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== dependencies: confbox "^0.1.8" @@ -250,7 +255,7 @@ pkg-types@^1.2.0, pkg-types@^1.3.0, pkg-types@^1.3.1: rc9@^2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/rc9/-/rc9-2.1.2.tgz#6282ff638a50caa0a91a31d76af4a0b9cbd1080d" + resolved "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz" integrity sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg== dependencies: defu "^6.1.4" @@ -258,17 +263,17 @@ rc9@^2.1.2: readdirp@^4.0.1: version "4.1.2" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz" integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== source-map@^0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== tar@^6.2.1: version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== dependencies: chownr "^2.0.0" @@ -280,30 +285,30 @@ tar@^6.2.1: tinyexec@^0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz" integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== -typescript@^5.9.3: +typescript@^5.5.3, typescript@^5.9.3: version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ufo@^1.5.4: version "1.5.4" - resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.4.tgz#16d6949674ca0c9e0fbbae1fa20a71d7b1ded754" + resolved "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz" integrity sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ== uglify-js@^3.1.4: version "3.19.3" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== wordwrap@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==