feat: vylepšení objednávek
CI / Generate TypeScript types (pull_request) Successful in 20s
CI / Server unit tests (pull_request) Failing after 20s
CI / Build client (pull_request) Failing after 30s
CI / Build server (pull_request) Successful in 3m13s
CI / Playwright E2E tests (pull_request) Has been skipped
CI / Build and push Docker image (pull_request) Has been skipped
CI / Notify (pull_request) Has been skipped
CI / Build client (push) Failing after 10m5s
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Failing after 22s
CI / Build server (push) Successful in 41s
CI / Playwright E2E tests (push) Has been skipped
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 4s
CI / Generate TypeScript types (pull_request) Successful in 20s
CI / Server unit tests (pull_request) Failing after 20s
CI / Build client (pull_request) Failing after 30s
CI / Build server (pull_request) Successful in 3m13s
CI / Playwright E2E tests (pull_request) Has been skipped
CI / Build and push Docker image (pull_request) Has been skipped
CI / Notify (pull_request) Has been skipped
CI / Build client (push) Failing after 10m5s
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Failing after 22s
CI / Build server (push) Successful in 41s
CI / Playwright E2E tests (push) Has been skipped
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 4s
This commit is contained in:
+13
-2
@@ -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
|
||||
|
||||
@@ -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<Props>) {
|
||||
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<string | null>(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<string, any> = { 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 (
|
||||
<Modal show={isOpen} onHide={onClose} size="lg">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title><h2>Poplatky skupiny — {group.name}</h2></Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{error && (
|
||||
<Alert variant="danger" onClose={() => setError(null)} dismissible>{error}</Alert>
|
||||
)}
|
||||
|
||||
<div className="d-flex gap-3 flex-wrap mb-3">
|
||||
<Form.Group>
|
||||
<Form.Label>Poplatky (Kč)</Form.Label>
|
||||
<Form.Control
|
||||
type="number" min={0} step={0.01}
|
||||
value={fees} onChange={e => setFees(e.target.value)}
|
||||
placeholder="0" style={{ width: 110 }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Doprava (Kč)</Form.Label>
|
||||
<Form.Control
|
||||
type="number" min={0} step={0.01}
|
||||
value={shipping} onChange={e => setShipping(e.target.value)}
|
||||
placeholder="0" style={{ width: 110 }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group>
|
||||
<Form.Label>Spropitné (Kč)</Form.Label>
|
||||
<Form.Control
|
||||
type="number" min={0} step={0.01}
|
||||
value={tip} onChange={e => setTip(e.target.value)}
|
||||
placeholder="0" style={{ width: 110 }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
</Form.Group>
|
||||
</div>
|
||||
|
||||
<div className="d-flex gap-3 align-items-end flex-wrap mb-3">
|
||||
<Form.Group>
|
||||
<Form.Label>Sleva</Form.Label>
|
||||
<div className="d-flex gap-2 align-items-center">
|
||||
<Form.Select
|
||||
value={discountType}
|
||||
onChange={e => setDiscountType(e.target.value as 'percent' | 'fixed')}
|
||||
style={{ width: 160 }}
|
||||
>
|
||||
<option value="percent">Procentuální (%)</option>
|
||||
<option value="fixed">Pevná částka (Kč)</option>
|
||||
</Form.Select>
|
||||
<Form.Control
|
||||
type="number" min={0} step={discountType === 'percent' ? 1 : 0.01}
|
||||
value={discountValue} onChange={e => setDiscountValue(e.target.value)}
|
||||
placeholder="0" style={{ width: 100 }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
<span className="text-muted">{discountType === 'percent' ? '%' : 'Kč'}</span>
|
||||
</div>
|
||||
</Form.Group>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<h6>Náhled celkových částek ({memberCount} členů, {feeShare > 0 ? `poplatek ${feeShare} Kč/os.` : 'bez poplatku'})</h6>
|
||||
<Table size="sm" bordered>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Člen</th>
|
||||
<th className="text-end">Základ</th>
|
||||
<th className="text-end">Příplatek</th>
|
||||
<th className="text-end">Poplatek</th>
|
||||
<th className="text-end">Sleva</th>
|
||||
<th className="text-end fw-bold">Celkem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{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 (
|
||||
<tr key={login}>
|
||||
<td><strong>{login}</strong></td>
|
||||
<td className="text-end">{base > 0 ? `${base} Kč` : '—'}</td>
|
||||
<td className="text-end">{surcharge > 0 ? `${surcharge} Kč` : '—'}</td>
|
||||
<td className="text-end">{feeShare > 0 ? `${feeShare} Kč` : '—'}</td>
|
||||
<td className="text-end text-danger">{discount > 0 ? `-${discount} Kč` : '—'}</td>
|
||||
<td className="text-end fw-bold">{total > 0 ? `${total} Kč` : '—'}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={onClose} disabled={loading}>Storno</Button>
|
||||
<Button variant="primary" onClick={handleSave} disabled={loading}>
|
||||
{loading ? 'Ukládám...' : 'Uložit'}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -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<Props>) {
|
||||
const [diners, setDiners] = useState<DinerEntry[]>([]);
|
||||
const [tipTotal, setTipTotal] = useState('');
|
||||
const [error, setError] = useState<string | null>(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 (
|
||||
<Modal show={isOpen} onHide={onClose} size="lg">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title><h2>Zaplatit za skupinu — {group.name}</h2></Modal.Title>
|
||||
<Modal.Title><h2>Generovat QR — {group.name}</h2></Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{success ? (
|
||||
@@ -146,7 +122,7 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b
|
||||
</Alert>
|
||||
) : (
|
||||
<>
|
||||
<p>Zaplatili jste za skupinu. Nastavte příplatky a společné poplatky, poté vygenerujte QR kódy pro ostatní.</p>
|
||||
<p>Zaplatili jste za skupinu. Vyberte, komu vygenerovat QR kód k úhradě.</p>
|
||||
|
||||
{error && (
|
||||
<Alert variant="danger" onClose={() => setError(null)} dismissible>
|
||||
@@ -154,21 +130,36 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{hasFees && (
|
||||
<div className="d-flex gap-3 mb-2 text-muted" style={{ fontSize: '0.9em' }}>
|
||||
{fees > 0 && <span>Poplatky: <strong>{fees} Kč</strong></span>}
|
||||
{shipping > 0 && <span>Doprava: <strong>{shipping} Kč</strong></span>}
|
||||
{tip > 0 && <span>Spropitné: <strong>{tip} Kč</strong></span>}
|
||||
<span>→ {feeShare} Kč/os.</span>
|
||||
</div>
|
||||
)}
|
||||
{group.discountValue != null && group.discountValue > 0 && (
|
||||
<div className="mb-2 text-success" style={{ fontSize: '0.9em' }}>
|
||||
Sleva: {group.discountType === 'percent' ? `${group.discountValue}%` : `${group.discountValue} Kč`}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Table striped bordered hover responsive size="sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: 40 }}></th>
|
||||
<th>Člen</th>
|
||||
<th style={{ width: 90 }}>Základ (Kč)</th>
|
||||
<th style={{ width: 220 }}>Příplatek</th>
|
||||
<th style={{ width: 90 }}>Poplatek</th>
|
||||
<th style={{ width: 90 }}>Celkem</th>
|
||||
<th style={{ width: 90 }} className="text-end">Základ</th>
|
||||
<th style={{ width: 90 }} className="text-end">Příplatek</th>
|
||||
{hasFees && <th style={{ width: 90 }} className="text-end">Poplatek</th>}
|
||||
<th style={{ width: 90 }} className="text-end fw-bold">Celkem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{diners.map(d => {
|
||||
const isPayer = d.login === payerLogin;
|
||||
const total = getTotal(d);
|
||||
const total = getMemberTotal(d);
|
||||
const surcharge = d.member.surchargeAmount ?? 0;
|
||||
return (
|
||||
<tr key={d.login} className={!d.included && !isPayer ? 'text-muted' : ''}>
|
||||
<td className="text-center">
|
||||
@@ -182,74 +173,39 @@ export default function PayForGroupModal({ isOpen, onClose, group, payerLogin, b
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td><strong>{d.login}</strong></td>
|
||||
<td className="text-end">
|
||||
{d.baseAmount > 0 ? `${d.baseAmount} Kč` : <span className="text-muted">—</span>}
|
||||
</td>
|
||||
<td>
|
||||
<div className="d-flex gap-1">
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="popis"
|
||||
value={d.surchargeText}
|
||||
onChange={e => handleSurchargeText(d.login, e.target.value)}
|
||||
disabled={!isPayer && !d.included}
|
||||
size="sm"
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="Kč"
|
||||
value={d.surchargeAmount}
|
||||
onChange={e => handleSurchargeAmount(d.login, e.target.value)}
|
||||
disabled={!isPayer && !d.included}
|
||||
size="sm"
|
||||
style={{ width: 70 }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
<strong>{d.login}</strong>
|
||||
{d.member.surchargeText && (
|
||||
<small className="text-muted ms-1">({d.member.surchargeText})</small>
|
||||
)}
|
||||
</td>
|
||||
<td className="text-end">
|
||||
{(() => { const s = isPayer ? payerTipShare : tipPerPerson; return s > 0 ? `${s} Kč` : '—'; })()}
|
||||
{(d.member.amount ?? 0) > 0 ? `${d.member.amount} Kč` : <span className="text-muted">—</span>}
|
||||
</td>
|
||||
<td className="text-end">
|
||||
{surcharge > 0 ? `${surcharge} Kč` : <span className="text-muted">—</span>}
|
||||
</td>
|
||||
{hasFees && (
|
||||
<td className="text-end">
|
||||
{feeShare > 0 ? `${feeShare} Kč` : '—'}
|
||||
</td>
|
||||
)}
|
||||
<td className="text-end fw-bold">
|
||||
{`${total} Kč`}
|
||||
{total > 0 ? `${total} Kč` : <span className="text-muted">—</span>}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
<div className="d-flex align-items-center gap-2 mt-2">
|
||||
<label className="mb-0 text-nowrap">Poplatky celkem (Kč):</label>
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="0"
|
||||
value={tipTotal}
|
||||
onChange={e => setTipTotal(sanitizeAmount(e.target.value))}
|
||||
size="sm"
|
||||
style={{ width: 100 }}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
/>
|
||||
<small className="text-muted">
|
||||
{includedNonPayers.length > 0 && tipPerPerson > 0
|
||||
? `(${tipPerPerson} Kč / osoba)`
|
||||
: ''}
|
||||
</small>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
{!success && (
|
||||
<>
|
||||
<span className="me-auto text-muted">
|
||||
Příjemci: {includedNonPayers.length}
|
||||
</span>
|
||||
<Button variant="secondary" onClick={onClose} disabled={loading}>
|
||||
Storno
|
||||
</Button>
|
||||
<span className="me-auto text-muted">Příjemci: {includedNonPayers.length}</span>
|
||||
<Button variant="secondary" onClick={onClose} disabled={loading}>Storno</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleGenerate}
|
||||
|
||||
@@ -18,3 +18,4 @@ export const SocketContext = React.createContext();
|
||||
export const EVENT_CONNECT = 'connect';
|
||||
export const EVENT_DISCONNECT = 'disconnect';
|
||||
export const EVENT_MESSAGE = 'message';
|
||||
export const EVENT_PENDING_QR = 'pendingQr';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useContext, useEffect, useRef, useState } from 'react';
|
||||
import { Alert, Badge, Button, Card, Form, Modal, Table } from 'react-bootstrap';
|
||||
import { Alert, Badge, Button, Card, Form, Modal, OverlayTrigger, Table, Tooltip } from 'react-bootstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTrashCan } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faBasketShopping, faCircleCheck, faGear, faLock, faLockOpen, faSearch, faUserPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
@@ -16,6 +16,7 @@ import Footer from '../components/Footer';
|
||||
import Loader from '../components/Loader';
|
||||
import StoreAdminModal from '../components/modals/StoreAdminModal';
|
||||
import PayForGroupModal from '../components/modals/PayForGroupModal';
|
||||
import EditGroupFeesModal from '../components/modals/EditGroupFeesModal';
|
||||
|
||||
const SLOT = MealSlot.EXTRA;
|
||||
const TIME_REGEX = /^([01]\d|2[0-3]):[0-5]\d$/;
|
||||
@@ -41,8 +42,10 @@ export default function OrderGroupsPage() {
|
||||
const [adminModalOpen, setAdminModalOpen] = useState(false);
|
||||
const [editAmounts, setEditAmounts] = useState<Record<string, string>>({});
|
||||
const [editNotes, setEditNotes] = useState<Record<string, string>>({});
|
||||
const [editSurcharges, setEditSurcharges] = useState<Record<string, { text: string; amount: string }>>({});
|
||||
const [editTimes, setEditTimes] = useState<Record<string, { orderedAt: string; deliveryAt: string }>>({});
|
||||
const [payModal, setPayModal] = useState<OrderGroup | null>(null);
|
||||
const [feesModal, setFeesModal] = useState<OrderGroup | null>(null);
|
||||
const [confirmOrderGroup, setConfirmOrderGroup] = useState<OrderGroup | null>(null);
|
||||
const [pageError, setPageError] = useState<string | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
@@ -133,6 +136,19 @@ export default function OrderGroupsPage() {
|
||||
if (ok) setEditNotes(prev => { const next = { ...prev }; delete next[key]; return next; });
|
||||
};
|
||||
|
||||
const handleSaveSurcharge = async (groupId: string, login: string) => {
|
||||
const key = `${groupId}:${login}`;
|
||||
const surchargeText = editSurcharges[key]?.text ?? '';
|
||||
const rawAmount = editSurcharges[key]?.amount ?? '';
|
||||
const surchargeAmount = rawAmount === '' ? 0 : parseFloat(rawAmount.replace(',', '.'));
|
||||
if (rawAmount !== '' && (isNaN(surchargeAmount) || surchargeAmount < 0)) {
|
||||
setPageError('Zadejte platnou výši příplatku');
|
||||
return;
|
||||
}
|
||||
const ok = await refresh(() => updateGroupMember({ body: { id: groupId, login, surchargeText, surchargeAmount: rawAmount === '' ? 0 : surchargeAmount } }));
|
||||
if (ok) setEditSurcharges(prev => { const next = { ...prev }; delete next[key]; return next; });
|
||||
};
|
||||
|
||||
const handleSaveTimes = async (group: OrderGroup) => {
|
||||
const times = editTimes[group.id];
|
||||
if (!times) return;
|
||||
@@ -234,8 +250,23 @@ export default function OrderGroupsPage() {
|
||||
const isOrdered = group.state === GroupState.ORDERED;
|
||||
const isLocked = group.state === GroupState.LOCKED;
|
||||
const memberEntries = Object.entries(group.members) as [string, OrderGroupMember][];
|
||||
const memberCount = memberEntries.length;
|
||||
const editingTimes = group.id in editTimes;
|
||||
|
||||
const totalFees = (group.fees ?? 0) + (group.shipping ?? 0) + (group.tip ?? 0);
|
||||
const feeShare = memberCount > 0 ? Math.round(totalFees / memberCount * 100) / 100 : 0;
|
||||
const getMemberTotal = (m: OrderGroupMember) => {
|
||||
const base = m.amount ?? 0;
|
||||
const surcharge = m.surchargeAmount ?? 0;
|
||||
const dv = group.discountValue ?? 0;
|
||||
const discount = dv > 0
|
||||
? (group.discountType === 'percent'
|
||||
? Math.round((base + surcharge) * dv / 100 * 100) / 100
|
||||
: Math.round(dv / memberCount * 100) / 100)
|
||||
: 0;
|
||||
return Math.round((base + surcharge + feeShare - discount) * 100) / 100;
|
||||
};
|
||||
|
||||
return (
|
||||
<Card key={group.id} className="mb-3 fade-in">
|
||||
<Card.Header className="d-flex justify-content-between align-items-center">
|
||||
@@ -247,6 +278,9 @@ export default function OrderGroupsPage() {
|
||||
<div className="d-flex gap-2">
|
||||
{isCreator && !isOrdered && (
|
||||
<>
|
||||
<Button variant="outline-info" size="sm" onClick={() => setFeesModal(group)} title="Upravit poplatky a slevu">
|
||||
Poplatky
|
||||
</Button>
|
||||
<Button variant="outline-secondary" size="sm" onClick={() => handleToggleLock(group)} title={isLocked ? 'Odemknout' : 'Uzamknout'}>
|
||||
<FontAwesomeIcon icon={isLocked ? faLockOpen : faLock} />
|
||||
</Button>
|
||||
@@ -286,28 +320,35 @@ export default function OrderGroupsPage() {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Člen</th>
|
||||
<th style={{ width: 130 }}>Částka (Kč)</th>
|
||||
<th style={{ width: 120 }}>Částka (Kč)</th>
|
||||
<th style={{ width: 180 }}>Příplatek</th>
|
||||
<th>Poznámka</th>
|
||||
<th style={{ width: 90 }}>Celkem</th>
|
||||
<th style={{ width: 40 }}></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{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 (
|
||||
<tr key={memberLogin}>
|
||||
<td>
|
||||
<span className="user-info">
|
||||
<strong>{memberLogin}</strong>
|
||||
{memberLogin === group.creatorLogin && (
|
||||
<FontAwesomeIcon icon={faBasketShopping} className="ms-1 buyer-icon" title="Zakladatel / objednávající" />
|
||||
<OverlayTrigger placement="top" overlay={<Tooltip>Zakladatel / objednávající</Tooltip>}>
|
||||
<span className="ms-1"><FontAwesomeIcon icon={faBasketShopping} className="buyer-icon" /></span>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
{member.paid && (
|
||||
<FontAwesomeIcon icon={faCircleCheck} className="ms-1 text-success" title="Zaplaceno" />
|
||||
<OverlayTrigger placement="top" overlay={<Tooltip>Zaplaceno</Tooltip>}>
|
||||
<span className="ms-1"><FontAwesomeIcon icon={faCircleCheck} className="text-success" /></span>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
</span>
|
||||
</td>
|
||||
@@ -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}
|
||||
/>
|
||||
<Button size="sm" variant="outline-success" onClick={() => handleSaveAmount(group.id, memberLogin)}>✓</Button>
|
||||
@@ -329,22 +370,60 @@ export default function OrderGroupsPage() {
|
||||
) : (
|
||||
<span
|
||||
style={{ cursor: canEdit ? 'pointer' : undefined }}
|
||||
onClick={() => 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č` : <span className="text-muted">—</span>}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{canEdit && editingSurcharge ? (
|
||||
<div className="d-flex gap-1">
|
||||
<Form.Control
|
||||
type="text"
|
||||
size="sm"
|
||||
placeholder="popis"
|
||||
value={editSurcharges[key]?.text ?? ''}
|
||||
onChange={e => 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
|
||||
/>
|
||||
<Form.Control
|
||||
type="number"
|
||||
size="sm"
|
||||
placeholder="Kč"
|
||||
value={editSurcharges[key]?.amount ?? ''}
|
||||
onChange={e => 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 }}
|
||||
/>
|
||||
<Button size="sm" variant="outline-success" onClick={() => handleSaveSurcharge(group.id, memberLogin)}>✓</Button>
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
style={{ cursor: canEdit ? 'pointer' : undefined }}
|
||||
onClick={() => 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 ? (
|
||||
<small>{member.surchargeText ? `${member.surchargeText}: ` : ''}<strong>{member.surchargeAmount} Kč</strong></small>
|
||||
) : (
|
||||
<small className="text-muted">—</small>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td>
|
||||
{canEdit && editingNote ? (
|
||||
<div className="d-flex gap-1">
|
||||
<Form.Control
|
||||
type="text"
|
||||
size="sm"
|
||||
value={editNotes[noteKey]}
|
||||
onChange={e => 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
|
||||
/>
|
||||
<Button size="sm" variant="outline-success" onClick={() => handleSaveNote(group.id, memberLogin)}>✓</Button>
|
||||
@@ -352,13 +431,18 @@ export default function OrderGroupsPage() {
|
||||
) : (
|
||||
<span
|
||||
style={{ cursor: canEdit ? 'pointer' : undefined }}
|
||||
onClick={() => canEdit && setEditNotes(prev => ({ ...prev, [noteKey]: member.note ?? '' }))}
|
||||
onClick={() => canEdit && setEditNotes(prev => ({ ...prev, [key]: member.note ?? '' }))}
|
||||
title={canEdit ? 'Klikněte pro úpravu poznámky' : undefined}
|
||||
>
|
||||
<small className="text-muted">{member.note || '—'}</small>
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="text-end">
|
||||
<small className={memberTotal > 0 ? 'fw-bold' : 'text-muted'}>
|
||||
{memberTotal > 0 ? `${memberTotal} Kč` : '—'}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<div className="d-flex gap-1 justify-content-end">
|
||||
{canManageMembers(group) && (isCreator || memberLogin === login) && (memberLogin !== group.creatorLogin) && (
|
||||
@@ -377,6 +461,21 @@ export default function OrderGroupsPage() {
|
||||
</tbody>
|
||||
</Table>
|
||||
|
||||
{/* Souhrn poplatků a slevy */}
|
||||
{(totalFees > 0 || (group.discountValue != null && group.discountValue > 0)) && (
|
||||
<div className="px-3 py-2 border-top d-flex gap-3 flex-wrap" style={{ fontSize: '0.85em', color: 'var(--luncher-text-muted)' }}>
|
||||
{group.fees != null && group.fees > 0 && <span>Poplatky: <strong>{group.fees} Kč</strong></span>}
|
||||
{group.shipping != null && group.shipping > 0 && <span>Doprava: <strong>{group.shipping} Kč</strong></span>}
|
||||
{group.tip != null && group.tip > 0 && <span>Spropitné: <strong>{group.tip} Kč</strong></span>}
|
||||
{feeShare > 0 && <span>→ <strong>{feeShare} Kč</strong>/os.</span>}
|
||||
{group.discountValue != null && group.discountValue > 0 && (
|
||||
<span className="text-success">
|
||||
Sleva: <strong>{group.discountType === 'percent' ? `${group.discountValue}%` : `${group.discountValue} Kč`}</strong>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Časy objednání a doručení */}
|
||||
{isOrdered && (
|
||||
<div className="px-3 py-2 border-top">
|
||||
@@ -472,6 +571,21 @@ export default function OrderGroupsPage() {
|
||||
bankAccountHolder={settings.holderName}
|
||||
/>
|
||||
)}
|
||||
|
||||
{feesModal && (
|
||||
<EditGroupFeesModal
|
||||
isOpen={!!feesModal}
|
||||
onClose={() => setFeesModal(null)}
|
||||
group={feesModal}
|
||||
onSaved={newData => {
|
||||
if (newData) {
|
||||
setData(newData);
|
||||
socket.emit?.('message', newData as ClientData);
|
||||
}
|
||||
setFeesModal(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<ClientData> {
|
||||
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<ClientData> {
|
||||
const data = await getExtraData(date);
|
||||
const group = findGroup(data, groupId);
|
||||
|
||||
@@ -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 ?? {};
|
||||
|
||||
@@ -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 });
|
||||
|
||||
+11
-2
@@ -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);
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Generated
+594
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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:
|
||||
|
||||
+53
-48
@@ -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==
|
||||
|
||||
Reference in New Issue
Block a user