first commit
CI/CD / CI · Admin API (dotnet build) (push) Successful in 41s
CI/CD / CI · Admin Web (tsc) (push) Failing after 5s
CI/CD / CI · Website (tsc) (push) Failing after 4s
CI/CD / CI · Koja (tsc) (push) Failing after 5s
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m13s
CI/CD / CI · Dashboard (tsc) (push) Failing after 2m32s
CI/CD / Deploy · all services (push) Has been skipped

This commit is contained in:
soroush.asadi
2026-05-31 11:06:24 +03:30
parent 51e422272d
commit 345ae0a4b5
69 changed files with 11964 additions and 152 deletions
@@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslations } from "next-intl";
import * as signalR from "@microsoft/signalr";
import { apiGet, apiPatch, apiPost, ApiClientError } from "@/lib/api/client";
import { apiGet, apiPost, ApiClientError } from "@/lib/api/client";
import { printErrorMessage, printReceipt } from "@/lib/api/print";
import { requestPosPayment, posDeviceErrorMessage } from "@/lib/api/pos-device";
import { PosSlipModal } from "@/components/pos/pos-slip-modal";
@@ -54,6 +54,7 @@ export function PosPayPanel({ cafeId, numberLocale, branchId = null }: PosPayPan
const [search, setSearch] = useState("");
const [debouncedSearch, setDebouncedSearch] = useState("");
const [payMessage, setPayMessage] = useState<string | null>(null);
const [cancelReason, setCancelReason] = useState("");
const [receiptOrder, setReceiptOrder] = useState<Order | null>(null);
const printSettingsBranchId = receiptOrder?.branchId ?? branchId ?? null;
const [lastPaidOrderId, setLastPaidOrderId] = useState<string | null>(null);
@@ -167,6 +168,7 @@ export function PosPayPanel({ cafeId, numberLocale, branchId = null }: PosPayPan
useEffect(() => {
setLoyaltyRedeem(0);
setCancelReason("");
}, [selected?.id]);
useEffect(() => {
@@ -246,23 +248,31 @@ export function PosPayPanel({ cafeId, numberLocale, branchId = null }: PosPayPan
});
const cancelOrder = useMutation({
mutationFn: (orderId: string) =>
apiPatch(`/api/cafes/${cafeId}/orders/${orderId}/status`, {
status: "Cancelled",
mutationFn: ({ orderId, reason }: { orderId: string; reason: string }) =>
apiPost(`/api/cafes/${cafeId}/orders/${orderId}/cancel`, {
reason: reason.trim() || undefined,
}),
onSuccess: () => {
setPayMessage(t("cancelOrderSuccess"));
setCancelReason("");
setSelectedId(null);
setSelectedTableId(null);
setFilterTableId(null);
setPaymentRows([{ method: "Cash", amount: "" }]);
queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
queryClient.invalidateQueries({ queryKey: ["orders-live", cafeId] });
queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
},
onError: (err) => {
setPayMessage(
err instanceof ApiClientError ? err.message : t("cancelOrderError")
);
if (err instanceof ApiClientError) {
if (err.code === "ORDER_HAS_PAYMENTS") {
setPayMessage(t("cancelOrderHasPayments"));
return;
}
setPayMessage(err.message || t("cancelOrderError"));
return;
}
setPayMessage(t("cancelOrderError"));
},
});
@@ -596,6 +606,13 @@ export function PosPayPanel({ cafeId, numberLocale, branchId = null }: PosPayPan
>
{t("previewBill")}
</Button>
<Input
value={cancelReason}
onChange={(e) => setCancelReason(e.target.value)}
placeholder={t("cancelReasonPlaceholder")}
className="h-9"
maxLength={500}
/>
<Button
type="button"
variant="outline"
@@ -609,7 +626,7 @@ export function PosPayPanel({ cafeId, numberLocale, branchId = null }: PosPayPan
confirmLabel: t("cancelOrder"),
});
if (!ok) return;
cancelOrder.mutate(selected.id);
cancelOrder.mutate({ orderId: selected.id, reason: cancelReason });
}}
>
{cancelOrder.isPending ? "..." : t("cancelOrder")}