Files
KanbanGit/resources/js/pages/teams/Index.vue
T
Twyco b330ea4773
linter / quality (pull_request) Successful in 1m14s
tests / ci (8.4) (pull_request) Successful in 1m17s
linter
2026-03-26 20:51:31 +01:00

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>