Files
L-Ami-Fiduciaire/resources/js/components/WorkspaceForm.vue
Saad Ibn-Ezzoubayr 4807376c49 feat: implement Story 2.2 — Priority Alerts Panel with UI fixes
Add PriorityAlertsPanel component to the dashboard, update DashboardController
with alert logic, and apply misc UI fixes across sidebar, forms, and pages.
Includes epic-1 retrospective and sprint status update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:33:27 +00:00

172 lines
5.7 KiB
Vue

<script setup lang="ts">
/* eslint-disable vue/no-mutating-props -- Inertia useForm objects are reactive and designed to be mutated via props */
import type { Form } from '@inertiajs/vue3';
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 WorkspaceFormData = {
name: string;
slug: string;
user_ids: number[];
user_roles: Record<number, string>;
};
export type WorkspaceFormUser = {
id: number;
name: string;
email: string;
};
type Props = {
form: Form<WorkspaceFormData>;
users: WorkspaceFormUser[];
workspaceUserRoles: Record<string, string>;
submitLabel?: string;
};
const props = withDefaults(defineProps<Props>(), {
submitLabel: 'Save',
});
const emit = defineEmits<{
submit: [];
}>();
const defaultRole = 'worker';
function onUserToggle(userId: number, checked: boolean) {
if (checked) {
const ids = props.form.user_ids ?? [];
if (!ids.includes(userId)) {
props.form.user_ids = [...ids, userId];
}
const roles = props.form.user_roles ?? {};
props.form.user_roles = {
...roles,
[userId]: roles[userId] ?? defaultRole,
};
} else {
props.form.user_ids = (props.form.user_ids ?? []).filter(
(id) => id !== userId,
);
const roles = { ...(props.form.user_roles ?? {}) };
delete roles[userId];
props.form.user_roles = roles;
}
}
function onRoleChange(userId: number, role: string) {
const roles = props.form.user_roles ?? {};
props.form.user_roles = { ...roles, [userId]: role };
}
function isUserSelected(userId: number): boolean {
return (props.form.user_ids ?? []).includes(userId);
}
function getUserRole(userId: number): string {
return props.form.user_roles?.[userId] ?? defaultRole;
}
</script>
<template>
<form @submit.prevent="emit('submit')" class="flex flex-col space-y-6">
<div class="grid gap-2">
<Label for="name">Name</Label>
<Input
id="name"
v-model="form.name"
type="text"
required
placeholder="Cabinet Comptable XYZ"
aria-invalid="!!form.errors.name"
/>
<InputError :message="form.errors.name" />
</div>
<div class="grid gap-2">
<Label for="slug">Slug</Label>
<Input
id="slug"
v-model="form.slug"
type="text"
placeholder="cabinet-comptable-xyz (optional)"
aria-invalid="!!form.errors.slug"
/>
<p class="text-xs text-muted-foreground">
Leave empty to auto-generate from the name.
</p>
<InputError :message="form.errors.slug" />
</div>
<div v-if="users?.length" class="grid gap-2">
<Label>Users</Label>
<p class="text-xs text-muted-foreground">
Select users to add to this workspace.
</p>
<div
class="max-h-48 space-y-2 overflow-y-auto rounded-md border border-sidebar-border/70 p-3 dark:border-sidebar-border"
>
<div
v-for="user in users"
:key="user.id"
class="flex items-center gap-3 rounded px-2 py-1.5 hover:bg-muted/50"
>
<input
type="checkbox"
:value="user.id"
:checked="isUserSelected(user.id)"
class="size-4 shrink-0 rounded-[4px] border border-input focus-visible:ring-2 focus-visible:ring-ring"
@change="
onUserToggle(
user.id,
($event.target as HTMLInputElement).checked,
)
"
/>
<div class="flex min-w-0 flex-1 flex-col">
<span class="text-sm font-medium">{{ user.name }}</span>
<span class="text-xs text-muted-foreground">{{
user.email
}}</span>
</div>
<select
:value="getUserRole(user.id)"
:disabled="!isUserSelected(user.id)"
class="h-8 shrink-0 rounded-md border border-input bg-background px-2 text-sm disabled:opacity-50"
@change="
onRoleChange(
user.id,
($event.target as HTMLSelectElement).value,
)
"
>
<option
v-for="(label, value) in workspaceUserRoles"
:key="value"
:value="value"
>
{{ label }}
</option>
</select>
</div>
</div>
<InputError :message="form.errors.user_ids" />
</div>
<div class="flex items-center gap-4">
<Button
type="submit"
:disabled="form.processing"
data-test="workspace-form-submit"
>
<Spinner v-if="form.processing" />
{{ submitLabel }}
</Button>
</div>
</form>
</template>