179 lines
7.2 KiB
Vue
179 lines
7.2 KiB
Vue
<script setup lang="ts">
|
|
import { Link } from '@inertiajs/vue3';
|
|
import { ExternalLink, GitBranch } from 'lucide-vue-next';
|
|
import { computed } from 'vue';
|
|
import FeedbackState from '@/components/FeedbackState.vue';
|
|
import TicketBranchCreateDialog from '@/components/tickets/detail/TicketBranchCreateDialog.vue';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from '@/components/ui/card';
|
|
import { useLocalization } from '@/composables/useLocalization';
|
|
import { edit as editGitConnections } from '@/routes/git-connections';
|
|
import type { RepositoryConnectionModel } from '@/types/models/repository-connection-model';
|
|
import type { TicketGitBranchesProps } from '@/types/ticket-detail';
|
|
|
|
const props = defineProps<TicketGitBranchesProps>();
|
|
const { t } = useLocalization();
|
|
|
|
const visibleRepositories = computed(() =>
|
|
props.repositoryBranches.filter(
|
|
(repositoryBranch) =>
|
|
repositoryBranch.branches.length > 0 ||
|
|
repositoryBranch.error !== null ||
|
|
!repositoryBranch.repository_connection.has_personal_access,
|
|
),
|
|
);
|
|
const repositoryConnections = computed<RepositoryConnectionModel[]>(() =>
|
|
props.repositoryBranches.map(
|
|
(repositoryBranch) => repositoryBranch.repository_connection,
|
|
),
|
|
);
|
|
const canCreateBranch = computed(
|
|
() => props.canEditTicket && repositoryConnections.value.length > 0,
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<Card class="min-w-0 gap-0 overflow-hidden">
|
|
<CardHeader
|
|
class="flex flex-col gap-4 border-b sm:flex-row sm:items-start sm:justify-between"
|
|
>
|
|
<div class="grid min-w-0 gap-1.5">
|
|
<CardTitle class="flex items-center gap-2">
|
|
<GitBranch
|
|
class="text-primary size-5 shrink-0"
|
|
aria-hidden="true"
|
|
/>
|
|
{{ t('ticketDetail.branches') }}
|
|
</CardTitle>
|
|
<CardDescription
|
|
>{ t('ticketDetail.branchesDescription') }}</CardDescription
|
|
>
|
|
</div>
|
|
|
|
<TicketBranchCreateDialog
|
|
v-if="canCreateBranch"
|
|
:ticket="ticket"
|
|
:project="project"
|
|
:repository-connections="repositoryConnections"
|
|
:suggested-branch-name="suggestedBranchName"
|
|
/>
|
|
</CardHeader>
|
|
|
|
<CardContent class="grid gap-4 pt-6">
|
|
<FeedbackState
|
|
v-if="repositoryBranches.length === 0"
|
|
state="empty"
|
|
:title="t('ticketDetail.noLinkedRepositories')"
|
|
:description="t('ticketDetail.noLinkedRepositoriesDescription')"
|
|
/>
|
|
|
|
<FeedbackState
|
|
v-else-if="visibleRepositories.length === 0"
|
|
state="empty"
|
|
:title="t('tickets.noBranches')"
|
|
:description="t('tickets.noBranchesDescription')"
|
|
/>
|
|
|
|
<article
|
|
v-for="repositoryBranch in visibleRepositories"
|
|
v-else
|
|
:key="repositoryBranch.repository_connection.id"
|
|
class="border-border grid min-w-0 gap-4 rounded-lg border p-4"
|
|
>
|
|
<header class="grid min-w-0 gap-2">
|
|
<div class="flex min-w-0 flex-wrap items-center gap-2">
|
|
<Badge variant="secondary">
|
|
{{
|
|
repositoryBranch.repository_connection
|
|
.provider_label
|
|
}}
|
|
</Badge>
|
|
<a
|
|
:href="
|
|
repositoryBranch.repository_connection
|
|
.repository_url
|
|
"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
:aria-label="
|
|
t('ticketDetail.openRepositoryExternal', {
|
|
repository:
|
|
repositoryBranch.repository_connection
|
|
.repository_full_name,
|
|
provider:
|
|
repositoryBranch.repository_connection
|
|
.provider_label,
|
|
})
|
|
"
|
|
class="hover:text-primary focus-visible:ring-ring/35 group flex min-w-0 items-center gap-1.5 rounded-sm font-medium outline-none transition-colors focus-visible:ring-2"
|
|
>
|
|
<span class="wrap-anywhere">{{
|
|
repositoryBranch.repository_connection
|
|
.repository_full_name
|
|
}}</span>
|
|
<ExternalLink
|
|
class="size-3.5 shrink-0 opacity-60 transition-opacity group-hover:opacity-100"
|
|
aria-hidden="true"
|
|
/>
|
|
</a>
|
|
</div>
|
|
<p class="text-muted-foreground text-xs">
|
|
{{ repositoryBranch.repository_connection.base_url }}
|
|
</p>
|
|
</header>
|
|
|
|
<FeedbackState
|
|
v-if="
|
|
!repositoryBranch.repository_connection
|
|
.has_personal_access
|
|
"
|
|
state="empty"
|
|
:title="t('ticketDetail.gitAccessRequired')"
|
|
:description="t('ticketDetail.inspectBranches')"
|
|
>
|
|
<Button
|
|
as-child
|
|
variant="outline"
|
|
size="sm"
|
|
class="justify-self-start"
|
|
>
|
|
<Link :href="editGitConnections()">{{
|
|
t('ticketDetail.openGitConnections')
|
|
}}</Link>
|
|
</Button>
|
|
</FeedbackState>
|
|
|
|
<FeedbackState
|
|
v-else-if="repositoryBranch.error"
|
|
state="error"
|
|
:title="t('ticketDetail.branchesUnavailable')"
|
|
:description="repositoryBranch.error"
|
|
/>
|
|
|
|
<ul v-else class="grid min-w-0 gap-2 sm:grid-cols-2">
|
|
<li
|
|
v-for="branch in repositoryBranch.branches"
|
|
:key="branch.name"
|
|
class="bg-muted/30 flex min-w-0 items-center gap-2 rounded-md border px-3 py-2"
|
|
>
|
|
<GitBranch
|
|
class="text-muted-foreground size-3.5 shrink-0"
|
|
aria-hidden="true"
|
|
/>
|
|
<span class="wrap-anywhere font-mono text-xs">{{
|
|
branch.name
|
|
}}</span>
|
|
</li>
|
|
</ul>
|
|
</article>
|
|
</CardContent>
|
|
</Card>
|
|
</template>
|