--- phase: 04-review-download-security plan: 03 type: tdd wave: 3 depends_on: [04-02] files_modified: - src/components/wizard/ReviewStep.tsx autonomous: true requirements: [CONF-02, CONF-03, DOWN-01, DOWN-02, DOWN-03, DOWN-04, DOWN-05, DOWN-06, SECU-01, SECU-02] must_haves: truths: - "ReviewStep renders a live preview of the generated rclone.conf content" - "A security warning checkbox must be checked before any download or copy button is enabled" - "A 'no data sent to server' notice is visible in the ReviewStep render output" - "OutputBlocks for all four generated files are shown (rclone.conf, intune-install.ps1, intune-detection.ps1, rmm-script.ps1)" - "A 'Download ZIP' button calls downloadZip with all 4 file entries" - "All test stubs from Plan 01 pass GREEN after this plan" artifacts: - path: "src/components/wizard/ReviewStep.tsx" provides: "Step 3 component — live preview + security gate + all download/copy actions" exports: ["ReviewStep"] min_lines: 60 key_links: - from: "src/components/wizard/ReviewStep.tsx" to: "src/generators/index.ts" via: "import { buildRcloneConf, buildIntuneInstall, buildIntuneDetection, buildRmmScript }" pattern: "buildRcloneConf" - from: "src/components/wizard/ReviewStep.tsx" to: "src/store/context.tsx" via: "import { useWizard }" pattern: "useWizard" - from: "src/components/wizard/ReviewStep.tsx" to: "src/components/wizard/OutputBlock.tsx" via: "import { OutputBlock }" pattern: "OutputBlock" - from: "src/components/wizard/ReviewStep.tsx" to: "src/utils/downloadZip.ts" via: "import { downloadZip }" pattern: "downloadZip" --- Implement ReviewStep.tsx: the full Phase 4 wizard step that composes all download, copy, preview, and security behaviors. This plan turns every RED stub from Plan 01 GREEN. Purpose: This is the primary deliverable of Phase 4. All eleven requirement behaviors (CONF-02, CONF-03, DOWN-01–DOWN-06, SECU-01, SECU-02) are implemented in this single component, composed from the utilities and OutputBlock built in Plan 02. Output: src/components/wizard/ReviewStep.tsx — fully implemented, all tests GREEN. @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md @.planning/phases/04-review-download-security/04-RESEARCH.md @src/store/types.ts @src/store/context.tsx @src/generators/index.ts @src/components/wizard/OutputBlock.tsx @src/utils/downloadFile.ts @src/utils/downloadZip.ts @src/components/wizard/ReviewStep.test.tsx From src/store/context.tsx: ```typescript const { state } = useWizard(); // state.remote.backendType: BackendType | null // state.remote.name: string // state.deployment: { includeInstall, configPath, scriptTargets } ``` From src/generators/index.ts (all accept WizardState, return string, buildRcloneConf throws on incomplete state): ```typescript export function buildRcloneConf(state: WizardState): string export function buildIntuneInstall(state: WizardState): string export function buildIntuneDetection(state: WizardState): string export function buildRmmScript(state: WizardState): string ``` From src/components/wizard/OutputBlock.tsx: ```typescript export interface OutputBlockProps { label: string; content: string; filename?: string; // when provided, shows Download button disabled?: boolean; // SECU-01 gate } export function OutputBlock(props: OutputBlockProps): JSX.Element ``` From src/utils/downloadZip.ts: ```typescript export async function downloadZip( files: { name: string; content: string }[], zipName: string ): Promise ``` Task 1: Implement ReviewStep.tsx — full component turning all stubs GREEN src/components/wizard/ReviewStep.tsx - CONF-02: rcloneConfContent computed via useMemo calling buildRcloneConf(state); wrapped in try/catch returning placeholder on error - CONF-03: rclone.conf OutputBlock has no filename (copy-only for live preview area); separate rclone.conf OutputBlock with filename='rclone.conf' for download - DOWN-01: OutputBlock with filename='rclone.conf' calls downloadFile(content, 'rclone.conf') on Download click - DOWN-02: OutputBlock with filename='intune-install.ps1' calls downloadFile on click - DOWN-03: OutputBlock with filename='intune-detection.ps1' calls downloadFile on click - DOWN-04: OutputBlock with filename='rmm-script.ps1' calls downloadFile on click - DOWN-05: "Download All (ZIP)" button calls downloadZip([...4 files...], 'rclone-deployment.zip') - DOWN-06: every OutputBlock's Copy button calls clipboard.writeText with that block's content - SECU-01: acknowledged useState boolean controls disabled prop on all OutputBlocks and Download ZIP button - SECU-02: a visible notice "No data is sent to any server" (or similar) is present in the render Create src/components/wizard/ReviewStep.tsx. Follow the TDD cycle: RED: Run `npm test -- src/components/wizard/ReviewStep.test.tsx` first to confirm all stubs fail. GREEN: Implement the component, run tests after each significant block. Implementation structure: ```typescript // src/components/wizard/ReviewStep.tsx import { useMemo, useState } from 'react'; import { useWizard } from '../../store/context'; import { buildRcloneConf, buildIntuneInstall, buildIntuneDetection, buildRmmScript, } from '../../generators/index'; import { OutputBlock } from './OutputBlock'; import { downloadZip } from '../../utils/downloadZip'; const PLACEHOLDER = '# Fill in the wizard steps to generate your rclone.conf'; export function ReviewStep() { const { state } = useWizard(); const [acknowledged, setAcknowledged] = useState(false); // CONF-02: live preview — updates on every state change const rcloneConf = useMemo(() => { try { return buildRcloneConf(state); } catch { return PLACEHOLDER; } }, [state]); const intuneInstall = useMemo(() => { try { return buildIntuneInstall(state); } catch { return ''; } }, [state]); const intuneDetection = useMemo(() => { try { return buildIntuneDetection(state); } catch { return ''; } }, [state]); const rmmScript = useMemo(() => { try { return buildRmmScript(state); } catch { return ''; } }, [state]); // DOWN-05: ZIP bundle — always include all 4 files async function handleDownloadZip() { await downloadZip( [ { name: 'rclone.conf', content: rcloneConf }, { name: 'intune-install.ps1', content: intuneInstall }, { name: 'intune-detection.ps1', content: intuneDetection }, { name: 'rmm-script.ps1', content: rmmScript }, ], 'rclone-deployment.zip' ); } return (
{/* SECU-02: client-side only notice */}

No data is sent to any server. All file generation happens in your browser.

{/* SECU-01: security acknowledgement gate */}

Security warning: generated files contain credentials in plain text.

{/* Output blocks — DOWN-01 through DOWN-04, CONF-03, DOWN-06 */} {/* DOWN-05: ZIP bundle */}
); } ``` After writing the file, run tests and fix any assertion mismatches: - If a test expects a specific button text, match it exactly - If a test queries by role or label, ensure the rendered output has matching accessible text - Update test stubs to real assertions as you implement (replace `expect.fail('not yet implemented')` with actual assertions matching the rendered output) PITFALL — buildRcloneConf throws on incomplete state: Always use try/catch in useMemo. Show PLACEHOLDER string when thrown. This prevents the component from crashing during navigation with partial state. PITFALL — useMemo dependency: Pass `[state]` (the full state object) as the dependency array. WizardContext re-renders on every dispatch, so this will update on every form change (satisfies CONF-02).
npm test -- src/components/wizard/ReviewStep.test.tsx 2>&1 | tail -20 All test stubs in ReviewStep.test.tsx are replaced with real assertions and pass GREEN. npm test shows 0 failing tests for this file. All pre-existing tests in other files remain GREEN.
`npm test` full suite passes GREEN. ReviewStep.test.tsx: all CONF-02, CONF-03, DOWN-01 through DOWN-06, SECU-01, SECU-02 assertions pass. reducer.test.ts SECU-03 passes. All Phase 1–3 tests still GREEN. - src/components/wizard/ReviewStep.tsx exists and exports ReviewStep - All 10 requirement behaviors tested in ReviewStep.test.tsx pass GREEN - No TypeScript errors in the component - Full test suite green After completion, create `.planning/phases/04-review-download-security/04-03-SUMMARY.md`