Files
L-Ami-Fiduciaire/resources/js/components/FolderForm.vue
Saad Zoubir 7a18c40361 chore: add BMAD framework modules, folder features, and tooling configs
Includes BMAD bmb/bmm/cis/tea workflow modules, folder (declaration)
feature implementation (controllers, models, enums, views, tests),
claude/cursor command configs, and email templates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 21:24:17 +01:00

310 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { Form } from '@inertiajs/vue3';
import { watch } from 'vue';
import InputError from '@/components/InputError.vue';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner';
export type FolderFormData = {
client_id: number | '';
title: string;
type: string;
period_year: number | string;
period_month: number | string;
period_quarter: number | string;
due_date: string;
status: string;
priority: string;
assigned_to: number | '';
notes_internal: string;
notes_client: string;
};
type Client = {
id: number;
company_name: string;
};
type WorkspaceUser = {
id: number;
name: string;
email: string;
};
type Props = {
form: Form<FolderFormData>;
folderTypeLabels: Record<string, string>;
folderStatusLabels: Record<string, string>;
folderPriorityLabels: Record<string, string>;
clients: Client[];
workspaceUsers: WorkspaceUser[];
submitLabel?: string;
};
const props = withDefaults(defineProps<Props>(), {
submitLabel: 'Enregistrer',
});
const emit = defineEmits<{
submit: [];
}>();
const currentYear = new Date().getFullYear();
const years = Array.from({ length: 10 }, (_, i) => currentYear - 2 + i);
const months = [
{ value: '', label: '—' },
...Array.from({ length: 12 }, (_, i) => ({
value: (i + 1).toString(),
label: `${i + 1}`,
})),
];
const quarters = [
{ value: '', label: '—' },
{ value: '1', label: 'T1 (JanMar)' },
{ value: '2', label: 'T2 (AvrJuin)' },
{ value: '3', label: 'T3 (JuilSep)' },
{ value: '4', label: 'T4 (OctDéc)' },
];
const isVatMonthly = () => props.form.type === 'vat_monthly';
const isVatQuarterly = () => props.form.type === 'vat_quarterly';
// Clear both period fields when type changes
watch(
() => props.form.type,
() => {
props.form.period_month = '';
props.form.period_quarter = '';
},
);
</script>
<template>
<form @submit.prevent="emit('submit')" class="flex flex-col space-y-6">
<div class="grid gap-2">
<Label for="client_id">Client</Label>
<select
id="client_id"
v-model="form.client_id"
required
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.client_id"
>
<option value="" disabled>Sélectionner un client</option>
<option
v-for="client in clients"
:key="client.id"
:value="client.id"
>
{{ client.company_name }}
</option>
</select>
<InputError :message="form.errors.client_id" />
</div>
<div class="grid gap-2">
<Label for="title">Titre</Label>
<Input
id="title"
v-model="form.title"
type="text"
required
placeholder="Ex. Déclaration TVA - T1 2026"
aria-invalid="!!form.errors.title"
/>
<InputError :message="form.errors.title" />
</div>
<div class="grid gap-2">
<Label for="type">Type</Label>
<select
id="type"
v-model="form.type"
required
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.type"
>
<option value="" disabled>Sélectionner un type</option>
<option
v-for="(label, value) in folderTypeLabels"
:key="value"
:value="value"
>
{{ label }}
</option>
</select>
<InputError :message="form.errors.type" />
</div>
<div class="grid gap-2 sm:grid-cols-3">
<div class="grid gap-2">
<Label for="period_year">Année</Label>
<select
id="period_year"
v-model="form.period_year"
required
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.period_year"
>
<option v-for="y in years" :key="y" :value="y">
{{ y }}
</option>
</select>
<InputError :message="form.errors.period_year" />
</div>
<template v-if="isVatMonthly()">
<div class="grid gap-2">
<Label for="period_month">Mois</Label>
<select
id="period_month"
v-model="form.period_month"
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.period_month"
>
<option
v-for="m in months"
:key="m.value"
:value="m.value"
>
{{ m.label }}
</option>
</select>
<InputError :message="form.errors.period_month" />
</div>
</template>
<template v-if="isVatQuarterly()">
<div class="grid gap-2">
<Label for="period_quarter">Trimestre</Label>
<select
id="period_quarter"
v-model="form.period_quarter"
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.period_quarter"
>
<option
v-for="q in quarters"
:key="q.value"
:value="q.value"
>
{{ q.label }}
</option>
</select>
<InputError :message="form.errors.period_quarter" />
</div>
</template>
</div>
<div class="grid gap-2 sm:grid-cols-2">
<div class="grid gap-2">
<Label for="due_date">Date limite</Label>
<Input
id="due_date"
v-model="form.due_date"
type="date"
aria-invalid="!!form.errors.due_date"
/>
<InputError :message="form.errors.due_date" />
</div>
<div class="grid gap-2">
<Label for="status">Statut</Label>
<select
id="status"
v-model="form.status"
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.status"
>
<option value="" disabled>Sélectionner un statut</option>
<option
v-for="(label, value) in folderStatusLabels"
:key="value"
:value="value"
>
{{ label }}
</option>
</select>
<InputError :message="form.errors.status" />
</div>
</div>
<div class="grid gap-2 sm:grid-cols-2">
<div class="grid gap-2">
<Label for="priority">Priorité</Label>
<select
id="priority"
v-model="form.priority"
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.priority"
>
<option value=""></option>
<option
v-for="(label, value) in folderPriorityLabels"
:key="value"
:value="value"
>
{{ label }}
</option>
</select>
<InputError :message="form.errors.priority" />
</div>
<div class="grid gap-2">
<Label for="assigned_to">Assigné à</Label>
<select
id="assigned_to"
v-model="form.assigned_to"
class="border-input bg-background h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
:aria-invalid="!!form.errors.assigned_to"
>
<option :value="''"></option>
<option
v-for="user in workspaceUsers"
:key="user.id"
:value="user.id"
>
{{ user.name }} ({{ user.email }})
</option>
</select>
<InputError :message="form.errors.assigned_to" />
</div>
</div>
<div class="grid gap-2">
<Label for="notes_internal">Notes internes</Label>
<textarea
id="notes_internal"
v-model="form.notes_internal"
rows="3"
class="border-input bg-background w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
placeholder="Notes confidentielles"
:aria-invalid="!!form.errors.notes_internal"
/>
<InputError :message="form.errors.notes_internal" />
</div>
<div class="grid gap-2">
<Label for="notes_client">Notes client</Label>
<textarea
id="notes_client"
v-model="form.notes_client"
rows="3"
class="border-input bg-background w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:ring-ring"
placeholder="Notes partagées avec le client"
:aria-invalid="!!form.errors.notes_client"
/>
<InputError :message="form.errors.notes_client" />
</div>
<div class="flex items-center gap-4">
<Button
type="submit"
:disabled="form.processing"
data-test="folder-form-submit"
>
<Spinner v-if="form.processing" />
{{ submitLabel }}
</Button>
</div>
</form>
</template>