vanta-auditor-tui
Version:
Beautiful terminal UI for exporting Vanta audit evidence with ZIP support and progress tracking
83 lines • 6.92 kB
JavaScript
import React, { useMemo, useState } from "react";
import { Box, Text, useInput } from "ink";
import { SelectInput, TextInput } from "../lib/inkModules.js";
import { theme } from "../theme.js";
import { FormSection } from "./ui/FormSection.js";
import path from "node:path";
export function ExportOptions({ auditId, onSubmit }) {
const [structure, setStructure] = useState("single");
const [prefix, setPrefix] = useState("CSP-001");
const [createZip, setCreateZip] = useState(true);
const defaultDir = useMemo(() => {
const now = new Date();
const stamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
return path.resolve(process.cwd(), "exports", `${auditId}-${stamp}`);
}, [auditId]);
const [dir, setDir] = useState(defaultDir);
const [zipName, setZipName] = useState(`audit-${auditId}.zip`);
const [focusedInput, setFocusedInput] = useState("dir");
// Navigate between inputs with Tab
useInput((input, key) => {
if (key.tab) {
const inputs = ["dir", "structure"];
if (structure === "separate")
inputs.push("prefix");
inputs.push("zip");
if (createZip)
inputs.push("zipName");
inputs.push("submit");
const currentIndex = inputs.indexOf(focusedInput);
const nextIndex = key.shift
? (currentIndex - 1 + inputs.length) % inputs.length
: (currentIndex + 1) % inputs.length;
setFocusedInput(inputs[nextIndex]);
}
});
return (React.createElement(Box, { flexDirection: "column" },
React.createElement(FormSection, { title: "\uD83D\uDCC1 Export Configuration", subtitle: "Configure how your audit evidence will be exported" },
React.createElement(Box, null,
React.createElement(Text, { color: theme.colors.primaryLight }, "Output directory:"),
React.createElement(Box, { borderStyle: focusedInput === "dir" ? "double" : "single", borderColor: focusedInput === "dir" ? theme.colors.primary : theme.colors.border, paddingX: 1, marginTop: 1 }, focusedInput === "dir" ? (React.createElement(TextInput, { value: dir, onChange: setDir, onSubmit: () => setFocusedInput("structure") })) : (React.createElement(Text, null, dir)))),
React.createElement(Box, { flexDirection: "column", marginTop: 2 },
React.createElement(Text, { color: theme.colors.primaryLight }, "Folder structure:"),
React.createElement(Box, { borderStyle: focusedInput === "structure" ? "double" : "single", borderColor: focusedInput === "structure" ? theme.colors.primary : theme.colors.border, paddingX: 1, paddingY: 1, marginTop: 1 }, focusedInput === "structure" ? (React.createElement(SelectInput, { items: [
{ label: "Single folder (all files together)", value: "single" },
{ label: "Separate folder per evidence", value: "separate" }
], onSelect: (i) => {
setStructure(i.value);
setFocusedInput(structure === "single" && i.value === "separate" ? "prefix" : "zip");
} })) : (React.createElement(Text, null, structure === "single"
? "Single folder (all files together)"
: "Separate folder per evidence")))),
structure === "separate" && (React.createElement(Box, { marginTop: 2 },
React.createElement(Text, { color: theme.colors.primaryLight }, "Folder prefix:"),
React.createElement(Box, { borderStyle: focusedInput === "prefix" ? "double" : "single", borderColor: focusedInput === "prefix" ? theme.colors.primary : theme.colors.border, paddingX: 1, marginLeft: 1 }, focusedInput === "prefix" ? (React.createElement(TextInput, { value: prefix, onChange: setPrefix, onSubmit: () => setFocusedInput("zip") })) : (React.createElement(Text, null, prefix))))),
React.createElement(Box, { flexDirection: "column", marginTop: 2 },
React.createElement(Text, { color: theme.colors.primaryLight }, "Create ZIP archive:"),
React.createElement(Box, { borderStyle: focusedInput === "zip" ? "double" : "single", borderColor: focusedInput === "zip" ? theme.colors.primary : theme.colors.border, paddingX: 1, paddingY: 1, marginTop: 1 }, focusedInput === "zip" ? (React.createElement(SelectInput, { items: [
{ label: "Yes - Create a ZIP file", value: "yes" },
{ label: "No - Keep files in folder", value: "no" }
], onSelect: (i) => {
setCreateZip(i.value === "yes");
setFocusedInput(i.value === "yes" ? "zipName" : "submit");
} })) : (React.createElement(Text, null, createZip ? "Yes - Create a ZIP file" : "No - Keep files in folder")))),
createZip && (React.createElement(Box, { marginTop: 2 },
React.createElement(Text, { color: theme.colors.primaryLight }, "ZIP filename:"),
React.createElement(Box, { borderStyle: focusedInput === "zipName" ? "double" : "single", borderColor: focusedInput === "zipName" ? theme.colors.primary : theme.colors.border, paddingX: 1, marginLeft: 1 }, focusedInput === "zipName" ? (React.createElement(TextInput, { value: zipName, onChange: setZipName, onSubmit: () => setFocusedInput("submit") })) : (React.createElement(Text, null, zipName))))),
React.createElement(Box, { marginTop: 2 },
React.createElement(Text, { color: theme.colors.text, dimColor: true }, "\uD83D\uDCA1 Use Tab/Shift+Tab to navigate between fields")),
React.createElement(Box, { marginTop: 1, borderStyle: focusedInput === "submit" ? "double" : "single", borderColor: focusedInput === "submit" ? theme.colors.success : theme.colors.border, paddingX: 2, paddingY: 1 },
React.createElement(Text, { color: theme.colors.success, bold: true }, focusedInput === "submit"
? "✨ Press Enter to start downloading evidence"
: "Navigate here and press Enter to start")),
focusedInput === "submit" && (React.createElement(Box, { marginTop: 1 },
React.createElement(TextInput, { value: "", onChange: () => { }, onSubmit: () => onSubmit({
outputDir: dir,
structure,
folderPrefix: structure === "separate" ? prefix : undefined,
createZip,
zipName: createZip ? zipName : undefined
}), focus: true }))))));
}
export default ExportOptions;
//# sourceMappingURL=ExportOptions.js.map