- 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
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
import portalApi from './portal-api';
|
|
|
|
interface PortalSubscriber {
|
|
accountNumber: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
}
|
|
|
|
interface PortalAuthState {
|
|
subscriber: PortalSubscriber | null;
|
|
portalToken: string | null;
|
|
tenantSlug: string | null;
|
|
isAuthenticated: boolean;
|
|
login: (tenantSlug: string, accountNumber: string, password: string) => Promise<void>;
|
|
logout: () => void;
|
|
}
|
|
|
|
export const usePortalAuthStore = create<PortalAuthState>()(
|
|
persist(
|
|
(set) => ({
|
|
subscriber: null,
|
|
portalToken: null,
|
|
tenantSlug: null,
|
|
isAuthenticated: false,
|
|
login: async (tenantSlug, accountNumber, password) => {
|
|
const res = await portalApi.post('/api/v1/portal/auth/login', {
|
|
tenantSlug,
|
|
accountNumber,
|
|
password,
|
|
});
|
|
const { token, subscriber } = res.data;
|
|
localStorage.setItem('portal_token', token);
|
|
set({
|
|
portalToken: token,
|
|
tenantSlug,
|
|
subscriber: {
|
|
accountNumber: subscriber.accountNumber,
|
|
firstName: subscriber.firstName,
|
|
lastName: subscriber.lastName,
|
|
},
|
|
isAuthenticated: true,
|
|
});
|
|
},
|
|
logout: () => {
|
|
localStorage.removeItem('portal_token');
|
|
set({ subscriber: null, portalToken: null, tenantSlug: null, isAuthenticated: false });
|
|
},
|
|
}),
|
|
{ name: 'portal_auth' }
|
|
)
|
|
);
|