Intelligence

Artifacts

Browse the repository, read documents, and manage the governance folders. Source, runtime, and infrastructure are read-only.

Repository
.gitignoreDockerfilenext-env.d.tsnext.config.mjspackage-lock.jsonpackage.jsonpostcss.config.mjsREADME.mdtailwind.config.tstsconfig.jsontsconfig.tsbuildinfo
README.md
CONSTITUTION_COMPLIANCE_AUDIT_V1.mdREADME.md
repositories/aaf-holdings/hq01/components/assets/asset-filters.tsx
2.8 KB
"use client";

import { useRouter } from "next/navigation";

interface Facets {
  types: string[];
  repositories: string[];
  executives: string[];
  statuses: string[];
  tags: string[];
}

/**
 * Asset Ledger filters. Search by mission, type, repository, executive, tag,
 * and status — driven through the URL so the server filters the ledger.
 */
export function AssetFilters({
  facets,
  current,
}: {
  facets: Facets;
  current: Record<string, string | undefined>;
}) {
  const router = useRouter();

  function setParam(key: string, value: string) {
    const params = new URLSearchParams();
    for (const [k, v] of Object.entries(current)) {
      if (v && k !== key) params.set(k, v);
    }
    if (value) params.set(key, value);
    const qs = params.toString();
    router.push(qs ? `/assets?${qs}` : "/assets");
  }

  const Select = ({
    label,
    name,
    options,
  }: {
    label: string;
    name: string;
    options: string[];
  }) => (
    <label className="flex flex-col gap-1">
      <span className="text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
        {label}
      </span>
      <select
        value={current[name] ?? ""}
        onChange={(e) => setParam(name, e.target.value)}
        className="h-9 rounded-md border border-input bg-background px-2 text-sm"
      >
        <option value="">All</option>
        {options.map((o) => (
          <option key={o} value={o}>
            {o}
          </option>
        ))}
      </select>
    </label>
  );

  const hasFilter = Object.values(current).some(Boolean);

  return (
    <div className="flex flex-wrap items-end gap-3">
      <Select label="Type" name="type" options={facets.types} />
      <Select label="Repository" name="repository" options={facets.repositories} />
      <Select label="Executive" name="executive" options={facets.executives} />
      <Select label="Status" name="status" options={facets.statuses} />
      <Select label="Tag" name="tag" options={facets.tags} />
      <label className="flex flex-col gap-1">
        <span className="text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
          Mission
        </span>
        <input
          defaultValue={current.mission ?? ""}
          onKeyDown={(e) => {
            if (e.key === "Enter") setParam("mission", (e.target as HTMLInputElement).value.trim());
          }}
          onBlur={(e) => setParam("mission", e.target.value.trim())}
          placeholder="MISSION-000001"
          className="h-9 rounded-md border border-input bg-background px-2 font-mono text-xs"
        />
      </label>
      {hasFilter && (
        <button
          type="button"
          onClick={() => router.push("/assets")}
          className="h-9 rounded-md px-3 text-[13px] text-muted-foreground hover:text-foreground"
        >
          Clear
        </button>
      )}
    </div>
  );
}

root · /srv/aaf