initial commit after split

This commit is contained in:
2026-06-24 01:43:27 +02:00
parent 2406e70597
commit 7f25a3ccdf
125 changed files with 25551 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
import { useEffect, useState } from "react";
import { Navigate, Route, Routes, useLocation, useNavigate, useParams } from "react-router-dom";
import type { ApiSettings, AuthInfo, CampaignWorkspaceSection } from "../../types";
import SectionSidebar from "../../layout/SectionSidebar";
import CampaignOverviewPage from "./CampaignOverviewPage";
import CampaignFieldsPage from "./CampaignFieldsPage";
import GlobalSettingsPage from "./GlobalSettingsPage";
import RecipientDataPage from "./RecipientDataPage";
import RecipientDetailsPage from "./RecipientDetailsPage";
import TemplateDataPage from "./TemplateDataPage";
import AttachmentsDataPage from "./AttachmentsDataPage";
import MailSettingsPage from "./MailSettingsPage";
import ReviewSendPage from "./ReviewSendPage";
import CreateWizard from "./wizard/CreateWizard";
import ReviewWizard from "./wizard/ReviewWizard";
import SendWizard from "./wizard/SendWizard";
import CampaignJsonView from "./CampaignJsonView";
import CampaignReportPage from "./CampaignReportPage";
import CampaignAuditPage from "./CampaignAuditPage";
import { CampaignUnsavedChangesProvider, useCampaignUnsavedChanges } from "./context/UnsavedChangesContext";
const sectionPaths: Record<CampaignWorkspaceSection, string> = {
overview: "",
campaign: "recipients",
"global-settings": "global-settings",
fields: "fields",
recipients: "recipients",
"recipient-data": "recipient-data",
template: "template",
files: "files",
"mail-settings": "mail-settings",
review: "review",
report: "report",
audit: "audit",
json: "json"
};
export default function CampaignWorkspace({ settings, auth }: { settings: ApiSettings; auth: AuthInfo }) {
return (
<CampaignUnsavedChangesProvider>
<CampaignWorkspaceInner settings={settings} auth={auth} />
</CampaignUnsavedChangesProvider>
);
}
function CampaignWorkspaceInner({ settings, auth }: { settings: ApiSettings; auth: AuthInfo }) {
const { campaignId } = useParams();
const navigate = useNavigate();
const { requestNavigation } = useCampaignUnsavedChanges();
const location = useLocation();
const active = sectionFromPath(location.pathname);
const urlVersionId = new URLSearchParams(location.search).get("version");
const [selectedVersionId, setSelectedVersionId] = useState<string | null>(urlVersionId);
useEffect(() => {
if (urlVersionId) setSelectedVersionId(urlVersionId);
}, [urlVersionId]);
// Relative links inside the workspace historically dropped ?version=. Keep
// the selected version stable for the lifetime of this campaign workspace,
// while still opening the latest version on a fresh campaign entry.
useEffect(() => {
if (urlVersionId || !selectedVersionId) return;
const params = new URLSearchParams(location.search);
params.set("version", selectedVersionId);
navigate(`${location.pathname}?${params.toString()}`, { replace: true });
}, [location.pathname, location.search, navigate, selectedVersionId, urlVersionId]);
function select(section: CampaignWorkspaceSection) {
const path = sectionPaths[section];
const pathname = path ? `/campaigns/${campaignId}/${path}` : `/campaigns/${campaignId}`;
const params = new URLSearchParams(location.search);
const versionId = urlVersionId ?? selectedVersionId;
if (versionId) params.set("version", versionId);
const query = params.toString();
const target = query ? `${pathname}?${query}` : pathname;
if (`${location.pathname}${location.search}` === target) return;
requestNavigation(() => navigate(target));
}
return (
<div className="workspace">
<SectionSidebar active={active} onSelect={select} />
<section className="workspace-content">
<Routes>
<Route index element={<CampaignOverviewPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="data" element={<Navigate to="../recipients" replace />} />
<Route path="fields" element={<CampaignFieldsPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="recipients" element={<RecipientDataPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="recipient-data" element={<RecipientDetailsPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="template" element={<TemplateDataPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="files" element={<AttachmentsDataPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="attachments" element={<Navigate to="../files" replace />} />
<Route path="mail-settings" element={<MailSettingsPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="server-settings" element={<Navigate to="../mail-settings" replace />} />
<Route path="global-settings" element={<GlobalSettingsPage settings={settings} auth={auth} campaignId={campaignId || ""} />} />
<Route path="review" element={<ReviewSendPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="send" element={<Navigate to="../review" replace />} />
<Route path="report" element={<CampaignReportPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="reports" element={<Navigate to="../report" replace />} />
<Route path="audit" element={<CampaignAuditPage settings={settings} campaignId={campaignId || ""} />} />
<Route path="json" element={<CampaignJsonView settings={settings} campaignId={campaignId || ""} />} />
<Route path="wizard/create" element={<CreateWizard settings={settings} campaignId={campaignId || ""} />} />
<Route path="wizard/review" element={<ReviewWizard />} />
<Route path="wizard/send" element={<SendWizard />} />
<Route path="create" element={<Navigate to="../wizard/create" replace />} />
<Route path="campaign" element={<Navigate to="../data" replace />} />
<Route path="mail" element={<Navigate to="../mail-settings" replace />} />
<Route path="settings" element={<Navigate to="../global-settings" replace />} />
<Route path="Review" element={<Navigate to="../" replace />} />
<Route path="*" element={<Navigate to="../" replace />} />
</Routes>
</section>
</div>
);
}
function sectionFromPath(pathname: string): CampaignWorkspaceSection {
const segments = pathname.split("/").filter(Boolean);
const section = segments[2];
if (!section || section === "wizard" || section === "create") return "overview";
if (section === "data" || section === "campaign") return "recipients";
if (section === "global-settings" || section === "settings") return "global-settings";
if (section === "fields") return "fields";
if (section === "recipients") return "recipients";
if (section === "recipient-data" || section === "recipient-details") return "recipient-data";
if (section === "template") return "template";
if (section === "files" || section === "attachments") return "files";
if (section === "mail-settings" || section === "server-settings" || section === "mail") return "mail-settings";
if (section === "review") return "review";
if (section === "send") return "review";
if (section === "report" || section === "reports") return "report";
if (section === "audit") return "audit";
if (section === "json") return "json";
return "overview";
}