feat(directory) : add Clients/Prospects repertoire front layer
LST-58 (2.4) front. Completes the Directory module.
- New frontend/modules/directory/ layer (auto-detected): /directory page with
Clients and Prospects tabs.
- Client front moved into the layer (clients service + client DTO +
ClientDrawer). New prospects service, prospect DTO and ProspectDrawer (with
a "Convert to client" action calling POST /prospects/{id}/convert).
- Consumers repointed to ~/modules/directory/... (admin client tab, PM project
drawer + project pages + project DTO, time-tracking page + export drawer).
- Sidebar admin item /directory gated by the directory module; /directory
protected by the admin middleware. i18n keys added (directory.*, prospects.*).
nuxt build passes; routes preserved.
Adds the 2.4 plan doc.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import type { Client, ClientWrite } from './dto/client'
|
||||
import type { HydraCollection } from '~/utils/api'
|
||||
import { extractHydraMembers } from '~/utils/api'
|
||||
|
||||
export function useClientService() {
|
||||
const api = useApi()
|
||||
|
||||
async function getAll(): Promise<Client[]> {
|
||||
const data = await api.get<HydraCollection<Client>>('/clients')
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function create(payload: ClientWrite): Promise<Client> {
|
||||
return api.post<Client>('/clients', payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'clients.created',
|
||||
})
|
||||
}
|
||||
|
||||
async function update(id: number, payload: Partial<ClientWrite>): Promise<Client> {
|
||||
return api.patch<Client>(`/clients/${id}`, payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'clients.updated',
|
||||
})
|
||||
}
|
||||
|
||||
async function remove(id: number): Promise<void> {
|
||||
await api.delete(`/clients/${id}`, {}, {
|
||||
toastSuccessKey: 'clients.deleted',
|
||||
})
|
||||
}
|
||||
|
||||
return { getAll, create, update, remove }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export type Client = {
|
||||
id: number
|
||||
'@id'?: string
|
||||
name: string
|
||||
email: string | null
|
||||
phone: string | null
|
||||
street: string | null
|
||||
city: string | null
|
||||
postalCode: string | null
|
||||
}
|
||||
|
||||
export type ClientWrite = {
|
||||
name: string
|
||||
email: string | null
|
||||
phone: string | null
|
||||
street: string | null
|
||||
city: string | null
|
||||
postalCode: string | null
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { Client } from './client'
|
||||
|
||||
export type ProspectStatus = 'new' | 'contacted' | 'qualified' | 'won' | 'lost'
|
||||
|
||||
export type Prospect = {
|
||||
id: number
|
||||
'@id'?: string
|
||||
name: string
|
||||
company: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
street: string | null
|
||||
city: string | null
|
||||
postalCode: string | null
|
||||
status: ProspectStatus
|
||||
source: string | null
|
||||
notes: string | null
|
||||
convertedClient: Client | string | null
|
||||
createdAt?: string
|
||||
updatedAt?: string
|
||||
}
|
||||
|
||||
export type ProspectWrite = {
|
||||
name: string
|
||||
company: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
street: string | null
|
||||
city: string | null
|
||||
postalCode: string | null
|
||||
status: ProspectStatus
|
||||
source: string | null
|
||||
notes: string | null
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { Prospect, ProspectStatus, ProspectWrite } from './dto/prospect'
|
||||
import type { HydraCollection } from '~/utils/api'
|
||||
import { extractHydraMembers } from '~/utils/api'
|
||||
|
||||
export function useProspectService() {
|
||||
const api = useApi()
|
||||
|
||||
async function getAll(status?: ProspectStatus): Promise<Prospect[]> {
|
||||
const query: Record<string, unknown> = {}
|
||||
if (status) query.status = status
|
||||
const data = await api.get<HydraCollection<Prospect>>('/prospects', query)
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function getById(id: number): Promise<Prospect> {
|
||||
return api.get<Prospect>(`/prospects/${id}`)
|
||||
}
|
||||
|
||||
async function create(payload: ProspectWrite): Promise<Prospect> {
|
||||
return api.post<Prospect>('/prospects', payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'prospects.created',
|
||||
})
|
||||
}
|
||||
|
||||
async function update(id: number, payload: Partial<ProspectWrite>): Promise<Prospect> {
|
||||
return api.patch<Prospect>(`/prospects/${id}`, payload as Record<string, unknown>, {
|
||||
toastSuccessKey: 'prospects.updated',
|
||||
})
|
||||
}
|
||||
|
||||
async function remove(id: number): Promise<void> {
|
||||
await api.delete(`/prospects/${id}`, {}, {
|
||||
toastSuccessKey: 'prospects.deleted',
|
||||
})
|
||||
}
|
||||
|
||||
async function convert(id: number): Promise<Prospect> {
|
||||
return api.post<Prospect>(`/prospects/${id}/convert`, {}, {
|
||||
toastSuccessKey: 'prospects.converted',
|
||||
})
|
||||
}
|
||||
|
||||
return { getAll, getById, create, update, remove, convert }
|
||||
}
|
||||
Reference in New Issue
Block a user