272 lines
6.9 KiB
Markdown
272 lines
6.9 KiB
Markdown
# Client Creation Flow - Complete Implementation
|
|
|
|
## Overview
|
|
Complete implementation of the client creation feature with:
|
|
- ✅ Form validation
|
|
- ✅ Error handling (Laravel validation errors)
|
|
- ✅ Loading states
|
|
- ✅ Success messages
|
|
- ✅ Auto-redirect after success
|
|
- ✅ Store integration
|
|
|
|
## Data Flow
|
|
|
|
```
|
|
AddClient.vue (Page)
|
|
↓ (passes props & handles events)
|
|
AddClientPresentation.vue (Organism)
|
|
↓ (passes props & emits)
|
|
NewClientForm.vue (Form Component)
|
|
↓ (user fills form & clicks submit)
|
|
↑ (emits createClient event)
|
|
AddClient.vue (receives event)
|
|
↓ (calls store)
|
|
clientStore.createClient()
|
|
↓ (API call)
|
|
Laravel ClientController
|
|
↓ (validates with StoreClientRequest)
|
|
↓ (creates client or returns 422 errors)
|
|
↑ (returns success or validation errors)
|
|
AddClient.vue (handles response)
|
|
↓ (passes validation errors back to form)
|
|
NewClientForm.vue (displays errors under inputs)
|
|
```
|
|
|
|
## Components Updated
|
|
|
|
### 1. AddClient.vue (Page Component)
|
|
**Location:** `src/views/pages/CRM/AddClient.vue`
|
|
|
|
**Responsibilities:**
|
|
- Fetch client categories on mount
|
|
- Handle form submission via `handleCreateClient`
|
|
- Call store to create client
|
|
- Handle validation errors from API (422 status)
|
|
- Show success message
|
|
- Redirect to clients list after success
|
|
|
|
**Key Code:**
|
|
```vue
|
|
<script setup>
|
|
const handleCreateClient = async (form) => {
|
|
try {
|
|
validationErrors.value = {};
|
|
showSuccess.value = false;
|
|
|
|
const client = await clientStore.createClient(form);
|
|
showSuccess.value = true;
|
|
|
|
setTimeout(() => {
|
|
router.push({ name: 'Clients' });
|
|
}, 2000);
|
|
|
|
} catch (error) {
|
|
if (error.response && error.response.status === 422) {
|
|
validationErrors.value = error.response.data.errors || {};
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
```
|
|
|
|
### 2. AddClientPresentation.vue (Organism)
|
|
**Location:** `src/components/Organism/CRM/AddClientPresentation.vue`
|
|
|
|
**Responsibilities:**
|
|
- Pass props to NewClientForm
|
|
- Relay events from form to parent
|
|
|
|
**Props Added:**
|
|
- `loading`: Boolean - loading state from store
|
|
- `validationErrors`: Object - validation errors from API
|
|
- `success`: Boolean - success state
|
|
|
|
### 3. NewClientForm.vue (Form Component)
|
|
**Location:** `src/components/molecules/form/NewClientForm.vue`
|
|
|
|
**Responsibilities:**
|
|
- Display form fields with proper validation styling
|
|
- Watch for validation errors from parent
|
|
- Display errors below each input
|
|
- Show loading spinner on button
|
|
- Show success alert
|
|
- Emit createClient event with form data
|
|
- Reset form on success
|
|
|
|
**Key Features:**
|
|
```vue
|
|
// Watch for validation errors
|
|
watch(() => props.validationErrors, (newErrors) => {
|
|
fieldErrors.value = { ...newErrors };
|
|
}, { deep: true });
|
|
|
|
// Watch for success
|
|
watch(() => props.success, (newSuccess) => {
|
|
if (newSuccess) {
|
|
resetForm();
|
|
}
|
|
});
|
|
|
|
// Submit form
|
|
const submitForm = async () => {
|
|
fieldErrors.value = {};
|
|
errors.value = [];
|
|
emit("createClient", form);
|
|
};
|
|
```
|
|
|
|
**Validation Error Display:**
|
|
```vue
|
|
<soft-input
|
|
v-model="form.name"
|
|
:class="{ 'is-invalid': fieldErrors.name }"
|
|
type="text"
|
|
/>
|
|
<div v-if="fieldErrors.name" class="invalid-feedback">
|
|
{{ fieldErrors.name }}
|
|
</div>
|
|
```
|
|
|
|
## Laravel Backend
|
|
|
|
### StoreClientRequest Validation Rules
|
|
|
|
```php
|
|
public function rules(): array
|
|
{
|
|
return [
|
|
'client_category_id' => 'nullable',
|
|
'name' => 'required|string|max:255',
|
|
'vat_number' => 'nullable|string|max:32',
|
|
'siret' => 'nullable|string|max:20',
|
|
'email' => 'nullable|email|max:191',
|
|
'phone' => 'nullable|string|max:50',
|
|
'billing_address_line1' => 'nullable|string|max:255',
|
|
'billing_address_line2' => 'nullable|string|max:255',
|
|
'billing_postal_code' => 'nullable|string|max:20',
|
|
'billing_city' => 'nullable|string|max:191',
|
|
'billing_country_code' => 'nullable|string|size:2',
|
|
'group_id' => 'nullable|exists:client_groups,id',
|
|
'notes' => 'nullable|string',
|
|
'is_active' => 'boolean',
|
|
'default_tva_rate_id' => 'nullable|exists:tva_rates,id',
|
|
];
|
|
}
|
|
```
|
|
|
|
## Test Data (Postman)
|
|
|
|
```json
|
|
{
|
|
"client_category_id": 1,
|
|
"name": "SARL TechnoPlus",
|
|
"vat_number": "FR98765432109",
|
|
"siret": "98765432100019",
|
|
"email": "compta@technoplus.fr",
|
|
"phone": "+33198765432",
|
|
"billing_address_line1": "789 Boulevard Haussmann",
|
|
"billing_postal_code": "75009",
|
|
"billing_city": "Paris",
|
|
"billing_country_code": "FR",
|
|
"notes": "Nouveau client entreprise",
|
|
"is_active": true
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Validation Errors (422)
|
|
When Laravel returns validation errors:
|
|
```json
|
|
{
|
|
"message": "The given data was invalid.",
|
|
"errors": {
|
|
"name": ["Le nom du client est obligatoire."],
|
|
"email": ["L'adresse email doit être valide."]
|
|
}
|
|
}
|
|
```
|
|
|
|
These are automatically displayed below each input field.
|
|
|
|
### Server Errors (500)
|
|
When server error occurs:
|
|
- Alert is shown with error message
|
|
- User can retry
|
|
|
|
### Network Errors
|
|
When network is unavailable:
|
|
- Alert is shown with generic error message
|
|
|
|
## User Experience Flow
|
|
|
|
1. **User fills the form**
|
|
- All fields are validated client-side (maxlength, email format)
|
|
|
|
2. **User clicks "Créer le client"**
|
|
- Button shows loading spinner
|
|
- Button is disabled
|
|
- Previous errors are cleared
|
|
|
|
3. **If validation fails (422)**
|
|
- Errors appear below each invalid field in red
|
|
- Loading stops
|
|
- Button is re-enabled
|
|
- User can fix errors and resubmit
|
|
|
|
4. **If creation succeeds**
|
|
- Success message appears in green
|
|
- Form is reset
|
|
- After 2 seconds, user is redirected to clients list
|
|
|
|
## Required Fields
|
|
|
|
Only **name** is required. All other fields are optional.
|
|
|
|
- ✅ Name: Required (max 255 chars)
|
|
- ⭕ Email: Optional but must be valid email format
|
|
- ⭕ VAT Number: Optional (max 32 chars)
|
|
- ⭕ SIRET: Optional (max 20 chars)
|
|
- ⭕ Phone: Optional (max 50 chars)
|
|
- ⭕ Address fields: Optional
|
|
- ⭕ Country code: Optional (must be 2 chars if provided)
|
|
- ⭕ Notes: Optional
|
|
- ✅ Active status: Defaults to true
|
|
|
|
## CSS Classes for Validation
|
|
|
|
```css
|
|
.is-invalid {
|
|
border-color: #f5365c !important;
|
|
}
|
|
|
|
.invalid-feedback {
|
|
display: block;
|
|
color: #f5365c;
|
|
font-size: 0.875rem;
|
|
margin-top: 0.25rem;
|
|
}
|
|
```
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] Submit empty form → "name" required error shows
|
|
- [ ] Submit with invalid email → email validation error shows
|
|
- [ ] Submit with VAT > 32 chars → VAT length error shows
|
|
- [ ] Submit with SIRET > 20 chars → SIRET length error shows
|
|
- [ ] Submit valid data → Success message & redirect
|
|
- [ ] Check loading spinner appears during submission
|
|
- [ ] Check button is disabled during submission
|
|
- [ ] Check form resets after success
|
|
- [ ] Check redirect happens after 2 seconds
|
|
|
|
## Future Enhancements
|
|
|
|
- [ ] Add client category dropdown (currently just ID)
|
|
- [ ] Add client group dropdown
|
|
- [ ] Add TVA rate dropdown
|
|
- [ ] Add real-time validation as user types
|
|
- [ ] Add confirmation modal before submit
|
|
- [ ] Add ability to create contact at same time
|
|
- [ ] Add file upload for documents
|