159 lines
6.1 KiB
Vue
159 lines
6.1 KiB
Vue
<script setup lang="ts">
|
|
import { Form, Link } from '@inertiajs/vue3';
|
|
import ProjectTicketLinkController from '@/actions/App/Http/Controllers/ProjectTicketLinkController';
|
|
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 { Label } from '@/components/ui/label';
|
|
import { frostedAsideCardClass, nativeSelectClass } from '@/lib/ticket-styles';
|
|
import { show as showTicket } from '@/routes/projects/tickets';
|
|
import type { TicketLinkModel } from '@/types/models/ticket-link-model';
|
|
import type { TicketLinkTypeModel } from '@/types/models/ticket-link-type-model';
|
|
import type { TicketReferenceModel } from '@/types/models/ticket-reference-model';
|
|
|
|
defineProps<{
|
|
canEditTicket: boolean;
|
|
linkedTickets: TicketLinkModel[];
|
|
projectId: number;
|
|
ticketId: number;
|
|
ticketLinkOptions: TicketReferenceModel[];
|
|
ticketLinkTypes: TicketLinkTypeModel[];
|
|
}>();
|
|
</script>
|
|
|
|
<template>
|
|
<Card :class="frostedAsideCardClass">
|
|
<CardHeader>
|
|
<CardTitle>Linked tickets</CardTitle>
|
|
<CardDescription>
|
|
Track cross-ticket relationships with project-defined link
|
|
labels.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
|
|
<CardContent class="space-y-4">
|
|
<Form
|
|
v-if="
|
|
canEditTicket &&
|
|
ticketLinkTypes.length > 0 &&
|
|
ticketLinkOptions.length > 0
|
|
"
|
|
:action="
|
|
ProjectTicketLinkController.store.url({
|
|
project: projectId,
|
|
ticket: ticketId,
|
|
})
|
|
"
|
|
class="border-border grid gap-4 rounded-xl border border-dashed p-4"
|
|
method="post"
|
|
v-slot="{ errors, processing }"
|
|
>
|
|
<div class="grid gap-2">
|
|
<Label for="ticket_link_type_id">Link type</Label>
|
|
<select
|
|
id="ticket_link_type_id"
|
|
name="ticket_link_type_id"
|
|
:class="nativeSelectClass"
|
|
>
|
|
<option
|
|
v-for="ticketLinkType in ticketLinkTypes"
|
|
:key="ticketLinkType.id"
|
|
:value="ticketLinkType.id"
|
|
>
|
|
{{ ticketLinkType.label }}
|
|
</option>
|
|
</select>
|
|
<InputError :message="errors.ticket_link_type_id" />
|
|
</div>
|
|
|
|
<div class="grid gap-2">
|
|
<Label for="linked_ticket_id">Linked ticket</Label>
|
|
<select
|
|
id="linked_ticket_id"
|
|
name="linked_ticket_id"
|
|
:class="nativeSelectClass"
|
|
>
|
|
<option
|
|
v-for="ticketLinkOption in ticketLinkOptions"
|
|
:key="ticketLinkOption.id"
|
|
:value="ticketLinkOption.id"
|
|
>
|
|
{{
|
|
`${ticketLinkOption.ticket_key} - ${ticketLinkOption.title}`
|
|
}}
|
|
</option>
|
|
</select>
|
|
<InputError :message="errors.linked_ticket_id" />
|
|
</div>
|
|
|
|
<Button type="submit" :disabled="processing">Save link</Button>
|
|
</Form>
|
|
|
|
<div
|
|
v-if="linkedTickets.length === 0"
|
|
class="border-border text-muted-foreground rounded-xl border border-dashed px-4 py-6 text-sm"
|
|
>
|
|
No ticket links are configured for this ticket yet.
|
|
</div>
|
|
|
|
<div v-else class="space-y-3">
|
|
<div
|
|
v-for="linkedTicket in linkedTickets"
|
|
:key="linkedTicket.id"
|
|
class="border-border rounded-xl border px-4 py-4"
|
|
>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<Badge variant="secondary">
|
|
{{ linkedTicket.ticket_link_type.label }}
|
|
</Badge>
|
|
<Link
|
|
:href="
|
|
showTicket({
|
|
project: projectId,
|
|
ticket: linkedTicket.linked_ticket.id,
|
|
})
|
|
"
|
|
class="text-foreground hover:text-primary font-medium transition"
|
|
>
|
|
{{ linkedTicket.linked_ticket.ticket_key }}
|
|
</Link>
|
|
</div>
|
|
<p class="text-muted-foreground mt-2 text-sm">
|
|
{{ linkedTicket.linked_ticket.title }}
|
|
</p>
|
|
|
|
<Form
|
|
v-if="canEditTicket"
|
|
:action="
|
|
ProjectTicketLinkController.destroy.url({
|
|
project: projectId,
|
|
ticket: ticketId,
|
|
ticketLink: linkedTicket.id,
|
|
})
|
|
"
|
|
class="mt-3 flex justify-end"
|
|
method="delete"
|
|
v-slot="{ processing: deleting }"
|
|
>
|
|
<Button
|
|
type="submit"
|
|
variant="destructive"
|
|
size="sm"
|
|
:disabled="deleting"
|
|
>
|
|
Remove
|
|
</Button>
|
|
</Form>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</template>
|