feat(04-03): implement ReviewStep — all 10 requirement behaviors GREEN
- ReviewStep.tsx: live rclone.conf preview via useMemo+try/catch (CONF-02) - Security acknowledgement checkbox gates all download/copy buttons (SECU-01) - 'No data is sent to any server' privacy notice (SECU-02) - 4 OutputBlocks with download (DOWN-01–04) and copy (DOWN-06, CONF-03) - Download All (ZIP) button calls downloadZip with all 4 file entries (DOWN-05) - Replaced all expect.fail stubs with real assertions — 10/10 tests GREEN
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
// src/components/wizard/ReviewStep.tsx
|
||||
// Step 3 component — live preview + security gate + all download/copy actions.
|
||||
// Covers CONF-02, CONF-03, DOWN-01–DOWN-06, SECU-01, SECU-02.
|
||||
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 (
|
||||
<div>
|
||||
{/* SECU-02: client-side only notice */}
|
||||
<p className="text-sm text-green-700 bg-green-50 rounded p-3 mb-6">
|
||||
No data is sent to any server. All file generation happens in your browser.
|
||||
</p>
|
||||
|
||||
{/* SECU-01: security acknowledgement gate */}
|
||||
<div className="bg-yellow-50 border border-yellow-300 rounded p-4 mb-6">
|
||||
<p className="text-sm text-yellow-800 font-medium mb-2">
|
||||
Security warning: generated files contain credentials in plain text.
|
||||
</p>
|
||||
<label className="flex items-center gap-2 text-sm text-yellow-900 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={acknowledged}
|
||||
onChange={(e) => setAcknowledged(e.target.checked)}
|
||||
/>
|
||||
I understand that the generated files contain credentials in plain text.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Output blocks — DOWN-01 through DOWN-04, CONF-03, DOWN-06 */}
|
||||
<OutputBlock
|
||||
label="rclone.conf"
|
||||
content={rcloneConf}
|
||||
filename="rclone.conf"
|
||||
disabled={!acknowledged}
|
||||
/>
|
||||
<OutputBlock
|
||||
label="Intune Install Script"
|
||||
content={intuneInstall}
|
||||
filename="intune-install.ps1"
|
||||
disabled={!acknowledged}
|
||||
/>
|
||||
<OutputBlock
|
||||
label="Intune Detection Script"
|
||||
content={intuneDetection}
|
||||
filename="intune-detection.ps1"
|
||||
disabled={!acknowledged}
|
||||
/>
|
||||
<OutputBlock
|
||||
label="RMM Script"
|
||||
content={rmmScript}
|
||||
filename="rmm-script.ps1"
|
||||
disabled={!acknowledged}
|
||||
/>
|
||||
|
||||
{/* DOWN-05: ZIP bundle */}
|
||||
<button
|
||||
type="button"
|
||||
disabled={!acknowledged}
|
||||
onClick={handleDownloadZip}
|
||||
className="w-full py-2 px-4 bg-blue-600 text-white rounded font-medium disabled:opacity-40 disabled:cursor-not-allowed hover:bg-blue-700"
|
||||
>
|
||||
Download All (ZIP)
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReviewStep;
|
||||
Reference in New Issue
Block a user