diff --git a/config/sidebar.php b/config/sidebar.php
index 7bee8f3..12d9a50 100644
--- a/config/sidebar.php
+++ b/config/sidebar.php
@@ -31,6 +31,7 @@ return [
'roles' => ['ROLE_ADMIN'],
'items' => [
['label' => 'sidebar.admin.teamAbsences', 'to' => '/team-absences', 'icon' => 'mdi:calendar-account-outline', 'module' => 'absence'],
+ ['label' => 'sidebar.admin.directory', 'to' => '/directory', 'icon' => 'mdi:contact-multiple-outline', 'module' => 'directory'],
['label' => 'sidebar.admin.administration', 'to' => '/admin', 'icon' => 'mdi:cog-outline', 'permission' => 'core.users.view'],
],
],
diff --git a/docs/superpowers/plans/2026-06-20-lst-58-directory-prospect.md b/docs/superpowers/plans/2026-06-20-lst-58-directory-prospect.md
new file mode 100644
index 0000000..b6df3d6
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-20-lst-58-directory-prospect.md
@@ -0,0 +1,66 @@
+# LST-58 (2.4) — Module Directory : Prospect + front répertoire (plan)
+
+> Suite de la migration Directory. Client (back) déjà livré (`c5738d2`).
+> Reste : **entité Prospect** (nouvelle) + **front répertoire** (Clients + Prospects).
+> Spec produit non fournie → design défini ici de façon raisonnable, à valider au test.
+> Additif, sans régression. Branche `integration/modular-monolith-0.1-1.3`.
+
+## Design Prospect (décidé, à valider)
+Aligné sur `Client` (même module Directory), enrichi des concepts de prospection commerciale.
+
+**Entité `App\Module\Directory\Domain\Entity\Prospect`** (table `prospect`) :
+- `id` int PK
+- `name` string(255) NOT NULL — contact ou société
+- `company` string(255) nullable
+- `email` string(255) nullable
+- `phone` string(50) nullable
+- `street` string(255) nullable / `city` string(255) nullable / `postalCode` string(20) nullable (alignés Client)
+- `status` enum `ProspectStatus` NOT NULL (default `New`)
+- `source` string(255) nullable — origine (recommandation, salon, site web…)
+- `notes` text nullable
+- `convertedClient` ManyToOne `ClientInterface` nullable, JoinColumn ON DELETE SET NULL — rempli à la conversion
+- Timestampable/Blamable (trait) + `#[Auditable]`
+- Groupes : `prospect:read` / `prospect:write`
+
+**Enum `App\Module\Directory\Domain\Enum\ProspectStatus`** : `New` (nouveau), `Contacted` (contacté), `Qualified` (qualifié), `Won` (gagné/converti), `Lost` (perdu). Méthode `label(): string` (FR), comme les autres enums.
+
+**API Platform** (aligné Client) :
+- `GetCollection` paginationEnabled:false, `is_granted('ROLE_USER')`
+- `Get` ROLE_USER ; `Post`/`Patch`/`Delete` ROLE_ADMIN
+- Opération custom **`Post /prospects/{id}/convert`** (processor `ConvertProspectProcessor`) : crée un `Client` à partir du Prospect (name/company→name, email, phone, adresse), lie `convertedClient`, passe `status=Won`. Sécurité ROLE_ADMIN. Renvoie le Prospect mis à jour. Idempotent si déjà converti (renvoie l'existant).
+- `#[ApiFilter]` SearchFilter sur `status` (filtre répertoire).
+
+**Repo** : `ProspectRepositoryInterface` (Domain) + `DoctrineProspectRepository` (Infra) + binding.
+
+**MCP** (cohérent avec clients, sous `Infrastructure/Mcp/Tool/`) : `list-prospects`, `get-prospect`, `create-prospect`, `update-prospect`, `delete-prospect`, `convert-prospect`. Serializer : ajouter `prospect()` dans `src/Mcp/Tool/Serializer.php`.
+
+**DirectoryModule.permissions()** : ajouter `directory.prospects.view`, `directory.prospects.manage` (additif).
+
+**Migration additive** : CREATE TABLE prospect (colonnes + FK converted_client→client ON DELETE SET NULL + created_by/updated_by FK user + index + COMMENT). Down = DROP TABLE.
+
+**Fixtures** : 2-3 prospects de démo (statuts variés), dont un converti.
+
+## Front répertoire (`frontend/modules/directory/`)
+Aujourd'hui : pas de page client dédiée (AdminClientTab + picker ProjectDrawer). On crée un vrai répertoire.
+- `nuxt.config.ts` vide.
+- `services/` : `clients.ts` (move depuis racine), `prospects.ts` (nouveau) + `dto/{client,prospect}.ts`.
+- `pages/directory.vue` : page à 2 onglets (Clients / Prospects), tableaux paginés côté client (paginationEnabled:false back), recherche/filtre statut pour prospects.
+- `components/` : `ClientDrawer.vue` (move depuis `components/client/`), `ProspectDrawer.vue` (nouveau, create/edit + bouton « Convertir en client »).
+- Sidebar : ajouter item `sidebar.general.directory` → `/directory`, `'module' => 'directory'`, gate ROLE_ADMIN (gestion référentiel).
+- Réécrire imports consommateurs de `~/services/clients` / `~/services/dto/client` (AdminClientTab, ProjectDrawer, pages projects) → `~/modules/directory/services/...`. AdminClientTab : soit le retirer de /admin au profit de /directory, soit le laisser pointer le nouveau service. Décision : garder AdminClientTab fonctionnel (repoint service) ET ajouter la page /directory (les deux coexistent ; /directory = vue dédiée).
+- i18n global : ajouter clés `directory.*`, `prospects.*`, `sidebar.general.directory`.
+
+## Vagues d'exécution
+1. **Back Prospect** : enum + entité + repo + API (CRUD + convert) + MCP (6 tools) + Serializer + permissions module + fixtures + migration. Vérif cache:clear/migrate/phpunit/cs-fixer → commit.
+2. **Front Directory** : layer (move client front + page répertoire + ProspectDrawer + prospects service/dto) + sidebar + imports + i18n. Vérif nuxt build → commit.
+
+## Critères d'acceptation (ticket #58)
+- [x] Clients en module (fait, c5738d2)
+- [ ] Prospects en module + front répertoire fonctionnel
+- [x] resolve_target_entities → Directory\Client
+- [ ] make test vert, aucune migration destructive
+- [ ] toggle module directory (sidebar + route /directory)
+
+## Suite phase 2 (après 2.4)
+- 2.5 (#67) Module Mail — WIP `docs/mail-integration.md`, à traiter avec précaution.
+- 2.6 (#68) Module Integration (Gitea/BookStack/Zimbra/Share).
diff --git a/frontend/components/admin/AdminClientTab.vue b/frontend/components/admin/AdminClientTab.vue
index 12d3863..36af726 100644
--- a/frontend/components/admin/AdminClientTab.vue
+++ b/frontend/components/admin/AdminClientTab.vue
@@ -40,8 +40,8 @@
diff --git a/frontend/modules/directory/nuxt.config.ts b/frontend/modules/directory/nuxt.config.ts
new file mode 100644
index 0000000..268da7f
--- /dev/null
+++ b/frontend/modules/directory/nuxt.config.ts
@@ -0,0 +1 @@
+export default defineNuxtConfig({})
diff --git a/frontend/modules/directory/pages/directory.vue b/frontend/modules/directory/pages/directory.vue
new file mode 100644
index 0000000..2bdaae3
--- /dev/null
+++ b/frontend/modules/directory/pages/directory.vue
@@ -0,0 +1,240 @@
+
+
+
+ {{ $t('directory.title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ (item as Client).email ?? '—' }}
+
+
+ {{ (item as Client).phone ?? '—' }}
+
+
+ {{ (item as Client).city ?? '—' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ (item as ProspectRow).email ?? '—' }}
+
+
+ {{ (item as ProspectRow).phone ?? '—' }}
+
+
+
+
+
+ —
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/services/clients.ts b/frontend/modules/directory/services/clients.ts
similarity index 100%
rename from frontend/services/clients.ts
rename to frontend/modules/directory/services/clients.ts
diff --git a/frontend/services/dto/client.ts b/frontend/modules/directory/services/dto/client.ts
similarity index 100%
rename from frontend/services/dto/client.ts
rename to frontend/modules/directory/services/dto/client.ts
diff --git a/frontend/modules/directory/services/dto/prospect.ts b/frontend/modules/directory/services/dto/prospect.ts
new file mode 100644
index 0000000..17a8f66
--- /dev/null
+++ b/frontend/modules/directory/services/dto/prospect.ts
@@ -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
+}
diff --git a/frontend/modules/directory/services/prospects.ts b/frontend/modules/directory/services/prospects.ts
new file mode 100644
index 0000000..a07e9b0
--- /dev/null
+++ b/frontend/modules/directory/services/prospects.ts
@@ -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 {
+ const query: Record = {}
+ if (status) query.status = status
+ const data = await api.get>('/prospects', query)
+ return extractHydraMembers(data)
+ }
+
+ async function getById(id: number): Promise {
+ return api.get(`/prospects/${id}`)
+ }
+
+ async function create(payload: ProspectWrite): Promise {
+ return api.post('/prospects', payload as Record, {
+ toastSuccessKey: 'prospects.created',
+ })
+ }
+
+ async function update(id: number, payload: Partial): Promise {
+ return api.patch(`/prospects/${id}`, payload as Record, {
+ toastSuccessKey: 'prospects.updated',
+ })
+ }
+
+ async function remove(id: number): Promise {
+ await api.delete(`/prospects/${id}`, {}, {
+ toastSuccessKey: 'prospects.deleted',
+ })
+ }
+
+ async function convert(id: number): Promise {
+ return api.post(`/prospects/${id}/convert`, {}, {
+ toastSuccessKey: 'prospects.converted',
+ })
+ }
+
+ return { getAll, getById, create, update, remove, convert }
+}
diff --git a/frontend/modules/project-management/components/ProjectDrawer.vue b/frontend/modules/project-management/components/ProjectDrawer.vue
index cd9aef4..0cef9b8 100644
--- a/frontend/modules/project-management/components/ProjectDrawer.vue
+++ b/frontend/modules/project-management/components/ProjectDrawer.vue
@@ -124,7 +124,7 @@