From 7a4c6f386572bcead352fb38a6fb6b5c3d328296 Mon Sep 17 00:00:00 2001 From: ericasch6 Date: Mon, 14 Sep 2026 15:52:42 +0200 Subject: [PATCH] KAN-35 ticket changes --- .../detail/TicketBranchCreateDialog.vue | 416 +++++++++++ .../tickets/detail/TicketDetailGit.vue | 655 +----------------- .../tickets/detail/TicketGitBranches.vue | 178 +++++ .../tickets/detail/TicketGitPullRequests.vue | 191 +++++ resources/js/locales/en.ts | 12 + resources/js/types/ticket-detail.ts | 19 + tests/Fixtures/ticket-detail-browser.mjs | 41 ++ tests/Fixtures/ticket-detail.mjs | 27 +- 8 files changed, 893 insertions(+), 646 deletions(-) create mode 100644 resources/js/components/tickets/detail/TicketBranchCreateDialog.vue create mode 100644 resources/js/components/tickets/detail/TicketGitBranches.vue create mode 100644 resources/js/components/tickets/detail/TicketGitPullRequests.vue diff --git a/resources/js/components/tickets/detail/TicketBranchCreateDialog.vue b/resources/js/components/tickets/detail/TicketBranchCreateDialog.vue new file mode 100644 index 0000000..f767af0 --- /dev/null +++ b/resources/js/components/tickets/detail/TicketBranchCreateDialog.vue @@ -0,0 +1,416 @@ + + + diff --git a/resources/js/components/tickets/detail/TicketDetailGit.vue b/resources/js/components/tickets/detail/TicketDetailGit.vue index 4f2156b..e5da046 100644 --- a/resources/js/components/tickets/detail/TicketDetailGit.vue +++ b/resources/js/components/tickets/detail/TicketDetailGit.vue @@ -1,273 +1,12 @@ diff --git a/resources/js/components/tickets/detail/TicketGitBranches.vue b/resources/js/components/tickets/detail/TicketGitBranches.vue new file mode 100644 index 0000000..0872e30 --- /dev/null +++ b/resources/js/components/tickets/detail/TicketGitBranches.vue @@ -0,0 +1,178 @@ + + + diff --git a/resources/js/components/tickets/detail/TicketGitPullRequests.vue b/resources/js/components/tickets/detail/TicketGitPullRequests.vue new file mode 100644 index 0000000..dd3b4ca --- /dev/null +++ b/resources/js/components/tickets/detail/TicketGitPullRequests.vue @@ -0,0 +1,191 @@ + + + diff --git a/resources/js/locales/en.ts b/resources/js/locales/en.ts index b9134de..a9c57d7 100644 --- a/resources/js/locales/en.ts +++ b/resources/js/locales/en.ts @@ -491,8 +491,20 @@ export const en = { 'ticketDetail.branchName': 'Branch name', 'ticketDetail.branchAccess': 'You can only create branches in repositories that match one of your personal Git connections.', + 'ticketDetail.gitAccessRequired': 'Personal Git access required', + 'ticketDetail.personalAccessRequired': 'personal Git access required', 'ticketDetail.openGitConnections': 'Open Git connections', 'ticketDetail.createNow': 'Create now', + 'ticketDetail.creatingBranch': 'Creating branch…', + 'ticketDetail.noLinkedRepositories': 'No linked repositories', + 'ticketDetail.noLinkedRepositoriesDescription': + 'A project administrator needs to link a repository before Git activity can appear here.', + 'ticketDetail.branchesUnavailable': 'Branches unavailable', + 'ticketDetail.pullRequestsUnavailable': 'Pull requests unavailable', + 'ticketDetail.openRepositoryExternal': + 'Open {repository} on {provider} (opens in a new tab)', + 'ticketDetail.openPullRequestExternal': + 'Open pull request #{number} on {provider} (opens in a new tab)', 'ticketDetail.inspectBranches': 'Connect a matching Git account in your user settings to inspect branches for this repository.', 'ticketDetail.pullRequests': 'Pull requests', diff --git a/resources/js/types/ticket-detail.ts b/resources/js/types/ticket-detail.ts index 6fe0f86..a9489e6 100644 --- a/resources/js/types/ticket-detail.ts +++ b/resources/js/types/ticket-detail.ts @@ -1,4 +1,5 @@ import type { ProjectTicketPageProps } from '@/types/page-props'; +import type { RepositoryConnectionModel } from '@/types/models/repository-connection-model'; export type TicketDetailHeaderProps = Pick< ProjectTicketPageProps, 'ticket' | 'project' | 'canEditTicket' @@ -32,6 +33,24 @@ export type TicketDetailGitProps = Pick< | 'repositoryPullRequests' | 'suggestedBranchName' >; +export type TicketGitBranchesProps = Pick< + ProjectTicketPageProps, + | 'ticket' + | 'project' + | 'canEditTicket' + | 'repositoryBranches' + | 'suggestedBranchName' +>; +export type TicketGitPullRequestsProps = Pick< + ProjectTicketPageProps, + 'repositoryPullRequests' +>; +export type TicketBranchCreateDialogProps = Pick< + ProjectTicketPageProps, + 'ticket' | 'project' | 'suggestedBranchName' +> & { + repositoryConnections: RepositoryConnectionModel[]; +}; export type TicketDetailRelationshipsProps = Pick< ProjectTicketPageProps, | 'ticket' diff --git a/tests/Fixtures/ticket-detail-browser.mjs b/tests/Fixtures/ticket-detail-browser.mjs index e57e1c2..5358bf8 100644 --- a/tests/Fixtures/ticket-detail-browser.mjs +++ b/tests/Fixtures/ticket-detail-browser.mjs @@ -342,6 +342,47 @@ try { .waitFor(); await page.getByRole('button', { name: 'Create now', exact: true }).click(); await dialog.waitFor({ state: 'hidden' }); + await visit('?slowBranch&serverError'); + await page + .getByRole('button', { name: 'Create branch', exact: true }) + .click(); + await page + .getByText('Branch name is available.', { exact: true }) + .waitFor(); + await page + .getByLabel('Branch name', { exact: true }) + .fill('feature/ENG-42-preserve-this-name'); + await page + .getByText('Branch name is available.', { exact: true }) + .waitFor(); + const failedBranchCreation = page.waitForResponse( + (response) => + response.url().includes('/tickets/42/branches') && + response.request().method() === 'POST', + ); + await page.getByRole('button', { name: 'Create now', exact: true }).click(); + await page.getByText('Creating branch…', { exact: true }).waitFor(); + assert.equal( + await page + .locator('button[type="submit"]') + .last() + .getAttribute('aria-busy'), + 'true', + ); + assert.equal((await failedBranchCreation).status(), 503); + await page + .getByText('The request could not be completed', { exact: true }) + .waitFor(); + assert.equal( + await page.getByLabel('Branch name', { exact: true }).inputValue(), + 'feature/ENG-42-preserve-this-name', + ); + assert.equal(await page.getByRole('dialog').isVisible(), true); + await page.getByLabel('Branch name', { exact: true }).fill('eng-42-cache'); + await page + .getByText('Branch name is available.', { exact: true }) + .waitFor(); + await page.keyboard.press('Escape'); const linkSaved = page.waitForResponse( (response) => response.url().includes('/tickets/42/links') && diff --git a/tests/Fixtures/ticket-detail.mjs b/tests/Fixtures/ticket-detail.mjs index ceedd96..25853b0 100644 --- a/tests/Fixtures/ticket-detail.mjs +++ b/tests/Fixtures/ticket-detail.mjs @@ -42,6 +42,7 @@ const repository = { provider_label: 'Gitea', base_url: 'https://git.example.test', repository_full_name: 'Engineering/platform', + repository_url: 'https://git.example.test/Engineering/platform', has_personal_access: true, }; const parent = { @@ -354,6 +355,9 @@ if (serve) { const data = body ? JSON.parse(body) : {}; const previous = new URL(request.headers.referer); const params = previous.searchParams; + if (request.url.includes('/branches') && params.has('slowBranch')) { + await new Promise((resolve) => setTimeout(resolve, 300)); + } if (params.has('serverError')) { response.writeHead(503); response.end('Simulated failure'); @@ -550,6 +554,14 @@ if (serve) { assert.match(html, /aria-label="Criterion 1"/); assert.match(html, /Add criterion/); assert.match(html, /Create branch/); + assert.match( + html, + /]*href="https:\/\/git\.example\.test\/Engineering\/platform")(?=[^>]*target="_blank")(?=[^>]*rel="noopener noreferrer")[^>]*>/, + ); + assert.match( + html, + /]*href="https:\/\/git\.example\.test\/Engineering\/platform\/pulls\/12")(?=[^>]*target="_blank")(?=[^>]*rel="noopener noreferrer")[^>]*>/, + ); assert.match(html, /Save link/); assert.match(html, /Outgoing/); const incoming = await render('?incoming'); @@ -579,8 +591,7 @@ if (serve) { 'Unassigned', 'No author', 'Not set', - 'No matching branches', - 'No matching pull requests', + 'No linked repositories', 'No worklog entries exist', ]) { assert.ok(empty.includes(text), `Missing empty state: ${text}`); @@ -590,11 +601,13 @@ if (serve) { assert.match(hierarchy, /Platform delivery/); assert.match(hierarchy, /Nested child/); assert.match(hierarchy, /Change parent/); - assert.match(await render('?noAccess'), /Open Git connections/); - assert.match( - await render('?gitError'), - /Repository temporarily unavailable/, - ); + const noAccess = await render('?noAccess'); + assert.match(noAccess, /Personal Git access required/); + assert.match(noAccess, /Open Git connections/); + const gitError = await render('?gitError'); + assert.match(gitError, /Branches unavailable/); + assert.match(gitError, /Pull requests unavailable/); + assert.match(gitError, /Repository temporarily unavailable/); const create = await render('?source=board', true); assert.match(create, /Create ticket/); assert.match(create, /Delivery \/ In progress/);