Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,41 @@ export default function PivotJsonImportPanel({
const publishedCount = data.data?.publishedCount ?? data.data?.published?.length ?? 0;
const failedCount = data.data?.failedCount ?? data.data?.failures?.length ?? 0;
const updatedCount = data.data?.updatedCount ?? 0;
const batchWeekCounts = data.data?.batchWeekCounts || {};
const weekKeys = Object.keys(batchWeekCounts).sort();
const weekSuffix =
weekKeys.length > 1
? ` Weeks: ${weekKeys.map((w) => `${w} (${batchWeekCounts[w]})`).join(', ')}.`
: weekKeys.length === 1
? ` Week ${weekKeys[0]}.`
: batchWeek
? ` Week ${batchWeek}.`
: '';
const landedInReviewWeek =
!forceBatchWeek && batchWeek && (batchWeekCounts[batchWeek] || 0) < publishedCount;
const updatedSuffix =
updatedCount > 0
? ` ${updatedCount} updated existing catalog event(s) — already-published rows stay live.`
: '';

addNotification({
title: failedCount ? 'Batch partially staged' : 'JSON staged',
message: failedCount
? `${publishedCount} staged, ${updatedCount} updated, ${failedCount} failed.`
: `${publishedCount} event(s) staged for ${batchWeek}.`,
type: failedCount ? 'warning' : 'success',
? `${publishedCount} staged, ${updatedCount} updated, ${failedCount} failed.${weekSuffix}${updatedSuffix}`
: `${publishedCount} event(s) staged.${weekSuffix}${updatedSuffix}${
landedInReviewWeek
? ' Some events landed outside the selected review week — switch batch week to review them.'
: ' Open the review queue below to publish.'
}`,
type: failedCount || landedInReviewWeek ? 'warning' : 'success',
});
onStaged?.({
batchWeekCounts,
publishedCount,
updatedCount,
failedCount,
forceBatchWeek,
});
onStaged?.();
}, [
addNotification,
batchWeek,
Expand Down Expand Up @@ -383,9 +409,16 @@ export default function PivotJsonImportPanel({
<details className="pivot-lab__json-import">
<summary className="pivot-lab__json-import-summary">
JSON import (agents)
{mode === 'stage' && batchWeek ? ` · stages to ${batchWeek}` : null}
{mode === 'stage' && batchWeek && forceBatchWeek ? ` · pins to ${batchWeek}` : null}
</summary>
<div className="pivot-lab__json-import-body">
{!forceBatchWeek && mode === 'stage' && batchWeek ? (
<p className="pivot-lab__json-import-hint pivot-lab__json-import-hint--warn">
<strong>Force into review week</strong> is off — events will land in the ISO week of each
start date and may split across multiple weeks. They will not appear in the review queue
until you switch to that batch week.
</p>
) : null}
<p className="pivot-lab__json-import-hint">
For Just Go weekly ops: give agents the prompt below, paste their JSON here, then review
before {mode === 'stage' ? 'staging' : 'loading'}.
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/pages/PlatformAdmin/PivotLab/PivotLabPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,14 @@
code {
font-size: 11px;
}

&--warn {
padding: 8px 10px;
border-radius: var(--la-radius);
border: 1px solid rgba(180, 120, 40, 0.35);
background: rgba(180, 120, 40, 0.08);
color: var(--la-text, inherit);
}
}

&__json-import-actions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function PivotTenantCurationPage({ tenantKey, cityDisplayName }) {
isValidIsoWeek(urlBatchWeek) ? urlBatchWeek.trim() : toIsoWeek(),
);
/** When true, crawl/manual ingest pins every event into `batchWeek` instead of the event's start date. */
const [forceBatchWeek, setForceBatchWeek] = useState(false);
const [forceBatchWeek, setForceBatchWeek] = useState(true);
const [filter, setFilter] = useState(
FILTER_OPTIONS.some((opt) => opt.value === urlFilter) ? urlFilter : 'all',
);
Expand Down Expand Up @@ -169,6 +169,8 @@ function PivotTenantCurationPage({ tenantKey, cityDisplayName }) {
const [tagSuggestLoadingKey, setTagSuggestLoadingKey] = useState(null);
const [urlImportValue, setUrlImportValue] = useState('');
const [urlImportLoading, setUrlImportLoading] = useState(false);
/** Shown when a bulk stage lands events outside the selected review week. */
const [stageLandHint, setStageLandHint] = useState(null);
const initializedWeekRef = useRef(false);

// Keep committed week / filter bookmarkable (preserve page=1). Drop legacy stage= param.
Expand Down Expand Up @@ -196,6 +198,10 @@ function PivotTenantCurationPage({ tenantKey, cityDisplayName }) {
);
}, [committedWeek, committedWeekValid, filter, searchParams, setSearchParams]);

useEffect(() => {
setStageLandHint(null);
}, [committedWeek, forceBatchWeek]);

// Sync from deep links when the URL changes externally.
useEffect(() => {
if (isValidIsoWeek(urlBatchWeek)) {
Expand Down Expand Up @@ -395,6 +401,45 @@ function PivotTenantCurationPage({ tenantKey, cityDisplayName }) {
if (activeRunId) refetchRun();
}, [activeRunId, refetchOps, refetchRun]);

const handleJsonStaged = useCallback(
(result) => {
refreshAll();
setStageLandHint(null);

const counts = result?.batchWeekCounts || {};
const weeks = Object.keys(counts).sort();
if (!weeks.length) return;

const inReviewWeek = counts[committedWeek] || 0;
const totalStaged = weeks.reduce((sum, week) => sum + (counts[week] || 0), 0);

if (forceBatchWeek || inReviewWeek === totalStaged) {
return;
}

if (weeks.length === 1) {
const landedWeek = weeks[0];
setBatchWeek(landedWeek, { immediate: true });
addNotification({
title: 'Review week switched',
message: `${totalStaged} event(s) staged into ${landedWeek} by start date — switched the review queue to that week.`,
type: 'info',
});
return;
}

setStageLandHint({ batchWeekCounts: counts, totalStaged });
addNotification({
title: 'Events staged in other weeks',
message: `${totalStaged} event(s) landed by start date (${weeks
.map((week) => `${week} (${counts[week]})`)
.join(', ')}). Switch the batch week above to review them, or enable “Force into review week”.`,
type: 'warning',
});
},
[addNotification, committedWeek, forceBatchWeek, refreshAll, setBatchWeek],
);

const keybindsEnabled =
batchWeekValid &&
!manualImportOpen &&
Expand Down Expand Up @@ -1651,10 +1696,48 @@ function PivotTenantCurationPage({ tenantKey, cityDisplayName }) {
disabled={!committedWeekValid || !weekSettled || !tenantKey}
mode="stage"
onBeforeStage={() => setBatchWeek(committedWeek, { immediate: true })}
onStaged={refreshAll}
onStaged={handleJsonStaged}
/>
</section>

{stageLandHint ? (
<div
className="pivot-tenant-curation__run-banner pivot-tenant-curation__run-banner--failed"
role="status"
>
<div>
<strong>Staged events are in other batch weeks</strong>
<span className="pivot-tenant-curation__run-msg">
{' '}
{stageLandHint.totalStaged} event(s) landed by start date:{' '}
{Object.keys(stageLandHint.batchWeekCounts)
.sort()
.map((week) => (
<button
key={week}
type="button"
className="linear-btn linear-btn--ghost pivot-tenant-curation__week-link"
onClick={() => {
setBatchWeek(week, { immediate: true });
setStageLandHint(null);
}}
>
{week} ({stageLandHint.batchWeekCounts[week]})
</button>
))}
. Enable “Force into review week” before staging to pin everything to {committedWeek}.
</span>
</div>
<button
type="button"
className="linear-btn linear-btn--ghost"
onClick={() => setStageLandHint(null)}
>
Dismiss
</button>
</div>
) : null}

<section className="linear-section pivot-lab__section" aria-labelledby="curation-queue">
<div className="pivot-lab__section-head">
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,15 @@
color: var(--la-text-secondary, inherit);
}

&__week-link {
display: inline;
margin: 0 4px;
padding: 0 6px;
min-height: 0;
font-size: inherit;
vertical-align: baseline;
}

&__url-cell {
max-width: 280px;
overflow: hidden;
Expand Down