180 lines
7.5 KiB
Vue
180 lines
7.5 KiB
Vue
<script setup lang="ts">
|
|
import { Form, Head } from '@inertiajs/vue3';
|
|
import { Shield } from 'lucide-vue-next';
|
|
import TeamController from '@/actions/App/Http/Controllers/TeamController';
|
|
import Heading from '@/components/Heading.vue';
|
|
import InputError from '@/components/InputError.vue';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from '@/components/ui/card';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import AppLayout from '@/layouts/AppLayout.vue';
|
|
import { index as teamsIndex } from '@/routes/admin/teams';
|
|
import type { BreadcrumbItem, TeamsIndexPageProps } from '@/types';
|
|
|
|
defineProps<TeamsIndexPageProps>();
|
|
|
|
const breadcrumbs: BreadcrumbItem[] = [
|
|
{
|
|
title: 'Teams',
|
|
href: teamsIndex(),
|
|
},
|
|
];
|
|
</script>
|
|
|
|
<template>
|
|
<AppLayout :breadcrumbs="breadcrumbs">
|
|
<Head title="Teams" />
|
|
|
|
<div class="flex flex-1 flex-col gap-6 p-4">
|
|
<div
|
|
class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between"
|
|
>
|
|
<Heading
|
|
title="Teams"
|
|
description="Create reusable teams and manage member assignments for inherited project permissions."
|
|
/>
|
|
|
|
<Badge variant="outline" class="gap-1.5 px-3 py-1">
|
|
<Shield class="size-3.5" />
|
|
Admin-only area
|
|
</Badge>
|
|
</div>
|
|
|
|
<div
|
|
v-if="status"
|
|
class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900 dark:border-emerald-900/70 dark:bg-emerald-950/40 dark:text-emerald-100"
|
|
>
|
|
{{ status }}
|
|
</div>
|
|
|
|
<div class="grid gap-6 xl:grid-cols-[22rem_minmax(0,1fr)]">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Create team</CardTitle>
|
|
<CardDescription>
|
|
Teams group users so project permissions can be
|
|
granted once and inherited by every member.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
|
|
<CardContent>
|
|
<Form
|
|
v-bind="TeamController.store.form()"
|
|
class="space-y-4"
|
|
v-slot="{ errors, processing }"
|
|
>
|
|
<div class="grid gap-2">
|
|
<Label for="name">Team name</Label>
|
|
<Input
|
|
id="name"
|
|
name="name"
|
|
required
|
|
autofocus
|
|
placeholder="Platform Team"
|
|
/>
|
|
<InputError :message="errors.name" />
|
|
</div>
|
|
|
|
<Button type="submit" :disabled="processing">
|
|
Create team
|
|
</Button>
|
|
</Form>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<div class="grid gap-4">
|
|
<Card
|
|
v-for="team in teams"
|
|
:key="team.id"
|
|
class="gap-0 overflow-hidden"
|
|
>
|
|
<CardHeader class="border-b">
|
|
<div
|
|
class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between"
|
|
>
|
|
<div>
|
|
<CardTitle>{{ team.name }}</CardTitle>
|
|
<CardDescription>
|
|
{{ team.users.length }} member{{
|
|
team.users.length === 1 ? '' : 's'
|
|
}}
|
|
</CardDescription>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
<Badge
|
|
v-for="member in team.users"
|
|
:key="member.id"
|
|
variant="secondary"
|
|
>
|
|
{{ member.name }}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
<CardContent class="pt-6">
|
|
<Form
|
|
v-bind="
|
|
TeamController.updateMembers.form(team.id)
|
|
"
|
|
class="space-y-4"
|
|
v-slot="{ processing }"
|
|
>
|
|
<div class="grid gap-2">
|
|
<Label :for="`team-members-${team.id}`">
|
|
Members
|
|
</Label>
|
|
<select
|
|
:id="`team-members-${team.id}`"
|
|
name="user_ids[]"
|
|
multiple
|
|
class="min-h-44 rounded-md border border-input bg-background px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
|
|
>
|
|
<option
|
|
v-for="user in users"
|
|
:key="user.id"
|
|
:value="user.id"
|
|
:selected="
|
|
team.user_ids.includes(user.id)
|
|
"
|
|
>
|
|
{{ user.name }} ({{ user.email }})
|
|
</option>
|
|
</select>
|
|
<p class="text-sm text-muted-foreground">
|
|
Hold Cmd or Ctrl to select multiple
|
|
users.
|
|
</p>
|
|
</div>
|
|
|
|
<Button
|
|
type="submit"
|
|
variant="outline"
|
|
:disabled="processing"
|
|
>
|
|
Save members
|
|
</Button>
|
|
</Form>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card v-if="teams.length === 0">
|
|
<CardContent class="p-6 text-sm text-muted-foreground">
|
|
No teams exist yet.
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
</template>
|