diff --git a/src/app/dashboard/financial/page.test.tsx b/src/app/dashboard/financial/page.test.tsx
index 091f554..17e988e 100644
--- a/src/app/dashboard/financial/page.test.tsx
+++ b/src/app/dashboard/financial/page.test.tsx
@@ -188,7 +188,7 @@ it("counts only deliveries received this month, and names the suppliers owed", a
// R400 + R200 this month; the R100 from September is out of the window.
expect(tileValue(html, "Received this month")).toBe("R 600");
expect(tileSub(html, "Received this month")).toBe("Owed to 2 suppliers for this month");
- expect(html).toContain('/dashboard/financial/suppliers?site=site1');
+ expect(html).toContain('/dashboard/financial/suppliers?site=site1&period=thisMonth');
});
it("says 'supplier' when only one supplier is owed", async () => {
diff --git a/src/app/dashboard/financial/page.tsx b/src/app/dashboard/financial/page.tsx
index b75ef47..45abe23 100644
--- a/src/app/dashboard/financial/page.tsx
+++ b/src/app/dashboard/financial/page.tsx
@@ -204,7 +204,7 @@ export default async function FinancialLanding({
number -- and the same number the Suppliers page shows. */}
}) {
- const scope = await getSiteScope(await searchParams, "financial");
+ const params = await searchParams;
+ const scope = await getSiteScope(params, "financial");
+ // A deep link such as the Financial landing's "Received this month" tile
+ // must open on the period it linked to, or the number on the tile and the
+ // number on this page disagree.
+ const initialPeriod = parseInitialPeriod(params.period);
const pb = await pbAdmin();
const [suppliers, pos, items, deliveryLines, candidates] = await Promise.all([
@@ -91,7 +97,7 @@ export default async function SuppliersPage({ searchParams }: { searchParams: Pr
subtitle="What's owed to whom — computed live from purchase orders and deliveries."
right={}
/>
-
+
>
);
}
diff --git a/src/app/dashboard/financial/suppliers/suppliers-view.tsx b/src/app/dashboard/financial/suppliers/suppliers-view.tsx
index 65c82e9..527964a 100644
--- a/src/app/dashboard/financial/suppliers/suppliers-view.tsx
+++ b/src/app/dashboard/financial/suppliers/suppliers-view.tsx
@@ -105,14 +105,17 @@ function periodSubLabel(period: PeriodFilter): string {
export function SuppliersView({
rows,
deliveryValues,
+ initialPeriod = "all",
}: {
rows: SupplierRow[];
deliveryValues: DeliveryValue[];
+ /** Pre-selected filter, e.g. from `?period=thisMonth`. Unvalidated here. */
+ initialPeriod?: PeriodFilter;
}) {
const [search, setSearch] = React.useState("");
const [sortKey, setSortKey] = React.useState("outstanding");
const [sortDir, setSortDir] = React.useState("desc");
- const [period, setPeriod] = React.useState("all");
+ const [period, setPeriod] = React.useState(initialPeriod);
const [fromDate, setFromDate] = React.useState("");
const [toDate, setToDate] = React.useState("");
diff --git a/src/lib/supplier-period.test.ts b/src/lib/supplier-period.test.ts
index 655dc4c..9e16c07 100644
--- a/src/lib/supplier-period.test.ts
+++ b/src/lib/supplier-period.test.ts
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import {
isInPeriod,
+ parseInitialPeriod,
resolvePeriodWindow,
type PeriodWindow,
} from "./supplier-period";
@@ -87,4 +88,29 @@ describe("isInPeriod", () => {
expect(isInPeriod("2026-08-31T00:00:00.000Z", sept)).toBe(false);
expect(isInPeriod("2026-11-01T00:00:00.000Z", sept)).toBe(false);
});
+});
+
+describe("parseInitialPeriod", () => {
+ it("accepts a known period value", () => {
+ expect(parseInitialPeriod("thisMonth")).toBe("thisMonth");
+ expect(parseInitialPeriod("week2")).toBe("week2");
+ });
+
+ it("takes the first value when given an array", () => {
+ expect(parseInitialPeriod(["thisWeek", "thisMonth"])).toBe("thisWeek");
+ });
+
+ it("falls back to all for custom, which needs date inputs", () => {
+ expect(parseInitialPeriod("custom")).toBe("all");
+ });
+
+ it("falls back to all for anything unrecognised", () => {
+ expect(parseInitialPeriod("nonsense")).toBe("all");
+ expect(parseInitialPeriod(["nonsense"])).toBe("all");
+ expect(parseInitialPeriod("__proto__")).toBe("all");
+ });
+
+ it("falls back to all when absent", () => {
+ expect(parseInitialPeriod(undefined)).toBe("all");
+ });
});
\ No newline at end of file
diff --git a/src/lib/supplier-period.ts b/src/lib/supplier-period.ts
index 45830ca..8086db9 100644
--- a/src/lib/supplier-period.ts
+++ b/src/lib/supplier-period.ts
@@ -168,4 +168,22 @@ export const PERIOD_OPTIONS: { value: PeriodFilter; label: string }[] = [
{ value: "month2", label: "Month 2" },
{ value: "month3", label: "Month 3" },
{ value: "custom", label: "Custom range…" },
-];
\ No newline at end of file
+];
+
+/**
+ * Period a deep link (`?period=`) may preselect. "custom" is excluded:
+ * it needs a from/to date pair the URL does not carry, so it stays a
+ * deliberate choice in the UI. Anything not on the known list -- garbage,
+ * a prototype key, a missing param -- falls back to "all" instead of
+ * reaching the filter state unchecked.
+ */
+const INITIAL_PERIODS = PERIOD_OPTIONS
+ .map((option) => option.value)
+ .filter((value): value is PeriodFilter => value !== "custom");
+
+export function parseInitialPeriod(raw: string | string[] | undefined): PeriodFilter {
+ const value = Array.isArray(raw) ? raw[0] : raw;
+ return value && INITIAL_PERIODS.includes(value as PeriodFilter)
+ ? (value as PeriodFilter)
+ : "all";
+}
\ No newline at end of file