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>
132 lines
4.3 KiB
Vue
132 lines
4.3 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 UserFormData = {
|
|
name: string;
|
|
email: string;
|
|
password?: string;
|
|
password_confirmation?: string;
|
|
group: string;
|
|
};
|
|
|
|
type Props = {
|
|
form: Form<UserFormData>;
|
|
userGroups: Record<string, string>;
|
|
showPasswordFields?: boolean;
|
|
passwordRequired?: boolean;
|
|
submitLabel?: string;
|
|
};
|
|
|
|
withDefaults(defineProps<Props>(), {
|
|
showPasswordFields: true,
|
|
passwordRequired: true,
|
|
submitLabel: 'Save',
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
submit: [];
|
|
}>();
|
|
</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
|
|
autocomplete="name"
|
|
placeholder="Full name"
|
|
aria-invalid="!!form.errors.name"
|
|
/>
|
|
<InputError :message="form.errors.name" />
|
|
</div>
|
|
|
|
<div class="grid gap-2">
|
|
<Label for="email">Email address</Label>
|
|
<Input
|
|
id="email"
|
|
v-model="form.email"
|
|
type="email"
|
|
required
|
|
autocomplete="email"
|
|
placeholder="email@example.com"
|
|
aria-invalid="!!form.errors.email"
|
|
/>
|
|
<InputError :message="form.errors.email" />
|
|
</div>
|
|
|
|
<div v-if="showPasswordFields" class="grid gap-2">
|
|
<Label for="password">Password</Label>
|
|
<Input
|
|
id="password"
|
|
v-model="form.password"
|
|
type="password"
|
|
:required="passwordRequired"
|
|
autocomplete="new-password"
|
|
:placeholder="
|
|
passwordRequired
|
|
? 'Password'
|
|
: 'Leave blank to keep current'
|
|
"
|
|
aria-invalid="!!form.errors.password"
|
|
/>
|
|
<InputError :message="form.errors.password" />
|
|
</div>
|
|
|
|
<div v-if="showPasswordFields" class="grid gap-2">
|
|
<Label for="password_confirmation">Confirm password</Label>
|
|
<Input
|
|
id="password_confirmation"
|
|
v-model="form.password_confirmation"
|
|
type="password"
|
|
:required="passwordRequired"
|
|
autocomplete="new-password"
|
|
placeholder="Confirm password"
|
|
aria-invalid="!!form.errors.password_confirmation"
|
|
/>
|
|
<InputError :message="form.errors.password_confirmation" />
|
|
</div>
|
|
|
|
<div class="grid gap-2">
|
|
<Label for="group">Group</Label>
|
|
<select
|
|
id="group"
|
|
v-model="form.group"
|
|
required
|
|
class="h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs outline-none placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
|
|
:aria-invalid="!!form.errors.group"
|
|
>
|
|
<option value="" disabled>Select a group</option>
|
|
<option
|
|
v-for="(label, value) in userGroups"
|
|
:key="value"
|
|
:value="value"
|
|
>
|
|
{{ label }}
|
|
</option>
|
|
</select>
|
|
<InputError :message="form.errors.group" />
|
|
</div>
|
|
|
|
<div class="flex items-center gap-4">
|
|
<Button
|
|
type="submit"
|
|
:disabled="form.processing"
|
|
data-test="user-form-submit"
|
|
>
|
|
<Spinner v-if="form.processing" />
|
|
{{ submitLabel }}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</template>
|