supersam/docs/superpowers/plans/2026-05-11-manual-delivery-...

14 KiB
Raw Permalink Blame History

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.jsx Responsible for the order group detail card, manual agreement UI, local form validation, and hiding confusing technical fields.

  • Modify: src/components/orders/OrderDetailPanel.test.jsx Server-rendered component tests for the detail card, editable controls, and visible copy.

  • Modify: src/services/supabase/orderGroupRepository.js Responsible for mapping raw order_groups rows into the frontend delivery group model and saving manual delivery choices through the Edge Function.

  • Modify: src/services/supabase/orderGroupRepository.test.js Mapping tests for missing counters, real delivery dates, and fallback behavior.

  • Modify: supabase/functions/update-order-group-delivery-choice/index.ts Server-side manual agreement validation and update logic.

  • Optional modify: src/components/UI/Select.jsx Only 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.sql Only 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 Select import from OrderDetailPanel.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_date values 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.