Fix reload behaviour; don't override global with empty local value
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -420,3 +420,4 @@ bin-release/
|
|||||||
# should NOT be excluded as they contain compiler settings and other important
|
# should NOT be excluded as they contain compiler settings and other important
|
||||||
# information for Eclipse / Flash Builder.
|
# information for Eclipse / Flash Builder.
|
||||||
|
|
||||||
|
.fuse_*
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import type { ApiSettings } from "../../types";
|
import type { ApiSettings } from "../../types";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
@@ -22,7 +22,6 @@ export default function AttachmentsDataPage({ settings, campaignId }: { settings
|
|||||||
const [dirty, setDirty] = useState(false);
|
const [dirty, setDirty] = useState(false);
|
||||||
const [saveState, setSaveState] = useState("Loaded");
|
const [saveState, setSaveState] = useState("Loaded");
|
||||||
const [localError, setLocalError] = useState("");
|
const [localError, setLocalError] = useState("");
|
||||||
const loadedVersionId = useRef<string | null>(null);
|
|
||||||
|
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
const locked = isAuditLockedVersion(version);
|
const locked = isAuditLockedVersion(version);
|
||||||
@@ -34,8 +33,6 @@ export default function AttachmentsDataPage({ settings, campaignId }: { settings
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version) return;
|
if (!version) return;
|
||||||
if (loadedVersionId.current === version.id) return;
|
|
||||||
loadedVersionId.current = version.id;
|
|
||||||
setDraft(ensureCampaignDraft(version));
|
setDraft(ensureCampaignDraft(version));
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import type { ApiSettings } from "../../types";
|
import type { ApiSettings } from "../../types";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
@@ -22,7 +22,6 @@ export default function CampaignDataPage({ settings, campaignId }: { settings: A
|
|||||||
const [dirty, setDirty] = useState(false);
|
const [dirty, setDirty] = useState(false);
|
||||||
const [saveState, setSaveState] = useState("Loaded");
|
const [saveState, setSaveState] = useState("Loaded");
|
||||||
const [localError, setLocalError] = useState("");
|
const [localError, setLocalError] = useState("");
|
||||||
const loadedVersionId = useRef<string | null>(null);
|
|
||||||
|
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
const locked = isAuditLockedVersion(version);
|
const locked = isAuditLockedVersion(version);
|
||||||
@@ -35,8 +34,6 @@ export default function CampaignDataPage({ settings, campaignId }: { settings: A
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version) return;
|
if (!version) return;
|
||||||
if (loadedVersionId.current === version.id) return;
|
|
||||||
loadedVersionId.current = version.id;
|
|
||||||
setDraft(ensureCampaignDraft(version));
|
setDraft(ensureCampaignDraft(version));
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { addressesFromValue } from "../../utils/emailAddresses";
|
|||||||
|
|
||||||
export default function CampaignOverviewPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
export default function CampaignOverviewPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId);
|
const { data, loading, error, reload, setError } = useCampaignWorkspaceData(settings, campaignId, { includeSummary: true });
|
||||||
const [copying, setCopying] = useState(false);
|
const [copying, setCopying] = useState(false);
|
||||||
const [locking, setLocking] = useState(false);
|
const [locking, setLocking] = useState(false);
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
|||||||
import { formatDateTime } from "./utils/campaignView";
|
import { formatDateTime } from "./utils/campaignView";
|
||||||
|
|
||||||
export default function CampaignReportPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
export default function CampaignReportPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
||||||
const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId);
|
const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId, { includeCurrentVersion: false, includeSummary: true });
|
||||||
const cards = data.summary?.cards;
|
const cards = data.summary?.cards;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import type { ApiSettings } from "../../types";
|
import type { ApiSettings } from "../../types";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
import Card from "../../components/Card";
|
import Card from "../../components/Card";
|
||||||
@@ -22,7 +22,6 @@ export default function GlobalSettingsPage({ settings, campaignId }: { settings:
|
|||||||
const [dirty, setDirty] = useState(false);
|
const [dirty, setDirty] = useState(false);
|
||||||
const [saveState, setSaveState] = useState("Loaded");
|
const [saveState, setSaveState] = useState("Loaded");
|
||||||
const [localError, setLocalError] = useState("");
|
const [localError, setLocalError] = useState("");
|
||||||
const loadedVersionId = useRef<string | null>(null);
|
|
||||||
|
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
const locked = isAuditLockedVersion(version);
|
const locked = isAuditLockedVersion(version);
|
||||||
@@ -36,8 +35,6 @@ export default function GlobalSettingsPage({ settings, campaignId }: { settings:
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version) return;
|
if (!version) return;
|
||||||
if (loadedVersionId.current === version.id) return;
|
|
||||||
loadedVersionId.current = version.id;
|
|
||||||
setDraft(ensureCampaignDraft(version));
|
setDraft(ensureCampaignDraft(version));
|
||||||
setEditorState(cloneJson(version.editor_state ?? {}));
|
setEditorState(cloneJson(version.editor_state ?? {}));
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import type { ApiSettings } from "../../types";
|
import type { ApiSettings } from "../../types";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
import Card from "../../components/Card";
|
import Card from "../../components/Card";
|
||||||
@@ -24,7 +24,6 @@ export default function MailSettingsPage({ settings, campaignId }: { settings: A
|
|||||||
const [imapTestResult, setImapTestResult] = useState<MailConnectionTestResponse | null>(null);
|
const [imapTestResult, setImapTestResult] = useState<MailConnectionTestResponse | null>(null);
|
||||||
const [folderResult, setFolderResult] = useState<MailImapFolderListResponse | null>(null);
|
const [folderResult, setFolderResult] = useState<MailImapFolderListResponse | null>(null);
|
||||||
const [mailActionState, setMailActionState] = useState<"smtp" | "imap" | "folders" | null>(null);
|
const [mailActionState, setMailActionState] = useState<"smtp" | "imap" | "folders" | null>(null);
|
||||||
const loadedVersionId = useRef<string | null>(null);
|
|
||||||
|
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
const locked = isAuditLockedVersion(version);
|
const locked = isAuditLockedVersion(version);
|
||||||
@@ -38,8 +37,6 @@ export default function MailSettingsPage({ settings, campaignId }: { settings: A
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version) return;
|
if (!version) return;
|
||||||
if (loadedVersionId.current === version.id) return;
|
|
||||||
loadedVersionId.current = version.id;
|
|
||||||
setDraft(ensureCampaignDraft(version));
|
setDraft(ensureCampaignDraft(version));
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import type { ApiSettings } from "../../types";
|
import type { ApiSettings } from "../../types";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
@@ -31,7 +31,6 @@ export default function RecipientDataPage({ settings, campaignId }: { settings:
|
|||||||
const [dirty, setDirty] = useState(false);
|
const [dirty, setDirty] = useState(false);
|
||||||
const [saveState, setSaveState] = useState("Loaded");
|
const [saveState, setSaveState] = useState("Loaded");
|
||||||
const [localError, setLocalError] = useState("");
|
const [localError, setLocalError] = useState("");
|
||||||
const loadedVersionId = useRef<string | null>(null);
|
|
||||||
|
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
const locked = isAuditLockedVersion(version);
|
const locked = isAuditLockedVersion(version);
|
||||||
@@ -49,8 +48,6 @@ export default function RecipientDataPage({ settings, campaignId }: { settings:
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version) return;
|
if (!version) return;
|
||||||
if (loadedVersionId.current === version.id) return;
|
|
||||||
loadedVersionId.current = version.id;
|
|
||||||
setDraft(ensureCampaignDraft(version));
|
setDraft(ensureCampaignDraft(version));
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
|||||||
import { asArray, asRecord, formatDateTime, stringifyPreview, summaryValue } from "./utils/campaignView";
|
import { asArray, asRecord, formatDateTime, stringifyPreview, summaryValue } from "./utils/campaignView";
|
||||||
|
|
||||||
export default function ReviewDataPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
export default function ReviewDataPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
||||||
const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId);
|
const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId, { includeSummary: true });
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
const issues = collectIssues(data.summary?.issues);
|
const issues = collectIssues(data.summary?.issues);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useCampaignWorkspaceData } from "./hooks/useCampaignWorkspaceData";
|
|||||||
import { asRecord, getDeliverySection, getNestedString } from "./utils/campaignView";
|
import { asRecord, getDeliverySection, getNestedString } from "./utils/campaignView";
|
||||||
|
|
||||||
export default function SendDataPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
export default function SendDataPage({ settings, campaignId }: { settings: ApiSettings; campaignId: string }) {
|
||||||
const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId);
|
const { data, loading, error, reload } = useCampaignWorkspaceData(settings, campaignId, { includeSummary: true });
|
||||||
const cards = data.summary?.cards;
|
const cards = data.summary?.cards;
|
||||||
const delivery = getDeliverySection(data.currentVersion);
|
const delivery = getDeliverySection(data.currentVersion);
|
||||||
const rateLimit = asRecord(delivery.rate_limit);
|
const rateLimit = asRecord(delivery.rate_limit);
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ function buildPreviewContext(draft: Record<string, unknown> | null, entry: Recor
|
|||||||
addContextValue(context, key, "local", value);
|
addContextValue(context, key, "local", value);
|
||||||
}
|
}
|
||||||
for (const [key, value] of Object.entries(entryFields)) {
|
for (const [key, value] of Object.entries(entryFields)) {
|
||||||
if (canOverrideField(overridePolicy, key)) {
|
if (canOverrideField(overridePolicy, key) && hasPreviewOverrideValue(value)) {
|
||||||
addContextValue(context, key, "local", value);
|
addContextValue(context, key, "local", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,6 +505,12 @@ function addContextValue(context: Record<string, string>, key: string, namespace
|
|||||||
context[`${namespace}::${key}`] = text;
|
context[`${namespace}::${key}`] = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasPreviewOverrideValue(value: unknown): boolean {
|
||||||
|
if (value === undefined || value === null) return false;
|
||||||
|
if (typeof value === "string") return value.trim() !== "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function renderPreviewText(text: string, context: Record<string, string>, ignoreEmptyFields: boolean): string {
|
function renderPreviewText(text: string, context: Record<string, string>, ignoreEmptyFields: boolean): string {
|
||||||
if (!text) return "";
|
if (!text) return "";
|
||||||
return text
|
return text
|
||||||
|
|||||||
@@ -18,7 +18,22 @@ const initialData: CampaignWorkspaceData = {
|
|||||||
summary: null
|
summary: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCampaignWorkspaceData(settings: ApiSettings, campaignId: string) {
|
type UseCampaignWorkspaceDataOptions = {
|
||||||
|
includeCurrentVersion?: boolean;
|
||||||
|
includeSummary?: boolean;
|
||||||
|
includeVersions?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useCampaignWorkspaceData(
|
||||||
|
settings: ApiSettings,
|
||||||
|
campaignId: string,
|
||||||
|
options: UseCampaignWorkspaceDataOptions = {}
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
includeCurrentVersion = true,
|
||||||
|
includeSummary = false,
|
||||||
|
includeVersions = false
|
||||||
|
} = options;
|
||||||
const [data, setData] = useState<CampaignWorkspaceData>(initialData);
|
const [data, setData] = useState<CampaignWorkspaceData>(initialData);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
@@ -28,20 +43,25 @@ export function useCampaignWorkspaceData(settings: ApiSettings, campaignId: stri
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
try {
|
try {
|
||||||
const [campaign, versions] = await Promise.all([
|
const needsCampaign = includeCurrentVersion || includeVersions;
|
||||||
getCampaign(settings, campaignId),
|
const summaryPromise = includeSummary ? getCampaignSummary(settings, campaignId) : Promise.resolve(null);
|
||||||
listCampaignVersions(settings, campaignId)
|
const campaign = needsCampaign ? await getCampaign(settings, campaignId) : null;
|
||||||
]);
|
let versions: CampaignVersionListItem[] = [];
|
||||||
const selectedVersionId = campaign.current_version_id ?? versions[0]?.id;
|
let selectedVersionId = campaign?.current_version_id;
|
||||||
|
|
||||||
|
if (includeVersions || (includeCurrentVersion && !selectedVersionId)) {
|
||||||
|
versions = await listCampaignVersions(settings, campaignId);
|
||||||
|
selectedVersionId = selectedVersionId ?? versions[0]?.id;
|
||||||
|
}
|
||||||
|
|
||||||
const [versionResult, summaryResult] = await Promise.allSettled([
|
const [versionResult, summaryResult] = await Promise.allSettled([
|
||||||
selectedVersionId ? getCampaignVersion(settings, campaignId, selectedVersionId) : Promise.resolve(null),
|
includeCurrentVersion && selectedVersionId ? getCampaignVersion(settings, campaignId, selectedVersionId) : Promise.resolve(null),
|
||||||
getCampaignSummary(settings, campaignId)
|
summaryPromise
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setData({
|
setData({
|
||||||
campaign,
|
campaign,
|
||||||
versions,
|
versions: includeVersions ? versions : [],
|
||||||
currentVersion: versionResult.status === "fulfilled" ? (versionResult.value as CampaignVersionDetail | null) : null,
|
currentVersion: versionResult.status === "fulfilled" ? (versionResult.value as CampaignVersionDetail | null) : null,
|
||||||
summary: summaryResult.status === "fulfilled" ? (summaryResult.value as CampaignSummary | null) : null
|
summary: summaryResult.status === "fulfilled" ? (summaryResult.value as CampaignSummary | null) : null
|
||||||
});
|
});
|
||||||
@@ -51,7 +71,7 @@ export function useCampaignWorkspaceData(settings: ApiSettings, campaignId: stri
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [settings, campaignId]);
|
}, [settings, campaignId, includeCurrentVersion, includeSummary, includeVersions]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reload();
|
reload();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import type { ApiSettings, WizardStep } from "../../../types";
|
import type { ApiSettings, WizardStep } from "../../../types";
|
||||||
import Stepper from "../../../components/Stepper";
|
import Stepper from "../../../components/Stepper";
|
||||||
@@ -36,7 +36,6 @@ export default function CreateWizard({ settings, campaignId }: { settings: ApiSe
|
|||||||
const [saveState, setSaveState] = useState("Loading…");
|
const [saveState, setSaveState] = useState("Loading…");
|
||||||
const [localError, setLocalError] = useState("");
|
const [localError, setLocalError] = useState("");
|
||||||
const [validationMessage, setValidationMessage] = useState("");
|
const [validationMessage, setValidationMessage] = useState("");
|
||||||
const loadedVersionId = useRef<string | null>(null);
|
|
||||||
const index = steps.findIndex((s) => s.id === activeStep);
|
const index = steps.findIndex((s) => s.id === activeStep);
|
||||||
const { data, loading, reload } = useCampaignWorkspaceData(settings, campaignId);
|
const { data, loading, reload } = useCampaignWorkspaceData(settings, campaignId);
|
||||||
const version = data.currentVersion;
|
const version = data.currentVersion;
|
||||||
@@ -44,8 +43,6 @@ export default function CreateWizard({ settings, campaignId }: { settings: ApiSe
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!version) return;
|
if (!version) return;
|
||||||
if (loadedVersionId.current === version.id) return;
|
|
||||||
loadedVersionId.current = version.id;
|
|
||||||
setDraft(ensureCampaignDraft(version));
|
setDraft(ensureCampaignDraft(version));
|
||||||
setDirty(false);
|
setDirty(false);
|
||||||
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
setSaveState(version.autosaved_at ? `Loaded saved draft ${formatDateTime(version.autosaved_at)}` : "Loaded");
|
||||||
|
|||||||
Reference in New Issue
Block a user