list coordination and design documents in their own sections

This commit is contained in:
Daniel Roth 2026-05-19 08:30:57 +00:00
parent 61be9a477c
commit f99374a16f
4 changed files with 65 additions and 11 deletions

View file

@ -59,6 +59,9 @@ export const DOC_TYPE_LABELS: Record<string, string> = {
installer_qualifications: "Installer Qualifications",
installer_feedback: "Installer Feedback",
contractor_other: "Other",
improvement_option_evaluation: "Improvement Option Evaluation",
medium_term_improvement_plan: "Medium Term Improvement Plan",
retrofit_design_doc: "Retrofit Design Document"
};
function formatDocDate(iso: string): string {
@ -172,8 +175,8 @@ export default function PropertyDocumentsContent({
return next;
});
const { retrofitDocs, installDocs } = splitDocumentsByType(documents);
const missingRetrofitTypes = getMissingRetrofitTypes(retrofitDocs);
const { docs: surveyDocs, coordinationDocs, designDocs, installDocs } = splitDocumentsByType(documents);
const missingRetrofitTypes = getMissingRetrofitTypes(surveyDocs);
const hasDocuments = documents.length > 0;
const isContractor = userCapability?.includes("contractor") ?? false;
@ -258,9 +261,9 @@ export default function PropertyDocumentsContent({
<h3 className="text-xs font-semibold uppercase tracking-wide text-gray-400 px-0.5">
Retrofit Assessment Documents
</h3>
{retrofitDocs.length > 0 ? (
{surveyDocs.length > 0 ? (
<div className="space-y-1.5">
{retrofitDocs.map((doc) => (
{surveyDocs.map((doc) => (
<DocumentRow key={doc.id} doc={doc} />
))}
</div>
@ -287,6 +290,40 @@ export default function PropertyDocumentsContent({
)}
</motion.div>
{/* Coordination Documents */}
<motion.div key="coordination" initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="space-y-3">
<h3 className="text-xs font-semibold uppercase tracking-wide text-gray-400 px-0.5 flex items-center gap-1.5">
Coordination Documents
</h3>
{coordinationDocs.length > 0 ? (
<div className="space-y-1.5">
{coordinationDocs.map((doc) => (
<DocumentRow key={doc.id} doc={doc} />
))}
</div>
) : (
<p className="text-xs text-gray-400 px-0.5">None uploaded yet.</p>
)}
</motion.div>
{/* Design Documents */}
<motion.div key="design" initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="space-y-3">
<h3 className="text-xs font-semibold uppercase tracking-wide text-gray-400 px-0.5 flex items-center gap-1.5">
Design Documents
</h3>
{designDocs.length > 0 ? (
<div className="space-y-1.5">
{designDocs.map((doc) => (
<DocumentRow key={doc.id} doc={doc} />
))}
</div>
) : (
<p className="text-xs text-gray-400 px-0.5">None uploaded yet.</p>
)}
</motion.div>
{/* Install Documents */}
<motion.div key="install" initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="space-y-3">
<h3 className="text-xs font-semibold uppercase tracking-wide text-gray-400 px-0.5 flex items-center gap-1.5">

View file

@ -35,21 +35,21 @@ function makeMeasureProgress(overrides: Partial<MeasureDocProgress> = {}): Measu
describe("splitDocumentsByType", () => {
it("puts survey doc types in retrofitDocs", () => {
const doc = makeDoc({ docType: "photo_pack" });
const { retrofitDocs, installDocs } = splitDocumentsByType([doc]);
const { docs: retrofitDocs, installDocs } = splitDocumentsByType([doc]);
expect(retrofitDocs).toHaveLength(1);
expect(installDocs).toHaveLength(0);
});
it("puts install doc types in installDocs", () => {
const doc = makeDoc({ docType: "pre_photo" });
const { retrofitDocs, installDocs } = splitDocumentsByType([doc]);
const { docs: retrofitDocs, installDocs } = splitDocumentsByType([doc]);
expect(retrofitDocs).toHaveLength(0);
expect(installDocs).toHaveLength(1);
});
it("includes optional ecmk types in retrofitDocs", () => {
const doc = makeDoc({ docType: "ecmk_site_note" });
const { retrofitDocs } = splitDocumentsByType([doc]);
const { docs: retrofitDocs } = splitDocumentsByType([doc]);
expect(retrofitDocs).toHaveLength(1);
});
@ -60,13 +60,13 @@ describe("splitDocumentsByType", () => {
makeDoc({ id: "3", docType: "site_note" }),
makeDoc({ id: "4", docType: "post_photo" }),
];
const { retrofitDocs, installDocs } = splitDocumentsByType(docs);
const { docs: retrofitDocs, installDocs } = splitDocumentsByType(docs);
expect(retrofitDocs).toHaveLength(2);
expect(installDocs).toHaveLength(2);
});
it("returns empty arrays for empty input", () => {
const { retrofitDocs, installDocs } = splitDocumentsByType([]);
const { docs: retrofitDocs, installDocs } = splitDocumentsByType([]);
expect(retrofitDocs).toHaveLength(0);
expect(installDocs).toHaveLength(0);
});

View file

@ -1,15 +1,21 @@
import {
SURVEY_ALL_DOC_TYPES,
EXPECTED_RETROFIT_ASSESSMENT_DOC_TYPES,
COORDINATION_DOC_TYPES,
DESIGN_DOC_TYPES,
} from "./types";
import type { PropertyDocument, MeasureDocProgress } from "./types";
export function splitDocumentsByType(docs: PropertyDocument[]): {
retrofitDocs: PropertyDocument[];
docs: PropertyDocument[];
coordinationDocs: PropertyDocument[];
designDocs: PropertyDocument[];
installDocs: PropertyDocument[];
} {
return {
retrofitDocs: docs.filter((d) => SURVEY_ALL_DOC_TYPES.has(d.docType)),
docs: docs.filter((d) => SURVEY_ALL_DOC_TYPES.has(d.docType)),
coordinationDocs: docs.filter((d) => COORDINATION_DOC_TYPES.has(d.docType)),
designDocs: docs.filter((d) => DESIGN_DOC_TYPES.has(d.docType)),
installDocs: docs.filter((d) => !SURVEY_ALL_DOC_TYPES.has(d.docType)),
};
}

View file

@ -253,6 +253,17 @@ export const SURVEY_ALL_DOC_TYPES = new Set<string>([
"pas_significance",
]);
// Coordination doc types
export const COORDINATION_DOC_TYPES = new Set<string>([
"improvement_option_evaluation",
"medium_term_improvement_plan"
]);
// Design doc types
export const DESIGN_DOC_TYPES = new Set<string>([
"retrofit_design_doc"
]);
// Per-measure document upload progress
export type MeasureDocProgress = {
measureName: string;