fix: логисты могут ставить прошедшие даты + dedup триггер
This commit is contained in:
parent
052e5e7e86
commit
48e238e634
|
|
@ -85,6 +85,9 @@ export const AppShell = ({
|
|||
<Button variant="ghost" className="mb-2 w-full justify-start" onClick={() => navigate("/settings")}>
|
||||
Настройки
|
||||
</Button>
|
||||
<Button variant="ghost" className="mb-2 w-full justify-start" onClick={() => window.open("https://forms.gle/feedback-supersam", "_blank")}>
|
||||
💡 Предложить улучшение
|
||||
</Button>
|
||||
<Button variant="ghost" className="w-full justify-start" onClick={onSignOut}>
|
||||
Выйти
|
||||
</Button>
|
||||
|
|
@ -124,6 +127,9 @@ export const AppShell = ({
|
|||
<Button size="sm" variant="ghost" onClick={() => navigate("/settings")} aria-label="Настройки">
|
||||
⚙
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={() => window.open("https://forms.gle/feedback-supersam", "_blank")} aria-label="Предложить улучшение">
|
||||
💡
|
||||
</Button>
|
||||
<ThemeToggle />
|
||||
<Button size="sm" variant="ghost" onClick={onSignOut}>
|
||||
Выйти
|
||||
|
|
@ -194,6 +200,9 @@ export const AppShell = ({
|
|||
<Button size="sm" variant="ghost" onClick={() => navigate("/settings")} aria-label="Настройки">
|
||||
⚙
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={() => window.open("https://forms.gle/feedback-supersam", "_blank")} aria-label="Предложить улучшение">
|
||||
💡
|
||||
</Button>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -722,7 +722,8 @@ export const OrderDetailPanel = ({
|
|||
return;
|
||||
}
|
||||
|
||||
if (!isFutureDeliveryDate(effectiveDate)) {
|
||||
const canUsePastDate = ["logistician", "admin", "mega_admin", "manager"].includes(userRole);
|
||||
if (!canUsePastDate && !isFutureDeliveryDate(effectiveDate)) {
|
||||
setFormMessage(deliveryType === "pickup" ? "Выберите дату самовывоза позже сегодняшнего дня." : "Выберите дату доставки позже сегодняшнего дня.");
|
||||
return;
|
||||
}
|
||||
|
|
@ -1337,7 +1338,7 @@ export const OrderDetailPanel = ({
|
|||
<CollapsibleOrderComposition order={order} />
|
||||
</Panel>
|
||||
|
||||
{userRole !== "driver" && order?.deliveryLink ? (
|
||||
{order?.deliveryLink ? (
|
||||
<Panel className="space-y-3 p-5">
|
||||
<strong>Ссылка на согласование</strong>
|
||||
<p className="text-sm text-[var(--color-text-muted)]">
|
||||
|
|
|
|||
|
|
@ -261,6 +261,23 @@ export const SmsStatusCard = ({ order, userRole }) => {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Client page access info */}
|
||||
{(order.invitationAccessCount > 0 || order.invitationOpenedAt) ? (
|
||||
<div className="mt-2 flex items-center gap-2 rounded-lg bg-[var(--color-surface-strong)] px-2 py-1.5 text-[11px]">
|
||||
<span className="text-[var(--color-text-muted)]">👁 Клиент открывал страницу согласования</span>
|
||||
<span className="font-medium text-[var(--color-text)]">{order.invitationAccessCount || 1} раз</span>
|
||||
{(order.invitationLastAccessedAt || order.invitationOpenedAt) && (
|
||||
<span className="text-[var(--color-text-muted)]">
|
||||
· последний: {fmtTime(order.invitationLastAccessedAt || order.invitationOpenedAt)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-2 flex items-center gap-2 rounded-lg bg-[var(--color-surface-strong)] px-2 py-1.5 text-[11px] text-[var(--color-text-muted)]">
|
||||
📭 Клиент ещё не открывал страницу согласования
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Restart buttons */}
|
||||
{canManage && (
|
||||
<div className="mt-3 flex gap-2 border-t border-[var(--color-border)] pt-3">
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const StatusActionPanel = ({
|
|||
{ value: "pending_confirmation", label: "Ожидает согласования" },
|
||||
{ value: "agreed", label: "Согласовано" },
|
||||
{ value: "driver_assigned", label: "Назначен водитель" },
|
||||
{ value: "picked_up", label: "Вывезено", primary: true, requiresSchedule: true },
|
||||
{ value: "picked_up", label: "Вывезено", primary: true, requiresSchedule: true, requiresDriver: false },
|
||||
{ value: "delivered", label: "Доставлено", mismatch: true, requiresSchedule: true },
|
||||
{ value: "requires_address", label: "Требуется адрес" },
|
||||
{ value: "problem", label: "Проблема" },
|
||||
|
|
@ -52,7 +52,7 @@ const StatusActionPanel = ({
|
|||
{ value: "pending_confirmation", label: "Ожидает согласования" },
|
||||
{ value: "agreed", label: "Согласовано" },
|
||||
{ value: "driver_assigned", label: "Назначен водитель" },
|
||||
{ value: "delivered", label: "Доставлено", primary: true, requiresSchedule: true },
|
||||
{ value: "delivered", label: "Доставлено", primary: true, requiresSchedule: true, requiresDriver: true },
|
||||
{ value: "picked_up", label: "Вывезено", mismatch: true, requiresSchedule: true },
|
||||
{ value: "requires_address", label: "Требуется адрес" },
|
||||
{ value: "problem", label: "Проблема" },
|
||||
|
|
@ -79,7 +79,7 @@ const StatusActionPanel = ({
|
|||
{!hasDeliverySchedule && (
|
||||
<Badge tone="warning">⚠ Дата не указана</Badge>
|
||||
)}
|
||||
{!hasDriver && (
|
||||
{!hasDriver && !isPickup && (
|
||||
<Badge tone="warning">⚠ Водитель не назначен</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -88,6 +88,7 @@ const StatusActionPanel = ({
|
|||
{allStatuses.map((statusOption) => {
|
||||
const isCurrent = currentStatus === statusOption.value;
|
||||
const blockedBySchedule = statusOption.requiresSchedule && !hasDeliverySchedule;
|
||||
const blockedByDriver = statusOption.requiresDriver !== false && !hasDriver;
|
||||
const hint = getHint(statusOption.value);
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -97,6 +98,13 @@ const StatusActionPanel = ({
|
|||
if (blockedBySchedule) {
|
||||
return;
|
||||
}
|
||||
if (blockedByDriver) {
|
||||
onConfirmStatus?.({
|
||||
type: "hint",
|
||||
hint: "⚠ Назначьте водителя перед статусом «Доставлено»",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (isCurrent && hint) {
|
||||
onConfirmStatus?.({ type: "hint", hint });
|
||||
return;
|
||||
|
|
@ -110,7 +118,7 @@ const StatusActionPanel = ({
|
|||
});
|
||||
}}
|
||||
disabled={isSavingStatusChange}
|
||||
className={blockedBySchedule ? "opacity-40 cursor-not-allowed" : ""}
|
||||
className={(blockedBySchedule || blockedByDriver) ? "opacity-40 cursor-not-allowed" : ""}
|
||||
>
|
||||
{statusOption.label}
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue