import type { Status } from "@/lib/issue-meta";
// The issue list's view state (scope, layout, grouping, filters) and its URL form.
// Shared by the server page, which parses it, and the client tracker.
export type Scope = "all" | "active" | "backlog";
export type ViewMode = "list" | "board";
export type Grouping = "status" | "priority" | "assignee" | "none";
export type Ordering = "manual" | "priority" | "updated" | "created";
export type TrackerView = {
scope: Scope;
view: ViewMode;
group: Grouping;
order: Ordering;
q: string;
assignee: string[];
priority: string[];
label: string[];
};
export const DEFAULT_VIEW: TrackerView = {
scope: "all",
view: "list",
group: "status",
order: "manual",
q: "",
assignee: [],
priority: [],
label: [],
};
export const SCOPE_STATUSES: Record<Scope, Status[] | null> = {
all: null,
active: ["todo", "in_progress", "in_review"],
backlog: ["backlog"],
};
/** Reads a view from URL search params, ignoring anything unknown. */
export function parseView(params: Record<string, string | string[] | undefined>): TrackerView {
const one = (key: string) => (typeof params[key] === "string" ? (params[key] as string) : "");
const list = (key: string) => one(key).split(",").filter(Boolean);
const pick = <T extends string>(value: string, allowed: readonly T[], fallback: T) =>
(allowed as readonly string[]).includes(value) ? (value as T) : fallback;
return {
scope: pick(one("scope"), ["all", "active", "backlog"], DEFAULT_VIEW.scope),
view: pick(one("view"), ["list", "board"], DEFAULT_VIEW.view),
group: pick(one("group"), ["status", "priority", "assignee", "none"], DEFAULT_VIEW.group),
order: pick(one("order"), ["manual", "priority", "updated", "created"], DEFAULT_VIEW.order),
q: one("q"),
assignee: list("assignee"),
priority: list("priority"),
label: list("label"),
};
}
export function viewToQuery(view: TrackerView) {
const params = new URLSearchParams();
for (const key of Object.keys(DEFAULT_VIEW) as (keyof TrackerView)[]) {
const value = view[key];
const text = Array.isArray(value) ? value.join(",") : value;
const fallback = DEFAULT_VIEW[key];
if (text && text !== (Array.isArray(fallback) ? fallback.join(",") : fallback)) params.set(key, text);
}
const query = params.toString();
return query ? `?${query}` : "";
}