From b919fb16468e1d4d01a10fc79c84b63a00ab569a Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 6 May 2026 20:57:06 +0000 Subject: [PATCH] modifying halted + moved pibi + additional survey --- .../live/PropertyDetailDrawer.tsx | 280 +----------------- .../your-projects/live/[dealId]/DealPage.tsx | 104 +++---- 2 files changed, 55 insertions(+), 329 deletions(-) diff --git a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/PropertyDetailDrawer.tsx b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/PropertyDetailDrawer.tsx index a76adf8..4aa2b46 100644 --- a/src/app/portfolio/[slug]/(portfolio)/your-projects/live/PropertyDetailDrawer.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/your-projects/live/PropertyDetailDrawer.tsx @@ -39,27 +39,24 @@ export type DrawerSection = | "measures" | "pibi" | "domna" - | "halted" | "technical"; -// The four tabs inside the drawer. -type DrawerTab = "overview" | "works" | "pibi" | "survey-admin"; +// The tabs inside the drawer. +type DrawerTab = "overview" | "works" | "pibi-surveys"; // Maps each focusable section to the tab that contains it. const SECTION_TO_TAB: Record = { survey: "overview", measures: "works", technical: "works", - pibi: "pibi", - domna: "survey-admin", - halted: "survey-admin", + pibi: "pibi-surveys", + domna: "pibi-surveys", }; const TAB_LABELS: Record = { overview: "Overview", works: "Works", - pibi: "PIBI", - "survey-admin": "Survey & Admin", + "pibi-surveys": "PIBIs & Surveys", }; // ----------------------------------------------------------------------- @@ -877,243 +874,6 @@ export function PibiDatesEditor({ ); } -// ----------------------------------------------------------------------- -// Halted section — editable for approvers (issue #255) -// ----------------------------------------------------------------------- -interface HaltedEditorProps { - dealId: string; - portfolioId: string; - initialHaltedDate: Date | string | null; - initialHaltedReason: string | null; - /** True when the user has the approver capability on this portfolio. */ - canEdit: boolean; -} - -export function HaltedEditor({ - dealId, - portfolioId, - initialHaltedDate, - initialHaltedReason, - canEdit, -}: HaltedEditorProps) { - const initialDate = useMemo( - () => toDateInputValue(initialHaltedDate), - [initialHaltedDate], - ); - const initialReason = initialHaltedReason ?? ""; - - const [dateValue, setDateValue] = useState(initialDate); - const [reasonValue, setReasonValue] = useState(initialReason); - const [savedDate, setSavedDate] = useState(initialDate); - const [savedReason, setSavedReason] = useState(initialReason); - const [submitting, setSubmitting] = useState(false); - const [error, setError] = useState(null); - - // Reset state when the drawer switches deals. - useEffect(() => { - setDateValue(initialDate); - setSavedDate(initialDate); - }, [initialDate]); - useEffect(() => { - setReasonValue(initialReason); - setSavedReason(initialReason); - }, [initialReason]); - - const dirty = dateValue !== savedDate || reasonValue !== savedReason; - const isHalted = !!savedDate; - - /** - * Send the supplied delta to the deal-properties endpoint. Used both by - * Save (sends only changed fields) and Resume (sends only the date null). - */ - async function patchFields(fields: Record) { - setSubmitting(true); - setError(null); - try { - const res = await fetch( - `/api/portfolio/${portfolioId}/deal-properties`, - { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ dealId, fields }), - }, - ); - if (!res.ok) { - const json = await res.json().catch(() => ({})); - return { - ok: false as const, - error: - typeof json.error === "string" - ? json.error - : "Failed to update halted state", - }; - } - const json = (await res.json()) as { - results: Record; - hubspotSync: "ok" | "failed" | "skipped"; - hubspotError?: string; - }; - const fieldErrors = Object.entries(json.results ?? {}) - .filter(([, r]) => !r.ok) - .map(([k, r]) => `${k}: ${r.error ?? "rejected"}`); - if (fieldErrors.length > 0) { - return { ok: false as const, error: fieldErrors.join("; ") }; - } - if (json.hubspotSync === "failed") { - return { - ok: true as const, - warning: json.hubspotError - ? `Saved locally — HubSpot sync failed: ${json.hubspotError}` - : "Saved locally — HubSpot sync failed", - }; - } - return { ok: true as const }; - } catch (err) { - return { - ok: false as const, - error: - err instanceof Error ? err.message : "Failed to update halted state", - }; - } finally { - setSubmitting(false); - } - } - - async function handleSave() { - if (!dirty) return; - const fields: Record = {}; - if (dateValue !== savedDate) { - fields.property_halted_date = dateInputToIso(dateValue); - } - if (reasonValue !== savedReason) { - fields.property_halted_reason = reasonValue.trim() === "" ? null : reasonValue; - } - // Optimistic update. - const prevDate = savedDate; - const prevReason = savedReason; - setSavedDate(dateValue); - setSavedReason(reasonValue); - - const result = await patchFields(fields); - if (!result.ok) { - setSavedDate(prevDate); - setSavedReason(prevReason); - setDateValue(prevDate); - setReasonValue(prevReason); - setError(result.error); - return; - } - if (result.warning) setError(result.warning); - } - - async function handleResume() { - // Resume clears only the date — reason is preserved as the last-set - // value per acceptance criteria. - const prevDate = savedDate; - setSavedDate(""); - setDateValue(""); - - const result = await patchFields({ property_halted_date: null }); - if (!result.ok) { - setSavedDate(prevDate); - setDateValue(prevDate); - setError(result.error); - return; - } - if (result.warning) setError(result.warning); - } - - if (!canEdit) { - return ( -
- - -
- ); - } - - return ( -
- {isHalted && ( -
- - Halted -
- )} -
- -
-