Add complete production-ready Ysterbaard PocketBase registry browser frontend: - Next.js 14 App Router with TypeScript, Tailwind CSS, and TanStack React Query - Secure server-side PocketBase API proxy to avoid exposing superuser tokens - Read-only directors and companies registry browse pages with search, filtering, and pagination - Client onboarding workflow for creating and managing client submissions - Docker container configuration for easy deployment - All supporting UI components, utility functions, and configuration files
182 lines
5.9 KiB
TypeScript
182 lines
5.9 KiB
TypeScript
// lib/types.ts
|
|
// Mirrors the two PocketBase collections used by the app. Field names
|
|
// match the live PB schema exactly (case-sensitive). Optional fields
|
|
// are nullable on the wire; we narrow them to `T | null` so callers
|
|
// have to handle empty / missing cases.
|
|
|
|
export interface YsterbaardDirector {
|
|
[key: string]: unknown;
|
|
id: string;
|
|
collectionId: string;
|
|
collectionName: string;
|
|
/** 13-digit SA ID number, e.g. "8001015009087". */
|
|
id_no: string;
|
|
surname: string;
|
|
first_names: string;
|
|
/** Free-text from CIPC: "Active", "Disqualified", etc. */
|
|
disqualification_status: string | null;
|
|
/** SA-format phone or empty. Pattern ^[0-9 +()\-]{0,30}$ enforced in PB. */
|
|
telephone: string | null;
|
|
total_companies: number;
|
|
active_companies: number;
|
|
inactive_companies: number;
|
|
first_seen: string | null; // ISO date
|
|
last_checked: string | null; // ISO date
|
|
notes: string | null;
|
|
created: string;
|
|
updated: string;
|
|
}
|
|
|
|
export interface YsterbaardCompany {
|
|
[key: string]: unknown;
|
|
id: string;
|
|
collectionId: string;
|
|
collectionName: string;
|
|
/** Unique CIPC enterprise number, e.g. "K2020123456". */
|
|
enterprise_no: string;
|
|
enterprise_name: string;
|
|
/** e.g. "In Business", "Deregistered", "In Process". */
|
|
enterprise_status: string | null;
|
|
/** Per-director status: "ACTIVE", "INACTIVE", "DISQUALIFIED", etc. */
|
|
director_status: string | null;
|
|
is_active: boolean;
|
|
/** Relation -> ysterbaard_directors. Expanded manually below. */
|
|
director: string;
|
|
/** Expanded director payload (when `?expand=director` is used). */
|
|
expand?: {
|
|
director?: Pick<
|
|
YsterbaardDirector,
|
|
"id" | "id_no" | "surname" | "first_names"
|
|
>;
|
|
};
|
|
source: string | null;
|
|
notes: string | null;
|
|
enterprise_type: string | null;
|
|
registration_date: string | null;
|
|
physical_address: string | null;
|
|
postal_address: string | null;
|
|
physical_address_normalized: string | null;
|
|
postal_address_normalized: string | null;
|
|
sars_tax_number: string | null;
|
|
uif_registration_number: string | null;
|
|
compensation_fund_registration_number: string | null;
|
|
compliance_notice: string | null;
|
|
bbbee_status: string | null;
|
|
bbbee_certificate_applied: boolean;
|
|
info_regulator_registered: boolean;
|
|
ar_outstanding: boolean;
|
|
ar_outstanding_details: string | null;
|
|
last_5_ars: unknown[] | Record<string, unknown> | null;
|
|
enterprise_history: unknown[] | Record<string, unknown> | null;
|
|
first_seen: string | null;
|
|
last_checked: string | null;
|
|
created: string;
|
|
updated: string;
|
|
}
|
|
|
|
/** PocketBase paginated response envelope. */
|
|
export interface PBList<T> {
|
|
page: number;
|
|
perPage: number;
|
|
totalItems: number;
|
|
totalPages: number;
|
|
items: T[];
|
|
}
|
|
|
|
/** Standard query params we forward to the proxy on every list call. */
|
|
export interface ListParams {
|
|
page?: number;
|
|
perPage?: number;
|
|
sort?: string;
|
|
filter?: string;
|
|
expand?: string;
|
|
fields?: string;
|
|
}
|
|
|
|
export type CompanyStatusFilter = "all" | "active" | "inactive" | "in_process" | "deregistered";
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Onboarding intake — paperless replacement for the Annexure 1/2/3 pack.
|
|
// A single submission holds company + directors + shareholders as JSON
|
|
// blobs, with uploaded documents attached. Once processed into the live
|
|
// `ysterbaard_directors` records, the row stays around for audit until
|
|
// archived (status='rejected') and deleted by hand.
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
export interface OnboardingDirector {
|
|
/** Generated client-side, never persisted as-is — just a UI key. */
|
|
ui_key: string;
|
|
first_names: string;
|
|
surname: string;
|
|
id_no: string;
|
|
date_of_birth: string; // YYYY-MM-DD
|
|
residential_address: string;
|
|
postal_address: string;
|
|
cell_phone: string;
|
|
email: string;
|
|
}
|
|
|
|
export interface OnboardingShareholder {
|
|
ui_key: string;
|
|
/** "person" or "entity" — toggles whether the next field validates as ID or company number. */
|
|
kind: "person" | "entity";
|
|
name: string;
|
|
id_or_company_no: string;
|
|
share_percent: number;
|
|
}
|
|
|
|
export interface OnboardingCompany {
|
|
proposed_names: [string, string, string]; // up to 3 in priority order
|
|
financial_year_end_month: number; // 1-12
|
|
authorised_shares: "100" | "1000";
|
|
physical_address: string;
|
|
postal_address: string;
|
|
company_cell_phone: string;
|
|
company_email: string;
|
|
public_officer_name: string;
|
|
company_secretary_name: string;
|
|
}
|
|
|
|
export type OnboardingDirectorPayload = Omit<OnboardingDirector, "ui_key">;
|
|
export type OnboardingShareholderPayload = Omit<OnboardingShareholder, "ui_key">;
|
|
|
|
export interface OnboardingSubmissionPayload {
|
|
/** Client may omit this — server mints a fresh reference_no on submit. */
|
|
reference_no?: string;
|
|
client_name: string;
|
|
client_email: string;
|
|
client_telephone: string;
|
|
company: OnboardingCompany;
|
|
directors: OnboardingDirectorPayload[];
|
|
shareholders: OnboardingShareholderPayload[];
|
|
}
|
|
|
|
export type OnboardingStatus =
|
|
| "draft"
|
|
| "submitted"
|
|
| "processing"
|
|
| "processed"
|
|
| "rejected";
|
|
|
|
export interface OnboardingSubmission {
|
|
[key: string]: unknown;
|
|
id: string;
|
|
collectionId: string;
|
|
collectionName: string;
|
|
created: string;
|
|
updated: string;
|
|
reference_no: string;
|
|
access_token_hash: string;
|
|
client_name: string;
|
|
client_email: string;
|
|
client_telephone: string | null;
|
|
company_data: OnboardingCompany;
|
|
directors_data: OnboardingDirector[];
|
|
shareholders_data: OnboardingShareholder[] | null;
|
|
processed_director_ids: string[] | null;
|
|
documents: string[];
|
|
submitted_at: string | null;
|
|
processed_at: string | null;
|
|
internal_notes: string | null;
|
|
status: OnboardingStatus;
|
|
}
|