From 44c619df7602a059f03555889fb343322ffc7a77 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 3a11ba4..c2bc946 100644 --- a/resources/js/locales/en.ts +++ b/resources/js/locales/en.ts @@ -476,8 +476,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 389ca6d..3ad4ff4 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' @@ -28,6 +29,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 bfb45f8..e2319a4 100644 --- a/tests/Fixtures/ticket-detail-browser.mjs +++ b/tests/Fixtures/ticket-detail-browser.mjs @@ -246,6 +246,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 be8deff..a8dd961 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 = { @@ -345,6 +346,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'); @@ -525,6 +529,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'); @@ -554,8 +566,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}`); @@ -565,11 +576,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/);