14 KiB
Manual Delivery Agreement Implementation Plan
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make manager/logistician manual delivery agreement safe and clear: only future delivery dates are allowed, controls match the app theme, order group counters are correct, and confusing technical fields are removed from the card.
Architecture: Keep the workflow centered on order_groups. The UI validates future dates before submit, the Edge Function enforces the same rule server-side, and the repository maps partial order_groups rows into a clean view model for dashboards and cards.
Tech Stack: React 18, Vite, Vitest, Supabase JS, Supabase Edge Functions in Deno, Tailwind utility classes with CSS variables.
File Structure
-
Modify:
src/components/orders/OrderDetailPanel.jsxResponsible for the order group detail card, manual agreement UI, local form validation, and hiding confusing technical fields. -
Modify:
src/components/orders/OrderDetailPanel.test.jsxServer-rendered component tests for the detail card, editable controls, and visible copy. -
Modify:
src/services/supabase/orderGroupRepository.jsResponsible for mapping raworder_groupsrows into the frontend delivery group model and saving manual delivery choices through the Edge Function. -
Modify:
src/services/supabase/orderGroupRepository.test.jsMapping tests for missing counters, real delivery dates, and fallback behavior. -
Modify:
supabase/functions/update-order-group-delivery-choice/index.tsServer-side manual agreement validation and update logic. -
Optional modify:
src/components/UI/Select.jsxOnly touch if other select controls still need a global design correction after the manual agreement block switches to app-styled buttons. -
Optional modify:
docs/sql/order-groups-manual-delivery-choice.sqlOnly touch if database constraints are added later. Current requirement can be enforced in the Edge Function.
Chunk 1: Data Mapping Correctness
Task 1: Stop Showing Fake Delivery Dates
Files:
-
Modify:
src/services/supabase/orderGroupRepository.js -
Test:
src/services/supabase/orderGroupRepository.test.js -
Step 1: Write the failing mapping test
Add a case where customer_date exists but delivery_date is null.
const group = mapOrderGroupRowToDeliveryGroup({
id: "group-without-delivery-date",
group_key: "9781632663|28.04.26",
customer_date: "28.04.26",
order_numbers: ["СФ Т\\ЕА-26979"],
status: "ready_for_notification",
delivery_status: "pending_confirmation",
created_at: "2026-05-05 09:43:53.750061+00",
updated_at: "2026-05-05 09:43:53.750061+00",
});
expect(group.customerDate).toBe("28.04.26");
expect(group.deliveryDate).toBe("");
- Step 2: Run the focused test
Run: npm test -- --run src/services/supabase/orderGroupRepository.test.js
Expected before implementation: FAIL because deliveryDate is incorrectly filled from customerDate.
- Step 3: Implement the mapping fix
In mapOrderGroupRowToDeliveryGroup, set:
const deliveryDate = normalizeText(row.delivery_date);
Do not fall back to customerDate for actual delivery agreement data.
- Step 4: Run the focused test
Run: npm test -- --run src/services/supabase/orderGroupRepository.test.js
Expected: PASS.
Task 2: Infer Counters When order_groups Counter Columns Are Empty
Files:
-
Modify:
src/services/supabase/orderGroupRepository.js -
Test:
src/services/supabase/orderGroupRepository.test.js -
Step 1: Write the failing counter test
Use a real-shaped row where order_numbers has values, but orders_count, ready_count, and not_ready_count are missing.
const group = mapOrderGroupRowToDeliveryGroup({
id: "group-without-counters",
group_key: "9781632663|28.04.26",
order_numbers: ["СФ Т\\ЕА-26979"],
status: "ready_for_notification",
delivery_status: "pending_confirmation",
created_at: "2026-05-05 09:43:53.750061+00",
updated_at: "2026-05-05 09:43:53.750061+00",
});
expect(group.ordersCount).toBe(1);
expect(group.readyCount).toBe(1);
expect(group.notReadyCount).toBe(0);
- Step 2: Run the focused test
Run: npm test -- --run src/services/supabase/orderGroupRepository.test.js
Expected before implementation: FAIL with 0 counters.
- Step 3: Implement fallback counters
Use order_numbers.length as a fallback for total count. For status === "ready_for_notification", infer readyCount as ordersCount when explicit ready counters are absent.
const orderNumbers = toStringArray(row.order_numbers);
const inferredOrderCount = orderNumbers.length;
const ordersCount = toNumber(row.orders_count ?? row.orders_total ?? row.legacy_orders_total, inferredOrderCount);
const readyCount = toNumber(
row.ready_count ?? row.orders_ready ?? row.legacy_orders_ready,
row.status === "ready_for_notification" ? ordersCount : 0,
);
const notReadyCount = toNumber(
row.not_ready_count ?? row.orders_not_ready ?? row.legacy_orders_not_ready,
Math.max(ordersCount - readyCount, 0),
);
- Step 4: Run mapping tests
Run: npm test -- --run src/services/supabase/orderGroupRepository.test.js
Expected: PASS.
Chunk 2: Manual Agreement UI
Task 3: Replace Native Date Input With Themed Future-Date Picker
Files:
-
Modify:
src/components/orders/OrderDetailPanel.jsx -
Test:
src/components/orders/OrderDetailPanel.test.jsx -
Step 1: Add date helper functions
Add local helpers near normalizeDateForInput:
const toDateKey = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const addDays = (date, amount) => {
const nextDate = new Date(date);
nextDate.setDate(nextDate.getDate() + amount);
return nextDate;
};
const getTomorrowDateKey = () => toDateKey(addDays(new Date(), 1));
const isFutureDeliveryDate = (value) => Boolean(value) && value >= getTomorrowDateKey();
- Step 2: Default the editable form to tomorrow
When the selected group has no valid future deliveryDate, initialize the manual form with tomorrow.
const normalizedDeliveryDate = normalizeDateForInput(order?.deliveryDate);
setDeliveryDate(isFutureDeliveryDate(normalizedDeliveryDate) ? normalizedDeliveryDate : getTomorrowDateKey());
- Step 3: Replace
<Input type="date">
Render a styled button that opens a compact 21-day date grid. The grid should use app CSS variables: --color-card, --color-surface, --color-border, --color-accent, --color-accent-soft, --color-text, and --color-text-muted.
- Step 4: Test editable controls
Update OrderDetailPanel.test.jsx so editable markup includes:
expect(editableMarkup).toContain("Ближайшие даты");
expect(editableMarkup).toContain("Согласовать");
- Step 5: Run component test
Run: npm test -- --run src/components/orders/OrderDetailPanel.test.jsx
Expected: PASS.
Task 4: Replace Native Time Select With Themed Segmented Buttons
Files:
-
Modify:
src/components/orders/OrderDetailPanel.jsx -
Test:
src/components/orders/OrderDetailPanel.test.jsx -
Step 1: Remove
Selectimport fromOrderDetailPanel.jsx
The manual agreement block should no longer use the native dropdown.
- Step 2: Render time options as buttons
Use DELIVERY_TIME_OPTIONS:
const DELIVERY_TIME_OPTIONS = ["Первая половина дня", "Вторая половина дня"];
Each option should be a button type="button" with aria-pressed={deliveryTime === option} and selected styling through app CSS variables.
- Step 3: Ensure mobile layout is comfortable
Use a responsive grid:
<div className="grid gap-2 sm:grid-cols-2">
- Step 4: Run component test
Run: npm test -- --run src/components/orders/OrderDetailPanel.test.jsx
Expected: PASS.
Chunk 3: Validation And Server Enforcement
Task 5: Block Today And Past Dates In The UI
Files:
-
Modify:
src/components/orders/OrderDetailPanel.jsx -
Test:
src/components/orders/OrderDetailPanel.test.jsx -
Step 1: Add submit validation
Before calling onSaveManualDeliveryChoice, check:
if (!isFutureDeliveryDate(deliveryDate)) {
setFormMessage("Выберите дату доставки позже сегодняшнего дня.");
return;
}
- Step 2: Add a client-side interaction test if the test setup supports events
If this component is only tested with renderToStaticMarkup, keep validation covered by a server-side Edge Function test/check instead. Do not add a brittle DOM test just to satisfy coverage.
- Step 3: Run component tests
Run: npm test -- --run src/components/orders/OrderDetailPanel.test.jsx
Expected: PASS.
Task 6: Enforce Future Dates In Edge Function
Files:
-
Modify:
supabase/functions/update-order-group-delivery-choice/index.ts -
Step 1: Add date comparison helpers
const getTodayKey = () => new Date().toISOString().slice(0, 10);
const isFutureDeliveryDate = (value: string) => isValidDate(value) && value > getTodayKey();
- Step 2: Replace date validation
Change:
if (!isValidDate(deliveryDate)) {
return jsonResponse({ ok: false, error: "Valid deliveryDate is required" }, 400, corsHeaders);
}
to:
if (!isFutureDeliveryDate(deliveryDate)) {
return jsonResponse({ ok: false, error: "Future deliveryDate is required" }, 400, corsHeaders);
}
- Step 3: Run Deno check
Run: deno check supabase/functions/update-order-group-delivery-choice/index.ts
Expected: PASS.
- Step 4: Deploy function after local verification
Run when ready:
supabase functions deploy update-order-group-delivery-choice
Expected: deployed function rejects today/past dates even if someone bypasses the UI.
Chunk 4: Detail Card Cleanup
Task 7: Remove Confusing Legacy Fields From The Card
Files:
-
Modify:
src/components/orders/OrderDetailPanel.jsx -
Test:
src/components/orders/OrderDetailPanel.test.jsx -
Step 1: Change empty value wording
Use Нет данных for generic missing data instead of Не указано, except for binary fields where the user expects Да or Нет.
const renderValue = (value) => value || "Нет данных";
- Step 2: Show SMS as a binary value
Change the SMS field:
<p className="mt-1 font-medium">{order.smsSentAt ? "Да" : "Нет"}</p>
- Step 3: Hide legacy customer
Remove the visible Клиент из старых данных field from the card.
- Step 4: Hide empty technical fields
Only render Создано из обмена and Ключ источника when values exist. Do not show Нет данных for these technical fields.
- Step 5: Run component tests
Run: npm test -- --run src/components/orders/OrderDetailPanel.test.jsx
Expected: PASS.
Chunk 5: Verification
Task 8: Run Focused Tests
Files:
-
Test:
src/components/orders/OrderDetailPanel.test.jsx -
Test:
src/services/supabase/orderGroupRepository.test.js -
Test:
src/pages/DashboardPage.test.jsx -
Step 1: Run focused frontend tests
Run:
npm test -- --run src/components/orders/OrderDetailPanel.test.jsx src/services/supabase/orderGroupRepository.test.js src/pages/DashboardPage.test.jsx
Expected: PASS.
- Step 2: Run Edge Function type check
Run:
deno check supabase/functions/update-order-group-delivery-choice/index.ts
Expected: PASS.
- Step 3: Run production build
Run:
npm run build
Expected: PASS.
Task 9: Manual Browser Verification
Files:
-
Manual check:
http://localhost:5174/dashboard -
Step 1: Open an order group as manager/logistician
Expected: card shows order counters based on available orders, not misleading 0/0 when order_numbers has values.
- Step 2: Check manual agreement block
Expected: date picker starts from tomorrow, not today or an old customer date.
- Step 3: Select date and half-day
Expected: controls visually match dark/light theme, no native browser dropdown styling dominates the UI.
- Step 4: Save manual agreement
Expected: valid future date saves; today/past date cannot be sent from UI.
- Step 5: Check additional data block
Expected: SMS отправлено shows Да or Нет; no Клиент из старых данных; empty technical fields are hidden.
Commit Plan
- Commit 1: Data mapping
git add src/services/supabase/orderGroupRepository.js src/services/supabase/orderGroupRepository.test.js
git commit -m "fix(order-groups): normalize delivery group counters"
- Commit 2: Manual agreement UI
git add src/components/orders/OrderDetailPanel.jsx src/components/orders/OrderDetailPanel.test.jsx
git commit -m "feat(order-groups): improve manual delivery agreement"
- Commit 3: Edge Function validation
git add supabase/functions/update-order-group-delivery-choice/index.ts
git commit -m "fix(edge): require future delivery dates"
Rollout Notes
- The frontend change is immediate after deploy.
- The Edge Function must be deployed separately with
supabase functions deploy update-order-group-delivery-choice. - Existing rows with old
delivery_datevalues will still contain those dates in the database. This plan prevents new manual agreements from writing today or past dates. - Temporary open RLS used for testing should be revisited before production hardening.