Files
jerapah-flow/packages/web/src/App.jsx
T
Cursor Agentandnsrb 7d51ff433f feat(workflows): add revision history, trash, UUID naming, and backup
- Store up to 50 revisions per workflow in SQLite with normalized SHA dedup
- Soft-delete workflows to trash (7-day retention) with restore and permanent purge
- Assign UUID filenames for new and duplicated workflows; show name + file in UI
- Warn on invalid YAML, unknown scripts, and plaintext secrets with save-anyway option
- Add workflow backup zip (workflows + plugins) and merge/replace restore
- Trash page, history panel on editor, and smoke test

Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
2026-08-20 02:14:50 +00:00

91 lines
3.7 KiB
React

import { useEffect } from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
import { useBootstrap, useMe } from "./api/hooks.js";
import { Layout } from "./components/Layout.jsx";
import { AuthPage } from "./pages/AuthPage.jsx";
import { HomePage } from "./pages/HomePage.jsx";
import { ScriptsPage } from "./pages/ScriptsPage.jsx";
import { ScriptDryRunPage } from "./pages/ScriptDryRunPage.jsx";
import { ScriptEditPage, ScriptNewPage } from "./pages/ScriptEditPage.jsx";
import { WorkflowsPage } from "./pages/WorkflowsPage.jsx";
import { WorkflowEditPage, WorkflowNewPage } from "./pages/WorkflowEditPage.jsx";
import { WorkflowTrashPage } from "./pages/WorkflowTrashPage.jsx";
import { EventsPage } from "./pages/EventsPage.jsx";
import { EventDetailPage } from "./pages/EventDetailPage.jsx";
import { FailuresPage } from "./pages/FailuresPage.jsx";
import { KvPage } from "./pages/KvPage.jsx";
import { AuthProfilesPage } from "./pages/AuthProfilesPage.jsx";
import { ResponsesPage } from "./pages/ResponsesPage.jsx";
import { UsersPage } from "./pages/UsersPage.jsx";
import { SecretsPage } from "./pages/SecretsPage.jsx";
import { VariablesPage } from "./pages/VariablesPage.jsx";
import { OpsPage } from "./pages/OpsPage.jsx";
export function App() {
const qc = useQueryClient();
const bootstrap = useBootstrap();
const me = useMe();
useEffect(() => {
function onUnauthorized() {
qc.setQueryData(["me"], null);
}
window.addEventListener("jerapah-flow:unauthorized", onUnauthorized);
return () => window.removeEventListener("jerapah-flow:unauthorized", onUnauthorized);
}, [qc]);
const user = me.data?.user;
const loading =
bootstrap.isLoading || (me.isLoading && !me.isError && me.data === undefined);
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<span className="loading loading-spinner loading-lg" />
</div>
);
}
if (!user) {
return <AuthPage needsSetup={Boolean(bootstrap.data?.needsSetup)} />;
}
return (
<Layout user={user}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/scripts" element={<ScriptsPage />} />
<Route path="/scripts/new" element={<ScriptNewPage />} />
<Route path="/scripts/:name/dry-run" element={<ScriptDryRunPage />} />
<Route path="/scripts/:name/edit" element={<ScriptEditPage />} />
<Route path="/workflows" element={<WorkflowsPage />} />
<Route path="/workflows/trash" element={<WorkflowTrashPage />} />
<Route path="/workflows/new" element={<WorkflowNewPage />} />
<Route path="/workflows/:owner/:file/edit" element={<WorkflowEditPage />} />
<Route path="/events" element={<EventsPage />} />
<Route path="/events/:id" element={<EventDetailPage />} />
<Route path="/failures" element={<FailuresPage />} />
<Route path="/kv" element={<KvPage />} />
<Route path="/variables" element={<VariablesPage />} />
<Route path="/auth" element={<AuthProfilesPage />} />
<Route path="/responses" element={<ResponsesPage />} />
{user.role === "admin" ? (
<>
<Route path="/users" element={<UsersPage />} />
<Route path="/secrets" element={<SecretsPage />} />
<Route path="/ops" element={<OpsPage />} />
</>
) : (
<>
<Route path="/users" element={<Navigate to="/" replace />} />
<Route path="/secrets" element={<Navigate to="/" replace />} />
<Route path="/ops" element={<Navigate to="/" replace />} />
</>
)}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</Layout>
);
}