- BackendSelectionStep h2: text-2xl font-bold text-on-surface mb-2 - RemoteConfigStep h2: text-2xl font-bold text-on-surface mb-2 - DeploymentStep h2: text-2xl font-bold text-on-surface mb-2 - ReviewStep h2: text-2xl font-bold text-on-surface mb-2
158 lines
4.7 KiB
TypeScript
158 lines
4.7 KiB
TypeScript
// 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 { MD3_BTN_FILLED, MD3_BTN_OUTLINED } from '../../styles/md3-buttons';
|
||
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, dispatch } = 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]);
|
||
|
||
const showIntune = state.deployment.scriptTargets.includes('intune');
|
||
const showRmm = state.deployment.scriptTargets.includes('rmm');
|
||
|
||
// DOWN-05: ZIP bundle — filtered by scriptTargets (TECH-01)
|
||
async function handleDownloadZip() {
|
||
const files: { name: string; content: string }[] = [
|
||
{ name: 'rclone.conf', content: rcloneConf },
|
||
];
|
||
if (showIntune) {
|
||
files.push({ name: 'intune-install.ps1', content: intuneInstall });
|
||
files.push({ name: 'intune-detection.ps1', content: intuneDetection });
|
||
}
|
||
if (showRmm) {
|
||
files.push({ name: 'rmm-script.ps1', content: rmmScript });
|
||
}
|
||
await downloadZip(files, 'rclone-deployment.zip');
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
<h2 className="text-2xl font-bold text-on-surface mb-2">Step 4: Review & Download</h2>
|
||
<p className="text-sm text-on-surface-variant mt-1 mb-4">
|
||
Review your generated files and download them. Acknowledge the security notice before
|
||
copying or downloading credentials.
|
||
</p>
|
||
|
||
{/* SECU-02: client-side only notice */}
|
||
<p className="text-sm text-success bg-success/10 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-warning/10 border border-warning rounded p-4 mb-6">
|
||
<p className="text-sm text-warning font-medium mb-2">
|
||
Security warning: generated files contain credentials in plain text.
|
||
</p>
|
||
<label className="flex items-center gap-2 text-sm text-warning 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}
|
||
/>
|
||
{showIntune && (
|
||
<OutputBlock
|
||
label="Intune Install Script"
|
||
content={intuneInstall}
|
||
filename="intune-install.ps1"
|
||
disabled={!acknowledged}
|
||
/>
|
||
)}
|
||
{showIntune && (
|
||
<OutputBlock
|
||
label="Intune Detection Script"
|
||
content={intuneDetection}
|
||
filename="intune-detection.ps1"
|
||
disabled={!acknowledged}
|
||
/>
|
||
)}
|
||
{showRmm && (
|
||
<OutputBlock
|
||
label="RMM Script"
|
||
content={rmmScript}
|
||
filename="rmm-script.ps1"
|
||
disabled={!acknowledged}
|
||
/>
|
||
)}
|
||
|
||
{/* TECH-02: Back button — dispatches SET_STEP(2) */}
|
||
<div className="flex flex-col sm:flex-row gap-3 mt-6">
|
||
<button
|
||
type="button"
|
||
onClick={() => dispatch({ type: 'SET_STEP', payload: 2 })}
|
||
className={`${MD3_BTN_OUTLINED} w-full sm:w-auto`}
|
||
>
|
||
Back
|
||
</button>
|
||
</div>
|
||
|
||
{/* DOWN-05: ZIP bundle */}
|
||
<button
|
||
type="button"
|
||
disabled={!acknowledged}
|
||
onClick={handleDownloadZip}
|
||
className={`w-full mt-4 ${MD3_BTN_FILLED}`}
|
||
>
|
||
Download All (ZIP)
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default ReviewStep;
|