- New route group app/(portal)/ separate from admin app - Minimal portal layout with FiberOps branding, no admin nav - portal-auth-store.ts: Zustand store with portal_token in localStorage - portal-api.ts: Axios instance using portal_token + X-Tenant-Slug - Login page: tenant slug + account number + password form - Dashboard: account info, subscription details, balance due, quick links - Invoices page: table with status badges (PAID/PARTIAL/OVERDUE/SENT) - Tickets page: ticket list + New Ticket modal (POST /portal/tickets) - Client detail profile tab: Portal Access Enabled/Disabled field - portalAccessEnabled added to Client type
332 lines
6.9 KiB
TypeScript
332 lines
6.9 KiB
TypeScript
export interface User {
|
|
id: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
phone?: string;
|
|
isActive: boolean;
|
|
lastLoginAt?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
roleAssignments?: RoleAssignment[];
|
|
}
|
|
|
|
export interface RoleAssignment {
|
|
id: string;
|
|
userId: string;
|
|
role: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface AuthUser {
|
|
id: string;
|
|
email: string;
|
|
firstName?: string;
|
|
lastName?: string;
|
|
name?: string;
|
|
role?: string;
|
|
}
|
|
|
|
export interface AuthState {
|
|
user: AuthUser | null;
|
|
accessToken: string | null;
|
|
tenantSlug: string | null;
|
|
isAuthenticated: boolean;
|
|
login: (tenantSlug: string, email: string, password: string) => Promise<void>;
|
|
logout: () => void;
|
|
}
|
|
|
|
export interface Client {
|
|
id: string;
|
|
tenantId: string;
|
|
accountNumber: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
phone: string;
|
|
address?: string;
|
|
isActive: boolean;
|
|
lat?: number | null;
|
|
lng?: number | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
area?: { id: string; name: string };
|
|
subscriptions?: Subscription[];
|
|
portalAccessEnabled?: boolean;
|
|
}
|
|
|
|
export interface Subscription {
|
|
id: string;
|
|
clientId: string;
|
|
planId: string;
|
|
plan?: Plan;
|
|
status: string;
|
|
type?: string;
|
|
startDate: string;
|
|
endDate?: string;
|
|
monthlyRate?: number;
|
|
mrc?: number;
|
|
monthlyPrice?: number | string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface Plan {
|
|
id: string;
|
|
name: string;
|
|
type: string;
|
|
speedDownMbps: number;
|
|
speedUpMbps: number;
|
|
monthlyPrice: number;
|
|
isActive: boolean;
|
|
}
|
|
|
|
export interface Invoice {
|
|
id: string;
|
|
invoiceNumber?: string;
|
|
clientId: string;
|
|
client?: { firstName: string; lastName: string; accountNumber: string };
|
|
amount: number;
|
|
totalAmount?: number;
|
|
dueDate: string;
|
|
status: string;
|
|
paidAt?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface Payment {
|
|
id: string;
|
|
clientId: string;
|
|
client?: { firstName: string; lastName: string; accountNumber: string };
|
|
amount: number;
|
|
channel: string;
|
|
reference?: string;
|
|
referenceNumber?: string;
|
|
status: string;
|
|
date?: string;
|
|
paymentDate?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface Ticket {
|
|
id: string;
|
|
ticketNumber?: string;
|
|
subject: string;
|
|
description?: string;
|
|
clientId?: string;
|
|
client?: { id: string; firstName: string; lastName: string; accountNumber: string };
|
|
type: string;
|
|
priority: string;
|
|
status: string;
|
|
assignedToId?: string;
|
|
assignedTo?: { id: string; firstName: string; lastName: string } | string;
|
|
assignedUser?: { id: string; firstName: string; lastName: string };
|
|
createdById?: string;
|
|
createdBy?: { firstName: string; lastName: string };
|
|
resolvedAt?: string;
|
|
updatedAt?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface Remittance {
|
|
id: string;
|
|
collectorId?: string;
|
|
collector?: { firstName: string; lastName: string };
|
|
amount: number | string;
|
|
notes?: string;
|
|
status: string;
|
|
payments?: Payment[];
|
|
createdAt: string;
|
|
updatedAt?: string;
|
|
}
|
|
|
|
export interface Task {
|
|
id: string;
|
|
title: string;
|
|
type: string;
|
|
status: string;
|
|
assignedTo?: string;
|
|
assignedUser?: { firstName: string; lastName: string };
|
|
dueDate?: string;
|
|
ticketId?: string;
|
|
ticket?: { subject: string };
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface AuditLog {
|
|
id: string;
|
|
userId?: string;
|
|
user?: { firstName: string; lastName: string; email: string };
|
|
action: string;
|
|
entity?: string;
|
|
entityType?: string;
|
|
entityId?: string;
|
|
details?: Record<string, unknown>;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface DashboardSummary {
|
|
subscribers: {
|
|
total: number;
|
|
active: number;
|
|
pending: number;
|
|
suspended: number;
|
|
};
|
|
billing: {
|
|
unpaidInvoices: number;
|
|
overdueInvoices: number;
|
|
};
|
|
support: {
|
|
openTickets: number;
|
|
inProgressTickets: number;
|
|
};
|
|
tasks: {
|
|
pending: number;
|
|
};
|
|
revenue: {
|
|
thisMonth: number;
|
|
lastMonth: number;
|
|
growth: number | null;
|
|
};
|
|
}
|
|
|
|
export interface Meta {
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
totalPages: number;
|
|
}
|
|
|
|
export interface PaginatedResponse<T> {
|
|
data: T[];
|
|
meta: Meta;
|
|
}
|
|
|
|
// For /users which returns plain array
|
|
export type UsersResponse = User[];
|
|
|
|
// For invoices/payments which return { data, total, page, limit }
|
|
export interface LegacyPaginatedResponse<T> {
|
|
data: T[];
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
}
|
|
|
|
// ─── Accounting ────────────────────────────────────────────────────────────────
|
|
|
|
export type AccountType = 'ASSET' | 'LIABILITY' | 'EQUITY' | 'REVENUE' | 'EXPENSE';
|
|
|
|
export interface Account {
|
|
id: string;
|
|
code: string;
|
|
name: string;
|
|
type: AccountType;
|
|
isActive: boolean;
|
|
balance?: number;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface JournalEntryLine {
|
|
id?: string;
|
|
accountId: string;
|
|
account?: Account;
|
|
debit: number;
|
|
credit: number;
|
|
memo?: string;
|
|
}
|
|
|
|
export interface JournalEntry {
|
|
id: string;
|
|
date: string;
|
|
reference?: string;
|
|
description: string;
|
|
sourceType?: string;
|
|
lines: JournalEntryLine[];
|
|
totalDebit?: number;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface Expense {
|
|
id: string;
|
|
date: string;
|
|
vendor?: string;
|
|
accountId: string;
|
|
account?: Account;
|
|
amount: number;
|
|
description?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export type CompanyAccountType = 'CASH' | 'BANK' | 'EWALLET';
|
|
|
|
export interface CompanyAccount {
|
|
id: string;
|
|
name: string;
|
|
type: CompanyAccountType;
|
|
bankName?: string;
|
|
accountNumber?: string;
|
|
balance: number;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface Transfer {
|
|
id: string;
|
|
fromAccountId: string;
|
|
fromAccount?: CompanyAccount;
|
|
toAccountId: string;
|
|
toAccount?: CompanyAccount;
|
|
amount: number;
|
|
date: string;
|
|
note?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface TrialBalanceLine {
|
|
code: string;
|
|
name: string;
|
|
debit: number;
|
|
credit: number;
|
|
balance: number;
|
|
}
|
|
|
|
export interface ProfitLossReport {
|
|
revenue: Array<{ name: string; amount: number }>;
|
|
expenses: Array<{ name: string; amount: number }>;
|
|
totalRevenue: number;
|
|
totalExpenses: number;
|
|
netIncome: number;
|
|
}
|
|
|
|
export interface BalanceSheetReport {
|
|
assets: Array<{ name: string; amount: number }>;
|
|
liabilities: Array<{ name: string; amount: number }>;
|
|
equity: Array<{ name: string; amount: number }>;
|
|
totalAssets: number;
|
|
totalLiabilities: number;
|
|
totalEquity: number;
|
|
}
|
|
|
|
export interface CashFlowReport {
|
|
operating: Array<{ name: string; amount: number }>;
|
|
investing: Array<{ name: string; amount: number }>;
|
|
financing: Array<{ name: string; amount: number }>;
|
|
netOperating: number;
|
|
netInvesting: number;
|
|
netFinancing: number;
|
|
netCashFlow: number;
|
|
}
|
|
|
|
export interface Lead {
|
|
id: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
phone: string;
|
|
email?: string;
|
|
address?: string;
|
|
status: 'NEW' | 'CONTACTED' | 'INTERESTED' | 'CONVERTED' | 'LOST';
|
|
source?: string;
|
|
notes?: string;
|
|
assignedTo?: { firstName: string; lastName: string };
|
|
createdAt: string;
|
|
}
|