Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 0 additions & 66 deletions .claude/TODO/2026-09-13-dead-empty-fields-branch-handlenext.md

This file was deleted.

15 changes: 7 additions & 8 deletions .claude/TODO/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Severity tiers:
- **medium**: doc drift, missing test, refactor with real cost
- **low**: nits, minor doc fixes, stylistic improvements

Total: **4 open TODOs**.
Total: **3 open TODOs**.

> **`step6`** and **`step8`** are the remaining steps from the 2026-09-30 MPNext
> playbook audit (one PR each). `held-*` items wait on an owner decision.
Expand All @@ -39,11 +39,10 @@ _none open_
| routing | security, drift | Step 6 — Headers, next.config, server-only, prerender + build in CI | [→](2026-09-30-step6-headers-csp-build-guards.md) |
| routing | security, doc | Held — CSP notes for customer-deployed clones | [→](2026-09-30-held-csp-customer-deployments.md) |

### Low (2)
### Low (1)
| Area | Tags | Title | File |
|---|---|---|---|
| components | security, bug | Step 8 — Address-label caps, error boundaries, tool-params decode, codegen escaping | [→](2026-09-30-step8-fork-specific-low-risk-items.md) |
| components | refactor, missing-test | Unreachable "empty STEP_FIELDS" branch in `GroupWizard.handleNext` | [→](2026-09-13-dead-empty-fields-branch-handlenext.md) |

---

Expand All @@ -60,11 +59,11 @@ _none open_
### drift (1)
- step6-headers-csp-build-guards — medium

### missing-test (1)
- dead-empty-fields-branch-handlenext — low
### missing-test (0)
_none open_

### refactor (1)
- dead-empty-fields-branch-handlenext — low
### refactor (0)
_none open_

### doc (1)
- held-csp-customer-deployments — medium
Expand All @@ -78,7 +77,7 @@ _none open_

| Area | Count |
|---|---|
| components | 2 |
| components | 1 |
| routing | 2 |
| services | 0 |
| auth | 0 |
Expand Down
11 changes: 3 additions & 8 deletions src/app/(web)/tools/groupwizard/group-wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,9 @@ export function GroupWizard({ params, mpTimezone }: GroupWizardProps) {
}, []);

const handleNext = useCallback(async () => {
const fields = STEP_FIELDS[currentStep];
if (!fields || fields.length === 0) {
setCompletedSteps((prev) => new Set(prev).add(currentStep));
setCurrentStep((prev) => Math.min(prev + 1, WIZARD_STEPS.length - 1));
return;
}

const isValid = await form.trigger(fields as (keyof GroupWizardFormData)[]);
// No special case for a step without fields: `trigger([])` resolves true,
// and a missing entry (`undefined`) validates the whole form — fails safe.
const isValid = await form.trigger(STEP_FIELDS[currentStep]);
if (isValid) {
setCompletedSteps((prev) => new Set(prev).add(currentStep));
setCurrentStep((prev) => Math.min(prev + 1, WIZARD_STEPS.length - 1));
Expand Down
12 changes: 12 additions & 0 deletions src/components/group-wizard/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
STEP_FIELDS,
type GroupWizardFormData,
} from './schema';
import { WIZARD_STEPS } from './types';

/** Helper: fill all required IDs + Start_Date so schema passes base validation */
function validBase(): GroupWizardFormData {
Expand Down Expand Up @@ -113,6 +114,17 @@ describe('STEP_FIELDS', () => {
expect(Object.keys(STEP_FIELDS)).toEqual(['0', '1', '2', '3', '4', '5']);
});

// GroupWizard.handleNext calls form.trigger(STEP_FIELDS[currentStep]) with no
// special case, and the last step shows Submit, not Next. So every step
// before Review must list fields to validate, or Next would skip validation.
it('every step before the Review step has fields, and only Review is empty', () => {
expect(Object.keys(STEP_FIELDS)).toHaveLength(WIZARD_STEPS.length);
WIZARD_STEPS.slice(0, -1).forEach((_, i) => {
expect(STEP_FIELDS[i].length).toBeGreaterThan(0);
});
expect(STEP_FIELDS[WIZARD_STEPS.length - 1]).toEqual([]);
});

it('STEP_FIELDS[4] includes the hyphenated Secure_Check-in field name', () => {
expect(STEP_FIELDS[4]).toContain('Secure_Check-in');
});
Expand Down
Loading