Files
Ready2Blob/.planning/phases/04-review-download-security/04-02-PLAN.md
T
kawaandClaude Sonnet 4.6 405bf477b4 docs(04-review-download-security): create phase 4 plan
5 plans across 5 waves: Wave 0 TDD stubs, utilities + OutputBlock,
ReviewStep implementation, App.tsx wiring, human verify checkpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 10:23:14 +01:00

9.4 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
04-review-download-security 02 execute 2
04-01
src/utils/downloadFile.ts
src/utils/downloadZip.ts
src/components/wizard/OutputBlock.tsx
true
DOWN-01
DOWN-02
DOWN-03
DOWN-04
DOWN-05
DOWN-06
CONF-03
truths artifacts key_links
downloadFile(content, filename) creates a Blob and triggers an anchor click download
downloadZip(files, zipName) builds a JSZip archive and triggers a download
OutputBlock renders pre-formatted content, a copy button, and an optional download button
OutputBlock copy button calls clipboard.writeText with block content
path provides exports
src/utils/downloadFile.ts Pure download utility — Blob + createObjectURL + anchor click
downloadFile
path provides exports
src/utils/downloadZip.ts ZIP bundle download utility using JSZip
downloadZip
path provides exports
src/components/wizard/OutputBlock.tsx Reusable code block with copy and optional download buttons
OutputBlock
OutputBlockProps
from to via pattern
src/components/wizard/OutputBlock.tsx src/utils/downloadFile import { downloadFile } downloadFile
from to via pattern
src/utils/downloadZip.ts jszip import JSZip from 'jszip' JSZip
Create the three utility/component building blocks that ReviewStep will compose: the file download helper, the ZIP bundle helper (installs jszip), and the reusable OutputBlock component.

Purpose: These are pure, independently testable pieces. Isolating them here keeps ReviewStep.tsx focused on layout and state wiring, not download mechanics. Output: src/utils/downloadFile.ts, src/utils/downloadZip.ts, src/components/wizard/OutputBlock.tsx

<execution_context> @C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md @C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/phases/04-review-download-security/04-RESEARCH.md @src/store/types.ts @src/components/wizard/ReviewStep.test.tsx

Expected by ReviewStep.test.tsx:

// src/utils/downloadFile.ts
export function downloadFile(content: string, filename: string): void

// src/utils/downloadZip.ts
export async function downloadZip(
  files: { name: string; content: string }[],
  zipName: string
): Promise<void>

// src/components/wizard/OutputBlock.tsx
export interface OutputBlockProps {
  label: string;
  content: string;
  filename?: string;     // if provided, show download button
  disabled?: boolean;    // gates both copy and download (security gate)
}
export function OutputBlock(props: OutputBlockProps): JSX.Element
Task 1: Create downloadFile.ts and downloadZip.ts utilities src/utils/downloadFile.ts, src/utils/downloadZip.ts - downloadFile creates a Blob with text/plain;charset=utf-8 MIME type, creates an object URL, clicks a hidden anchor, then revokes the URL immediately after click - downloadZip accepts an array of { name, content } entries, adds each to a JSZip instance, generates a blob asynchronously, then triggers download using the same Blob/anchor pattern - Both utilities have no React dependency (pure browser functions) First, install jszip (the only new runtime dependency this phase): ```bash npm install jszip ``` JSZip ships its own TypeScript declarations. Do NOT install @types/jszip (stale, 6 years old).
Create src/utils/downloadFile.ts:
```typescript
// src/utils/downloadFile.ts
// Triggers a browser file download from a string.
// Pattern: MDN URL.createObjectURL — https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static
export function downloadFile(content: string, filename: string): void {
  const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}
```

Create src/utils/downloadZip.ts:
```typescript
// src/utils/downloadZip.ts
// Builds a ZIP archive from file entries and triggers download.
// Pattern: JSZip official docs — https://stuk.github.io/jszip/documentation/examples/download-zip-file.html
import JSZip from 'jszip';

export async function downloadZip(
  files: { name: string; content: string }[],
  zipName: string
): Promise<void> {
  const zip = new JSZip();
  for (const { name, content } of files) {
    zip.file(name, content);
  }
  const blob = await zip.generateAsync({ type: 'blob' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = zipName;
  a.click();
  URL.revokeObjectURL(url);
}
```

Do NOT create a src/utils/index.ts barrel — ReviewStep will import directly from the specific utility files to keep the mock paths in tests predictable (the test file already uses '../../utils/downloadFile' and '../../utils/downloadZip').
npm run build 2>&1 | tail -10 Both utility files exist and TypeScript compilation succeeds with no errors. jszip appears in package.json dependencies. Task 2: Create OutputBlock.tsx reusable component src/components/wizard/OutputBlock.tsx - OutputBlock renders a labelled section with a pre/code block showing the content - A "Copy" button calls navigator.clipboard.writeText(content); button label changes to "Copied!" for 2 seconds then reverts - When filename prop is provided, a "Download" button calls downloadFile(content, filename) - When disabled prop is true, both Copy and Download buttons are disabled (HTML disabled attribute) — this is the SECU-01 gate applied per-block - Copy and Download buttons are always rendered (never conditionally hidden) — disabled state is visual only Create src/components/wizard/OutputBlock.tsx:
```typescript
// src/components/wizard/OutputBlock.tsx
// Reusable output block: pre-formatted content + copy button + optional download button.
// disabled prop gates both actions (SECU-01 security acknowledgement gate).
import { useState } from 'react';
import { downloadFile } from '../../utils/downloadFile';

export interface OutputBlockProps {
  label: string;
  content: string;
  filename?: string;
  disabled?: boolean;
}

export function OutputBlock({ label, content, filename, disabled = false }: OutputBlockProps) {
  const [copied, setCopied] = useState(false);

  async function handleCopy() {
    await navigator.clipboard.writeText(content);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  }

  return (
    <div className="mb-6">
      <div className="flex items-center justify-between mb-1">
        <span className="text-sm font-medium text-gray-700">{label}</span>
        <div className="flex gap-2">
          <button
            type="button"
            disabled={disabled}
            onClick={handleCopy}
            className="text-xs px-2 py-1 rounded border border-gray-300 disabled:opacity-40"
          >
            {copied ? 'Copied!' : 'Copy'}
          </button>
          {filename && (
            <button
              type="button"
              disabled={disabled}
              onClick={() => downloadFile(content, filename)}
              className="text-xs px-2 py-1 rounded border border-gray-300 disabled:opacity-40"
            >
              Download
            </button>
          )}
        </div>
      </div>
      <pre className="bg-gray-900 text-gray-100 rounded p-4 text-xs overflow-x-auto whitespace-pre-wrap">
        <code>{content}</code>
      </pre>
    </div>
  );
}
```

Styling uses Tailwind classes consistent with the existing wizard components. The `disabled:opacity-40` class communicates the locked state visually without hiding buttons.
npm test 2>&1 | tail -15 OutputBlock.tsx exists and compiles cleanly. npm test still shows ReviewStep.test.tsx as RED (component does not exist yet) and all other tests GREEN. No new TypeScript errors. `npm run build` passes. `npm test` shows: reducer.test.ts GREEN, ReviewStep.test.tsx still RED (ReviewStep not yet created — expected), all prior phase tests GREEN.

<success_criteria>

  • src/utils/downloadFile.ts exports downloadFile function
  • src/utils/downloadZip.ts exports downloadZip async function
  • src/components/wizard/OutputBlock.tsx exports OutputBlock and OutputBlockProps
  • jszip listed in package.json dependencies
  • TypeScript compilation clean </success_criteria>
After completion, create `.planning/phases/04-review-download-security/04-02-SUMMARY.md`