fix: resolve all 7 type errors that broke the production build

next build runs 'Linting and checking validity of types' and fails on
the first type error, so the Coolify image build died at
app/api/cipc/search/route.ts:640. These errors were pre-existing but
only surface on next build (npm run dev does not type check).

- cipc/search/route.ts: coerce spawnSync stdout/stderr with toString();
  result is typed as the un-overloaded return, so they are string|Buffer
- group-report pdf+email: use fetchCollection<YsterbaardGroup> instead of
  an invalid cast; normalise description null -> undefined for ReportData
- CipcExplorer (cipc + cipc-2): split the state ternary into two returns;
  the RowState union correlates state with differs, so 'diff' | 'match'
  matched neither variant
- ComplianceTreeGrid: ar_non_compliance_date is optional; the API omits it
  and this component never reads it

Verified: tsc --noEmit 0 errors, next build exit 0, standalone output ok.
This commit is contained in:
sjcoetzeesj-coder 2026-10-18 15:48:50 +02:00
parent 9ccd36ec93
commit 21db4d0e64
6 changed files with 30 additions and 25 deletions

View file

@ -637,8 +637,11 @@ export async function POST(req: NextRequest) {
); );
} }
duration = Date.now() - start; duration = Date.now() - start;
stdout = result.stdout || ""; // `result` is typed as the un-overloaded spawnSync return, so stdout/stderr
stderr = result.stderr || ""; // are `string | Buffer`. encoding: "utf-8" is set above, but toString()
// is a no-op on a string and the correct decode if that ever changes.
stdout = result.stdout?.toString() ?? "";
stderr = result.stderr?.toString() ?? "";
exitCode = result.status; exitCode = result.status;
wrapperUsed = false; wrapperUsed = false;
} else { } else {

View file

@ -14,7 +14,7 @@ import { HIMALAYA_BIN, himalayaSendArgs } from "@/lib/himalaya";
import { fetchCollection, fetchJunctionEntriesForCompany } from "@/lib/pb"; import { fetchCollection, fetchJunctionEntriesForCompany } from "@/lib/pb";
import { buildGroupReportBlocks, groupReportFilename, groupReportTitle } from "@/lib/report"; import { buildGroupReportBlocks, groupReportFilename, groupReportTitle } from "@/lib/report";
import { renderPdf } from "@/lib/pdf-render"; import { renderPdf } from "@/lib/pdf-render";
import type { YsterbaardCompany, YsterbaardDirector } from "@/lib/types"; import type { YsterbaardCompany, YsterbaardDirector, YsterbaardGroup } from "@/lib/types";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
@ -41,11 +41,11 @@ export async function POST(
} }
// ── Fetch group ───────────────────────────────────────────────────────── // ── Fetch group ─────────────────────────────────────────────────────────
const gResult = await fetchCollection("groups", { filter: `id="${id}"` }); const gResult = await fetchCollection<YsterbaardGroup>("groups", { filter: `id="${id}"` });
if (!gResult.items.length) { if (!gResult.items.length) {
return NextResponse.json({ error: "Group not found" }, { status: 404 }); return NextResponse.json({ error: "Group not found" }, { status: 404 });
} }
const group = gResult.items[0] as { id: string; name: string; description?: string }; const group = gResult.items[0];
// ── Fetch all companies in this group ───────────────────────────────── // ── Fetch all companies in this group ─────────────────────────────────
const companiesResult = await fetchCollection<YsterbaardCompany>("ysterbaard_companies", { const companiesResult = await fetchCollection<YsterbaardCompany>("ysterbaard_companies", {
@ -82,7 +82,7 @@ export async function POST(
// ── Build report blocks ──────────────────────────────────────────────── // ── Build report blocks ────────────────────────────────────────────────
const reportData = { const reportData = {
group: { id: group.id, name: group.name, description: group.description }, group: { id: group.id, name: group.name, description: group.description ?? undefined },
companies, companies,
directors, directors,
companyDirectors, companyDirectors,

View file

@ -7,7 +7,7 @@ import { NextRequest, NextResponse } from "next/server";
import { fetchCollection, fetchJunctionEntriesForCompany } from "@/lib/pb"; import { fetchCollection, fetchJunctionEntriesForCompany } from "@/lib/pb";
import { buildGroupReportBlocks, groupReportFilename, groupReportTitle } from "@/lib/report"; import { buildGroupReportBlocks, groupReportFilename, groupReportTitle } from "@/lib/report";
import { renderPdf } from "@/lib/pdf-render"; import { renderPdf } from "@/lib/pdf-render";
import type { YsterbaardCompany, YsterbaardDirector } from "@/lib/types"; import type { YsterbaardCompany, YsterbaardDirector, YsterbaardGroup } from "@/lib/types";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import os from "os"; import os from "os";
import path from "path"; import path from "path";
@ -23,11 +23,11 @@ export async function GET(
const { id } = await params; const { id } = await params;
// ── Fetch group ───────────────────────────────────────────────────────── // ── Fetch group ─────────────────────────────────────────────────────────
const gResult = await fetchCollection("groups", { filter: `id="${id}"` }); const gResult = await fetchCollection<YsterbaardGroup>("groups", { filter: `id="${id}"` });
if (!gResult.items.length) { if (!gResult.items.length) {
return NextResponse.json({ error: "Group not found" }, { status: 404 }); return NextResponse.json({ error: "Group not found" }, { status: 404 });
} }
const group = gResult.items[0] as { id: string; name: string; description?: string }; const group = gResult.items[0];
// ── Fetch all companies in this group ───────────────────────────────── // ── Fetch all companies in this group ─────────────────────────────────
// Companies are linked to groups via a `groups` multi-relation field // Companies are linked to groups via a `groups` multi-relation field
@ -68,7 +68,7 @@ export async function GET(
// ── Build report ─────────────────────────────────────────────────────── // ── Build report ───────────────────────────────────────────────────────
const reportData = { const reportData = {
group: { id: group.id, name: group.name, description: group.description }, group: { id: group.id, name: group.name, description: group.description ?? undefined },
companies, companies,
directors, directors,
companyDirectors, companyDirectors,

View file

@ -1794,13 +1794,13 @@ function KvSectionWithDiff({
if (!m.updatable) { if (!m.updatable) {
return { state: "readonly", cipcValue, pbValue, differs }; return { state: "readonly", cipcValue, pbValue, differs };
} }
return { // Split into two returns: the RowState union correlates `state` with
state: differs ? "diff" : "match", // `differs`, so a single `state: differs ? "diff" : "match"` infers a
cipcValue, // "diff" | "match" union that matches neither variant.
pbValue, if (differs) {
field: m.field, return { state: "diff", cipcValue, pbValue, field: m.field, differs: true };
differs, }
}; return { state: "match", cipcValue, pbValue, field: m.field, differs: false };
}); });
const selectableRowStates = rowStates.filter((r) => r.state === "diff"); const selectableRowStates = rowStates.filter((r) => r.state === "diff");

View file

@ -1034,13 +1034,13 @@ function KvSectionWithDiff({
if (!m.updatable) { if (!m.updatable) {
return { state: "readonly", cipcValue, pbValue, differs }; return { state: "readonly", cipcValue, pbValue, differs };
} }
return { // Split into two returns: the RowState union correlates `state` with
state: differs ? "diff" : "match", // `differs`, so a single `state: differs ? "diff" : "match"` infers a
cipcValue, // "diff" | "match" union that matches neither variant.
pbValue, if (differs) {
field: m.field, return { state: "diff", cipcValue, pbValue, field: m.field, differs: true };
differs, }
}; return { state: "match", cipcValue, pbValue, field: m.field, differs: false };
}); });
const selectableRowStates = rowStates.filter((r) => r.state === "diff"); const selectableRowStates = rowStates.filter((r) => r.state === "diff");

View file

@ -66,7 +66,9 @@ type AnnualReturn = {
is_filed: boolean; is_filed: boolean;
date_filed: string; date_filed: string;
amount_paid: number; amount_paid: number;
ar_non_compliance_date: string; // Optional: the /api/compliance payload only includes it when the
// PocketBase collection has the field. Unused in this component.
ar_non_compliance_date?: string;
}; };
type IRP6Record = { type IRP6Record = {