// 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; /** True if Ysterbaard / SJ is the appointed accountant for this director. */ is_client: boolean; /** 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; email: 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; // ─── Structured address fields (added 2026-08-18 from onboarding form) ─── /** Flat address — back-compat. Use line_1 + line_2 + town_city + province + postal_code. */ residential_address: string | null; residential_address_line_1: string | null; residential_address_line_2: string | null; residential_town_city: string | null; residential_province: string | null; residential_postal_code: string | null; postal_address: string | null; postal_address_line_1: string | null; postal_address_line_2: string | null; postal_town_city: string | null; postal_province: string | null; postal_postal_code: string | null; /** ISO date string YYYY-MM-DD. */ date_of_birth: string | null; created: string; updated: string; } export interface YsterbaardGroup { [key: string]: unknown; id: string; collectionId: string; collectionName: string; name: string; description: 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; /** True if Ysterbaard / SJ is the appointed accountant for this company. */ is_client: boolean; /** Relation -> ysterbaard_directors. Expanded manually below. */ director: string; /** Relation -> groups (multiple). Expanded manually below. */ groups: string[]; /** Expanded groups payload (when `?expand=groups` is used). */ expand?: { director?: Pick< YsterbaardDirector, "id" | "id_no" | "surname" | "first_names" | "is_client" >; groups?: YsterbaardGroup[]; }; 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; company_email: string | null; // ─── Structured address fields (added 2026-08-18 from onboarding form) ─── physical_address_line_1: string | null; physical_address_line_2: string | null; physical_town_city: string | null; physical_province: string | null; physical_postal_code: string | null; postal_address_line_1: string | null; postal_address_line_2: string | null; postal_town_city: string | null; postal_province: string | null; postal_postal_code: string | null; // ─── Company-level fields (added 2026-08-18 from onboarding form) ─── /** 1-12, month when the company financial year ends. */ financial_year_end_month: number | null; /** Total number of authorised shares (e.g. 100 or 1000). */ authorised_shares: number | null; /** Company cell phone (different from director mobile). */ company_cell_phone: string | null; /** SARS "Public Officer" — typically the accountant / responsible person. */ public_officer_name: string | null; /** Company secretary name. */ company_secretary_name: string | null; sars_tax_number: string | null; uif_registration_number: string | null; compensation_fund_registration_number: string | null; /** SARS eFiling username */ sars_login_username: string | null; /** SARS eFiling password */ sars_login_password: string | null; /** SARS PAYE reference number */ sars_paye_no: string | null; /** SARS VAT registration number */ sars_vat_no: string | null; /** SARS VAT returns frequency: 0=None, 1=Every Month, 2=Even Month, 3=Uneven Months, 6=Bi Annually, 12=Annually */ sars_vat_returns_period: number | null; /** SARS PAYE returns frequency: 0=None, 1=Every month */ sars_paye_return_period: number | 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 | null; enterprise_history: unknown[] | Record | null; first_seen: string | null; last_checked: string | null; created: string; updated: string; } /** PocketBase paginated response envelope. */ export interface PBList { 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; residential_address_line_1: string; residential_address_line_2: string; residential_town_city: string; residential_province: string; residential_postal_code: string; postal_address: string; postal_address_line_1: string; postal_address_line_2: string; postal_town_city: string; postal_province: string; postal_postal_code: 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; physical_address_line_1: string; physical_address_line_2: string; physical_town_city: string; physical_province: string; physical_postal_code: string; postal_address: string; postal_address_line_1: string; postal_address_line_2: string; postal_town_city: string; postal_province: string; postal_postal_code: string; company_cell_phone: string; company_email: string; public_officer_name: string; company_secretary_name: string; } export type OnboardingDirectorPayload = Omit; export type OnboardingShareholderPayload = Omit; 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; /** Free-form notes from the client (may have been captured via voice). */ client_notes?: 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; } export type ResolutionType = | "company_registration" | "director_resignation" | "director_appointment_acceptance" | "resolution_appointment_resignation_directors" | "general_resolution"; export type ResolutionStatus = "draft" | "pending" | "completed" | "archived"; // ───────────────────────────────────────────────────────────────────────── // Document management — types and document types taxonomy. // ───────────────────────────────────────────────────────────────────────── export type DocumentContext = "director" | "company"; export interface YsterbaardDocumentType { [key: string]: unknown; id: string; collectionId: string; collectionName: string; name: string; /** "director" | "company" */ context: DocumentContext; description: string | null; created: string; updated: string; } export interface YsterbaardDirectorDocument { [key: string]: unknown; id: string; collectionId: string; collectionName: string; director: string; document_type: string; file: string; original_name: string; notes: string | null; uploaded_at: string; created: string; updated: string; expand?: { document_type?: Pick; }; } export interface YsterbaardCompanyDocument { [key: string]: unknown; id: string; collectionId: string; collectionName: string; company: string; document_type: string; file: string; original_name: string; notes: string | null; uploaded_at: string; created: string; updated: string; expand?: { document_type?: Pick; }; } export interface YsterbaardResolution { [key: string]: unknown; id: string; collectionId: string; collectionName: string; /** Resolution type drives which tab it appears under. */ type: ResolutionType; /** Workflow state of this resolution. */ status: ResolutionStatus; /** Short human-readable label, e.g. "Onboarding — M Smith". */ title: string | null; /** Related company (optional — not all resolutions are company-specific). */ company: string | null; /** Related director (optional). */ director: string | null; /** For onboarding: the client's full name. */ client_name: string | null; /** For onboarding: SA ID number. */ client_id_no: string | null; /** For onboarding: client's email address. */ client_email: string | null; /** Free-form notes, e.g. "CIPC returned — resubmit". */ notes: string | null; /** ISO date string of the resolution itself. */ resolution_date: string | null; created: string; updated: string; } export interface YsterbaardDirectorAppointment { [key: string]: unknown; id: string; collectionId: string; collectionName: string; director: string; company: string; status: "pending" | "mail_sent" | "signature_received" | "mail_sent_to_all"; letter: string | null; signature: string | null; sent_to: string | null; sent_at: string | null; sign_token: string | null; expand?: { director?: Pick< YsterbaardDirector, "id" | "first_names" | "surname" | "email" | "cell" | "id_no" >; company?: Pick< YsterbaardCompany, "id" | "enterprise_name" | "enterprise_no" >; }; created: string; updated: string; }