From f458030c1072a6d23a8849d718ca817182cf3508 Mon Sep 17 00:00:00 2001 From: dave vader <48764154+plyr4@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:14:30 -0600 Subject: [PATCH] Refactor/elm rebuild tests (#767) --- .github/workflows/ci.yml | 54 +-- .github/workflows/codeql-analysis.yml | 8 +- .github/workflows/publish.yml | 10 +- cypress/fixtures/enable_repo_response.json | 1 + cypress/fixtures/log_service_long.json | 7 + cypress/fixtures/log_service_short.json | 7 + cypress/fixtures/log_step_long.json | 7 + cypress/fixtures/log_step_short.json | 7 + cypress/fixtures/overview_page.json | 4 + cypress/fixtures/repositories.json | 16 + cypress/fixtures/repositories_100.json | 400 ++++++++++++++++++ cypress/fixtures/repositories_10a.json | 40 ++ cypress/fixtures/repositories_10b.json | 40 ++ cypress/fixtures/repositories_5.json | 40 +- cypress/fixtures/repository.json | 1 + cypress/fixtures/repository_inactive.json | 7 +- cypress/fixtures/repository_updated.json | 4 + cypress/fixtures/secrets_org_5.json | 15 + cypress/integration/a11y.spec.js | 15 + cypress/integration/contextual_help.spec.js | 78 +--- cypress/integration/deployment.spec.js | 23 +- cypress/integration/graph.spec.js | 2 +- cypress/integration/logs.spec.js | 112 +++-- cypress/integration/org.spec.js | 4 +- cypress/integration/pipeline.spec.js | 68 ++- cypress/integration/repo_settings.spec.js | 27 +- cypress/integration/schedule.spec.js | 135 ++++-- cypress/integration/secrets.spec.js | 70 ++- cypress/integration/steps.spec.js | 125 +++--- cypress/support/commands.js | 7 +- package-lock.json | 30 +- package.json | 6 +- src/elm/Api/Operations.elm | 29 ++ src/elm/Components/Build.elm | 109 +++-- src/elm/Components/Builds.elm | 3 +- src/elm/Components/Form.elm | 52 ++- src/elm/Components/Logs.elm | 30 +- src/elm/Components/Nav.elm | 16 +- src/elm/Components/Repo.elm | 2 +- src/elm/Components/ScheduleForm.elm | 11 +- src/elm/Components/SecretForm.elm | 2 + src/elm/Components/Secrets.elm | 19 +- src/elm/Components/Table.elm | 28 +- src/elm/Components/Tabs.elm | 2 +- src/elm/Effect.elm | 38 +- src/elm/Layouts.elm | 4 +- src/elm/Layouts/Default.elm | 65 +-- src/elm/Layouts/Default/Build.elm | 253 +++++++++-- src/elm/Layouts/Default/Org.elm | 38 +- src/elm/Layouts/Default/Repo.elm | 67 ++- src/elm/Main.elm | 75 ++-- src/elm/Main/Pages/Model.elm | 4 +- src/elm/Main/Pages/Msg.elm | 4 +- src/elm/Pages/Account/Login.elm | 40 +- src/elm/Pages/Account/Settings.elm | 40 +- src/elm/Pages/Account/SourceRepos.elm | 76 ++-- src/elm/Pages/Home.elm | 87 ++-- src/elm/Pages/NotFound_.elm | 31 +- src/elm/Pages/Org_.elm | 30 +- src/elm/Pages/Org_/Builds.elm | 89 +++- src/elm/Pages/Org_/Repo_.elm | 110 ++++- src/elm/Pages/Org_/Repo_/Build_.elm | 94 ++-- src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm | 214 ++++++---- src/elm/Pages/Org_/Repo_/Deployments.elm | 47 +- src/elm/Pages/Org_/Repo_/Deployments/Add.elm | 359 ++++++++-------- .../Pages/Org_/Repo_/{Audit.elm => Hooks.elm} | 11 +- src/elm/Pages/Org_/Repo_/Schedules.elm | 23 +- src/elm/Pages/Org_/Repo_/Schedules/Add.elm | 162 ++++--- src/elm/Pages/Org_/Repo_/Schedules/Edit_.elm | 251 ++++++----- src/elm/Pages/Org_/Repo_/Settings.elm | 56 ++- src/elm/Pages/PageTemplate.elm | 63 +-- src/elm/Pages/Secrets/Engine_/Org/Org_.elm | 4 +- .../Pages/Secrets/Engine_/Org/Org_/Add.elm | 140 +++--- .../Pages/Secrets/Engine_/Org/Org_/Edit_.elm | 187 ++++---- .../Pages/Secrets/Engine_/Repo/Org_/Repo_.elm | 4 +- .../Secrets/Engine_/Repo/Org_/Repo_/Add.elm | 142 ++++--- .../Secrets/Engine_/Repo/Org_/Repo_/Edit_.elm | 189 +++++---- .../Secrets/Engine_/Shared/Org_/Team_.elm | 83 ++-- .../Secrets/Engine_/Shared/Org_/Team_/Add.elm | 166 ++++---- .../Engine_/Shared/Org_/Team_/Edit_.elm | 189 +++++---- src/elm/Route/Path.elm | 21 +- src/elm/Shared.elm | 65 --- src/elm/Shared/Msg.elm | 5 - src/elm/Utils/Helpers.elm | 21 +- src/scss/_pipelines.scss | 6 +- src/scss/_table.scss | 2 +- 86 files changed, 3234 insertions(+), 1994 deletions(-) create mode 100644 cypress/fixtures/log_service_long.json create mode 100644 cypress/fixtures/log_service_short.json create mode 100644 cypress/fixtures/log_step_long.json create mode 100644 cypress/fixtures/log_step_short.json rename src/elm/Pages/Org_/Repo_/{Audit.elm => Hooks.elm} (97%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba93bb0f1..5c24f77a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,26 +21,26 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.npm key: ${{ runner.os }}-npm-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: node_modules key: ${{ runner.os }}-modules-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-modules-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.cache/Cypress key: cypress-${{ runner.os }}-bin-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | cypress-${{ runner.os }}-bin-v2- - - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version-file: '.nvmrc' @@ -59,32 +59,32 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.elm key: ${{ runner.os }}-elm-v3-${{ hashFiles('**/elm.json') }} restore-keys: | ${{ runner.os }}-elm-v3- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.npm key: ${{ runner.os }}-npm-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: node_modules key: ${{ runner.os }}-modules-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-modules-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.cache/Cypress key: cypress-${{ runner.os }}-bin-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | cypress-${{ runner.os }}-bin-v2- - - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version-file: '.nvmrc' @@ -103,26 +103,26 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.npm key: ${{ runner.os }}-npm-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: node_modules key: ${{ runner.os }}-modules-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-modules-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.cache/Cypress key: cypress-${{ runner.os }}-bin-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | cypress-${{ runner.os }}-bin-v2- - - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version-file: '.nvmrc' @@ -142,32 +142,32 @@ jobs: TERM: xterm steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.elm key: ${{ runner.os }}-elm-v3-${{ hashFiles('**/elm.json') }} restore-keys: | ${{ runner.os }}-elm-v3- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.npm key: ${{ runner.os }}-npm-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: node_modules key: ${{ runner.os }}-modules-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-modules-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.cache/Cypress key: cypress-${{ runner.os }}-bin-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | cypress-${{ runner.os }}-bin-v2- - - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version-file: '.nvmrc' @@ -186,26 +186,26 @@ jobs: needs: [lint, elm-format, elm-test, integration] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.elm key: ${{ runner.os }}-elm-v3-${{ hashFiles('**/elm.json') }} restore-keys: | ${{ runner.os }}-elm-v3- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.npm key: ${{ runner.os }}-npm-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: node_modules key: ${{ runner.os }}-modules-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-modules-v2- - - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version-file: '.nvmrc' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 20e702a62..7d53a9dbb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -38,7 +38,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3 + uses: github/codeql-action/init@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -49,7 +49,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3 + uses: github/codeql-action/autobuild@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -63,4 +63,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3 + uses: github/codeql-action/analyze@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5132d2628..c95aec152 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,26 +16,26 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.elm key: ${{ runner.os }}-elm-v3-${{ hashFiles('**/elm.json') }} restore-keys: | ${{ runner.os }}-elm-v3- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: ~/.npm key: ${{ runner.os }}-npm-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-npm-v2- - - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 + - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: node_modules key: ${{ runner.os }}-modules-v2-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-modules-v2- - - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4 + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version-file: '.nvmrc' diff --git a/cypress/fixtures/enable_repo_response.json b/cypress/fixtures/enable_repo_response.json index e5e815d70..0530496c4 100644 --- a/cypress/fixtures/enable_repo_response.json +++ b/cypress/fixtures/enable_repo_response.json @@ -7,6 +7,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, diff --git a/cypress/fixtures/log_service_long.json b/cypress/fixtures/log_service_long.json new file mode 100644 index 000000000..d5cb1ceef --- /dev/null +++ b/cypress/fixtures/log_service_long.json @@ -0,0 +1,7 @@ +{ + "id": 2, + "service_id": 2, + "build_id": 1, + "repo_id": 1, + "data": "QXR0ZW1wdGluZyBsb2NhbGhvc3QNCiAgICAbWzMybSDinJMbWzBtG1s5MG0gbG9hZHMbWzBtG1szMW0gKDg3NG1zKRtbMG0NCiAgICAbWzBtICgbWzRtG1sxbVJ1biBTdGFydGluZxtbMjJtG1syNG0pG1swbQ0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCi4=" +} diff --git a/cypress/fixtures/log_service_short.json b/cypress/fixtures/log_service_short.json new file mode 100644 index 000000000..59bbfc0ef --- /dev/null +++ b/cypress/fixtures/log_service_short.json @@ -0,0 +1,7 @@ +{ + "id": 2, + "service_id": 2, + "build_id": 1, + "repo_id": 1, + "data": "Lgo=" +} diff --git a/cypress/fixtures/log_step_long.json b/cypress/fixtures/log_step_long.json new file mode 100644 index 000000000..726bd878e --- /dev/null +++ b/cypress/fixtures/log_step_long.json @@ -0,0 +1,7 @@ +{ + "id": 2, + "step_id": 2, + "build_id": 1, + "repo_id": 1, + "data": "QXR0ZW1wdGluZyBsb2NhbGhvc3QNCiAgICAbWzMybSDinJMbWzBtG1s5MG0gbG9hZHMbWzBtG1szMW0gKDg3NG1zKRtbMG0NCiAgICAbWzBtICgbWzRtG1sxbVJ1biBTdGFydGluZxtbMjJtG1syNG0pG1swbQ0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCi4=" +} diff --git a/cypress/fixtures/log_step_short.json b/cypress/fixtures/log_step_short.json new file mode 100644 index 000000000..2d7008352 --- /dev/null +++ b/cypress/fixtures/log_step_short.json @@ -0,0 +1,7 @@ +{ + "id": 2, + "step_id": 2, + "build_id": 1, + "repo_id": 1, + "data": "Lgo=" +} diff --git a/cypress/fixtures/overview_page.json b/cypress/fixtures/overview_page.json index fd645f210..ba24af178 100644 --- a/cypress/fixtures/overview_page.json +++ b/cypress/fixtures/overview_page.json @@ -8,6 +8,7 @@ "link": "https://github.com/go-vela/server", "clone": "https://github.com/go-vela/server.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -29,6 +30,7 @@ "link": "https://github.com/go-vela/feature-requests", "clone": "https://github.com/go-vela/feature-requests.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -50,6 +52,7 @@ "link": "https://github.com/go-vela/ideas", "clone": "https://github.com/go-vela/ideas.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -71,6 +74,7 @@ "link": "https://github.com/CookieCat/applications", "clone": "https://github.com/CookieCat/applications.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, diff --git a/cypress/fixtures/repositories.json b/cypress/fixtures/repositories.json index e3db600f9..91eb0128a 100644 --- a/cypress/fixtures/repositories.json +++ b/cypress/fixtures/repositories.json @@ -8,6 +8,7 @@ "link": "https://github.com/go-vela/server", "clone": "https://github.com/go-vela/server.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -40,6 +41,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -52,6 +56,7 @@ "link": "https://github.com/go-vela/feature-requests", "clone": "https://github.com/go-vela/feature-requests.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -84,6 +89,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -96,6 +104,7 @@ "link": "https://github.com/go-vela/ideas", "clone": "https://github.com/go-vela/ideas.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -127,6 +136,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -139,6 +151,7 @@ "link": "https://github.com/CookieCat/applications", "clone": "https://github.com/CookieCat/applications.git", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -171,6 +184,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } } diff --git a/cypress/fixtures/repositories_100.json b/cypress/fixtures/repositories_100.json index 37c804b6a..97d0343c8 100644 --- a/cypress/fixtures/repositories_100.json +++ b/cypress/fixtures/repositories_100.json @@ -8,6 +8,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -40,6 +41,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -52,6 +56,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -84,6 +89,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -96,6 +104,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -128,6 +137,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -140,6 +152,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -172,6 +185,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -184,6 +200,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -216,6 +233,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -228,6 +248,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -260,6 +281,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -272,6 +296,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -304,6 +329,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -316,6 +344,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -348,6 +377,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -360,6 +392,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -392,6 +425,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -404,6 +440,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -436,6 +473,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -448,6 +488,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -480,6 +521,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -492,6 +536,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -524,6 +569,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -536,6 +584,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -568,6 +617,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -580,6 +632,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -612,6 +665,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -624,6 +680,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -656,6 +713,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -668,6 +728,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -700,6 +761,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -712,6 +776,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -744,6 +809,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -756,6 +824,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -788,6 +857,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -800,6 +872,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -832,6 +905,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -844,6 +920,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -876,6 +953,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -888,6 +968,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -920,6 +1001,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -932,6 +1016,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -964,6 +1049,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -976,6 +1064,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1008,6 +1097,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1020,6 +1112,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1052,6 +1145,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1064,6 +1160,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1096,6 +1193,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1108,6 +1208,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1140,6 +1241,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1152,6 +1256,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1184,6 +1289,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1196,6 +1304,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1228,6 +1337,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1240,6 +1352,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1272,6 +1385,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1284,6 +1400,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1316,6 +1433,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1328,6 +1448,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1360,6 +1481,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1372,6 +1496,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1404,6 +1529,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1416,6 +1544,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1448,6 +1577,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1460,6 +1592,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1492,6 +1625,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1504,6 +1640,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1536,6 +1673,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1548,6 +1688,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1580,6 +1721,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1592,6 +1736,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1624,6 +1769,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1636,6 +1784,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1668,6 +1817,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1680,6 +1832,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1712,6 +1865,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1724,6 +1880,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1756,6 +1913,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1768,6 +1928,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1800,6 +1961,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1812,6 +1976,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1844,6 +2009,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1856,6 +2024,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1888,6 +2057,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1900,6 +2072,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1932,6 +2105,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1944,6 +2120,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -1976,6 +2153,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -1988,6 +2168,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2020,6 +2201,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2032,6 +2216,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2064,6 +2249,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2076,6 +2264,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2108,6 +2297,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2120,6 +2312,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2152,6 +2345,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2164,6 +2360,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2196,6 +2393,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2208,6 +2408,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2240,6 +2441,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2252,6 +2456,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2284,6 +2489,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2296,6 +2504,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2328,6 +2537,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2340,6 +2552,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2372,6 +2585,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2384,6 +2600,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2416,6 +2633,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2428,6 +2648,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2460,6 +2681,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2472,6 +2696,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2504,6 +2729,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2516,6 +2744,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2548,6 +2777,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2560,6 +2792,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2592,6 +2825,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2604,6 +2840,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2636,6 +2873,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2648,6 +2888,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2680,6 +2921,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2692,6 +2936,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2724,6 +2969,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2736,6 +2984,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2768,6 +3017,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2780,6 +3032,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2812,6 +3065,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2824,6 +3080,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2856,6 +3113,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2868,6 +3128,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2900,6 +3161,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2912,6 +3176,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2944,6 +3209,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -2956,6 +3224,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -2988,6 +3257,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3000,6 +3272,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3032,6 +3305,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3044,6 +3320,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3076,6 +3353,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3088,6 +3368,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3120,6 +3401,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3132,6 +3416,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3164,6 +3449,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3176,6 +3464,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3208,6 +3497,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3220,6 +3512,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3252,6 +3545,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3264,6 +3560,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3296,6 +3593,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3308,6 +3608,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3340,6 +3641,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3352,6 +3656,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3384,6 +3689,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3396,6 +3704,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3428,6 +3737,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3440,6 +3752,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3472,6 +3785,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3484,6 +3800,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3516,6 +3833,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3528,6 +3848,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3560,6 +3881,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3572,6 +3896,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3604,6 +3929,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3616,6 +3944,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3648,6 +3977,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3660,6 +3992,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3692,6 +4025,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3704,6 +4040,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3736,6 +4073,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3748,6 +4088,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3780,6 +4121,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3792,6 +4136,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3824,6 +4169,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3836,6 +4184,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3868,6 +4217,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3880,6 +4232,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3912,6 +4265,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3924,6 +4280,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -3956,6 +4313,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -3968,6 +4328,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4000,6 +4361,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4012,6 +4376,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4044,6 +4409,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4056,6 +4424,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4088,6 +4457,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4100,6 +4472,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4132,6 +4505,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4144,6 +4520,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4176,6 +4553,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4188,6 +4568,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4220,6 +4601,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4232,6 +4616,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4264,6 +4649,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4276,6 +4664,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4308,6 +4697,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4320,6 +4712,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4352,6 +4745,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -4364,6 +4760,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -4396,6 +4793,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } } diff --git a/cypress/fixtures/repositories_10a.json b/cypress/fixtures/repositories_10a.json index 834a1e1e4..781943a13 100644 --- a/cypress/fixtures/repositories_10a.json +++ b/cypress/fixtures/repositories_10a.json @@ -8,6 +8,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -40,6 +41,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -52,6 +56,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -84,6 +89,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -96,6 +104,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -128,6 +137,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -140,6 +152,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -172,6 +185,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -184,6 +200,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -216,6 +233,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -228,6 +248,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -260,6 +281,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -272,6 +296,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -304,6 +329,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -316,6 +344,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -348,6 +377,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -360,6 +392,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -392,6 +425,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -404,6 +440,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -436,6 +473,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } } diff --git a/cypress/fixtures/repositories_10b.json b/cypress/fixtures/repositories_10b.json index e6d69a7a7..dd22007fe 100644 --- a/cypress/fixtures/repositories_10b.json +++ b/cypress/fixtures/repositories_10b.json @@ -8,6 +8,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -40,6 +41,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -52,6 +56,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -84,6 +89,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -96,6 +104,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -128,6 +137,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -140,6 +152,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -172,6 +185,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -184,6 +200,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -216,6 +233,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -228,6 +248,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -260,6 +281,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -272,6 +296,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -304,6 +329,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -316,6 +344,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -348,6 +377,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -360,6 +392,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -392,6 +425,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } }, @@ -404,6 +440,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -436,6 +473,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } } } diff --git a/cypress/fixtures/repositories_5.json b/cypress/fixtures/repositories_5.json index 41222f613..613e0d58e 100644 --- a/cypress/fixtures/repositories_5.json +++ b/cypress/fixtures/repositories_5.json @@ -8,6 +8,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -39,9 +40,14 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, - "pipeline_type": "yaml" + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "fork-always" }, { "id": 102, @@ -52,6 +58,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -83,9 +90,14 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, - "pipeline_type": "yaml" + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "fork-always" }, { "id": 103, @@ -96,6 +108,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -127,9 +140,14 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, - "pipeline_type": "yaml" + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "fork-always" }, { "id": 104, @@ -140,6 +158,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -171,9 +190,14 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, - "pipeline_type": "yaml" + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "fork-always" }, { "id": 105, @@ -184,6 +208,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -215,8 +240,13 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, - "pipeline_type": "yaml" + "pipeline_type": "yaml", + "previous_name": "", + "approve_build": "fork-always" } ] diff --git a/cypress/fixtures/repository.json b/cypress/fixtures/repository.json index e9e0b395a..50b7985f3 100644 --- a/cypress/fixtures/repository.json +++ b/cypress/fixtures/repository.json @@ -7,6 +7,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 81, diff --git a/cypress/fixtures/repository_inactive.json b/cypress/fixtures/repository_inactive.json index f187528c8..797c94c1e 100644 --- a/cypress/fixtures/repository_inactive.json +++ b/cypress/fixtures/repository_inactive.json @@ -7,6 +7,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 10, "timeout": 30, "counter": 0, @@ -38,7 +39,11 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, - "pipeline_type": "yaml" + "pipeline_type": "yaml", + "approve_build": "fork-no-write" } diff --git a/cypress/fixtures/repository_updated.json b/cypress/fixtures/repository_updated.json index c0adf6aec..0cc53730d 100644 --- a/cypress/fixtures/repository_updated.json +++ b/cypress/fixtures/repository_updated.json @@ -7,6 +7,7 @@ "link": "", "clone": "", "branch": "main", + "topics": [], "build_limit": 15, "timeout": 80, "counter": 2000, @@ -38,6 +39,9 @@ "comment": { "created": false, "edited": false + }, + "schedule": { + "run": false } }, "pipeline_type": "yaml", diff --git a/cypress/fixtures/secrets_org_5.json b/cypress/fixtures/secrets_org_5.json index 4b1d843ea..35d2a6f19 100644 --- a/cypress/fixtures/secrets_org_5.json +++ b/cypress/fixtures/secrets_org_5.json @@ -29,6 +29,9 @@ "created": false, "edited": false }, + "schedule": { + "run": false + }, "schedule": { "run": true } @@ -65,6 +68,9 @@ "created": false, "edited": false }, + "schedule": { + "run": false + }, "schedule": { "run": true } @@ -101,6 +107,9 @@ "created": false, "edited": false }, + "schedule": { + "run": false + }, "schedule": { "run": true } @@ -137,6 +146,9 @@ "created": false, "edited": false }, + "schedule": { + "run": false + }, "schedule": { "run": true } @@ -173,6 +185,9 @@ "created": false, "edited": false }, + "schedule": { + "run": false + }, "schedule": { "run": true } diff --git a/cypress/integration/a11y.spec.js b/cypress/integration/a11y.spec.js index eba31aeed..f54f3ba61 100644 --- a/cypress/integration/a11y.spec.js +++ b/cypress/integration/a11y.spec.js @@ -91,6 +91,21 @@ context('Accessibility (a11y)', () => { cy.checkA11yForPage('/github/octocat/hooks', A11Y_OPTS); }); + it('schedules page', () => { + cy.checkA11yForPage('/github/octocat/schedules', A11Y_OPTS); + }); + + it('deployments page', () => { + cy.checkA11yForPage('/github/octocat/deployments', A11Y_OPTS); + }); + + it('repo secrets page', () => { + cy.checkA11yForPage( + '/-/secrets/native/repo/octocat/deployments', + A11Y_OPTS, + ); + }); + it('build page', () => { cy.login('/github/octocat/1'); cy.injectAxe(); diff --git a/cypress/integration/contextual_help.spec.js b/cypress/integration/contextual_help.spec.js index 330e28c32..f778bd669 100644 --- a/cypress/integration/contextual_help.spec.js +++ b/cypress/integration/contextual_help.spec.js @@ -6,14 +6,6 @@ context('Contextual Help', () => { context('error loading resource', () => { beforeEach(() => { cy.server(); - cy.route({ - method: 'GET', - url: 'api/v1/user*', - status: 500, - response: { - error: 'error fetching user', - }, - }); cy.login(); cy.get('[data-test=help-trigger]').as('trigger'); }); @@ -29,51 +21,9 @@ context('Contextual Help', () => { it('should show the dropdown', () => { cy.get('[data-test=help-tooltip]').should('be.visible'); }); - it('dropdown should contain error msg', () => { - cy.get('[data-test=help-row] input').should( - 'have.value', - 'something went wrong!', - ); - }); - it('dropdown footer should contain getting started docs', () => { - cy.get('[data-test=help-footer]').contains('Getting Started Docs'); - }); }); }); - context('successfully loading resource with no cli support (yet)', () => { - beforeEach(() => { - cy.server(); - cy.route('GET', '*api/v1/user*', 'fixture:favorites_none.json'); - cy.login(); - cy.get('[data-test=help-trigger]').as('trigger'); - }); - - it('should show the help button', () => { - cy.get('@trigger').should('be.visible'); - }); - - context('clicking help button', () => { - beforeEach(() => { - cy.get('@trigger').click(); - }); - it('should show the dropdown', () => { - cy.get('[data-test=help-tooltip]').should('be.visible'); - }); - it('cmd header should contain feature request upvote link', () => { - cy.get('[data-test=help-cmd-header]').contains('(upvote feature)'); - }); - it('cmd should contain not supported message', () => { - cy.get('[data-test=help-row] input') - .invoke('val') - .should('eq', 'not yet supported via the CLI'); - }); - it('dropdown footer should contain installation and authentication docs', () => { - cy.get('[data-test=help-footer]').contains('CLI Installation Docs'); - cy.get('[data-test=help-footer]').contains('CLI Authentication Docs'); - }); - }); - }); context('successfully loading resource with cli support', () => { beforeEach(() => { cy.server(); @@ -99,7 +49,7 @@ context('Contextual Help', () => { it('cmd should contain cli command', () => { cy.get('[data-test=help-row] input') .invoke('val') - .should('eq', 'vela get builds --org github --repo octocat'); + .should('eq', 'vela get builds --help'); }); it('dropdown footer should contain installation and authentication docs', () => { cy.get('[data-test=help-footer]').contains('CLI Installation Docs'); @@ -111,33 +61,9 @@ context('Contextual Help', () => { }); it('should show copied alert', () => { cy.get('@copy').click(); - cy.get('[data-test=alerts]').contains('Copied'); + cy.get('[data-test=alerts]').contains('copied'); }); }); }); }); - context('visit page with no resources (not found)', () => { - beforeEach(() => { - cy.server(); - cy.login('/notfound'); - cy.get('[data-test=help-trigger]').as('trigger'); - }); - - it('should show the help button', () => { - cy.get('@trigger').should('be.visible'); - }); - context('clicking help button', () => { - beforeEach(() => { - cy.get('@trigger').click(); - }); - it('dropdown should contain error msg', () => { - cy.get('[data-test=help-row] input') - .invoke('val') - .should('eq', 'something went wrong!'); - }); - it('dropdown footer should contain getting started docs', () => { - cy.get('[data-test=help-footer]').contains('Getting Started Docs'); - }); - }); - }); }); diff --git a/cypress/integration/deployment.spec.js b/cypress/integration/deployment.spec.js index 98b8000a4..8373d271e 100644 --- a/cypress/integration/deployment.spec.js +++ b/cypress/integration/deployment.spec.js @@ -6,33 +6,28 @@ context('Deployment', () => { context('server returning deployment', () => { beforeEach(() => { cy.server(); - cy.route( - 'GET', - '*api/v1/secrets/native/repo/github/octocat/password*', - 'fixture:secret_repo.json', - ); cy.route( 'POST', '*api/v1/deployments/github/octocat', 'fixture:deployment.json', ); - cy.login('/github/octocat/add-deployment'); + cy.login('/github/octocat/deployments/add'); }); - it('Add Parameter button should be disabled', () => { + it('add parameter button should be disabled', () => { cy.get('[data-test=add-parameter-button]') .should('exist') .should('not.be.enabled') .contains('Add'); }); - it('Add Parameter should work as intended', () => { + it('add parameter should work as intended', () => { cy.get('[data-test=parameters-list]') .should('exist') .children() .first() - .should('contain.text', 'No Parameters defined'); - cy.get('[data-test=parameter-key-input]').should('exist').type('key1'); - cy.get('[data-test=parameter-value-input]').should('exist').type('val1'); + .should('contain.text', 'no parameters defined'); + cy.get('[data-test=parameter-key]').should('exist').type('key1'); + cy.get('[data-test=parameter-value]').should('exist').type('val1'); cy.get('[data-test=add-parameter-button]') .should('exist') .should('be.enabled') @@ -46,12 +41,12 @@ context('Deployment', () => { .children() .first() .children() - .first() + .last() .should('contain.text', 'key1=val1'); - cy.get('[data-test=parameter-key-input]') + cy.get('[data-test=parameter-key]') .should('exist') .should('have.value', ''); - cy.get('[data-test=parameter-value-input]') + cy.get('[data-test=parameter-value]') .should('exist') .should('have.value', ''); }); diff --git a/cypress/integration/graph.spec.js b/cypress/integration/graph.spec.js index 980c831e5..54fdef98a 100644 --- a/cypress/integration/graph.spec.js +++ b/cypress/integration/graph.spec.js @@ -168,7 +168,7 @@ context('Build Graph', () => { cy.location('pathname').should('eq', '/github/octocat/4/graph'); cy.get('.d3-build-graph-node-step-a').first().click({ force: true }); cy.location('pathname').should('eq', '/github/octocat/4'); - cy.hash().should('eq', '#step:5'); + cy.hash().should('eq', '#5'); }); it('step should reflect build information', () => { cy.get('.d3-build-graph-node-step-a svg') diff --git a/cypress/integration/logs.spec.js b/cypress/integration/logs.spec.js index e52282200..47863b3a1 100644 --- a/cypress/integration/logs.spec.js +++ b/cypress/integration/logs.spec.js @@ -10,36 +10,36 @@ context( cy.stubStepsWithANSILogs(); cy.login('/github/octocat/1'); cy.get('[data-test=step-header-2]').click({ force: true }); - cy.get('[data-test=logs-1]').as('logs'); + cy.get('[data-test=logs-2]').as('logs'); cy.get('[data-test=step-header-2]').click({ force: true }); - cy.visit('/github/octocat/1#step:2:31'); + cy.visit('/github/octocat/1#2:31'); cy.reload(); cy.wait('@getLogs-2'); }); it('line should not contain ansi characters', () => { - cy.get('[data-test=log-line-step-2-1]').within(() => { + cy.get('[data-test=log-line-2-1]').within(() => { cy.get('[class=ansi-red-fg]').should('not.exist'); }); }); it('line should contain ansi color css', () => { - cy.get('[data-test=log-line-step-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-green-fg]').should('exist'); cy.get('[class=ansi-red-fg]').should('exist'); }); - cy.get('[data-test=log-line-step-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-bright-black-fg]').should('exist'); }); }); it('ansi fg classes should change css color', () => { - cy.get('[data-test=log-line-step-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-green-fg]') .should('have.css', 'color') .should('eq', 'rgb(125, 209, 35)'); }); - cy.get('[data-test=log-line-step-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-red-fg]') .should('have.css', 'color') .should('eq', 'rgb(235, 102, 117)'); @@ -47,7 +47,7 @@ context( }); it('line should respect ansi font style', () => { - cy.get('[data-test=log-line-step-2-3]').within(() => { + cy.get('[data-test=log-line-2-3]').within(() => { cy.get('.ansi-bold').should('exist'); }); }); @@ -178,6 +178,14 @@ context( }); it('click follow logs should focus follow new logs', () => { + // stub short logs + cy.route({ + method: 'GET', + url: 'api/v1/repos/*/*/builds/*/steps/2/logs', + status: 200, + response: 'fixture:log_step_short.json', + }).as('getLogs-2'); + // verify no prior focus cy.focused().should( 'not.have.attr', @@ -185,11 +193,22 @@ context( 'bottom-log-tracker-2', ); + cy.wait('@getLogs-2'); + // follow logs cy.get('[data-test=follow-logs-2]').first().click({ force: true }); + // stub long logs to trigger follow + cy.route({ + method: 'GET', + url: 'api/v1/repos/*/*/builds/*/steps/2/logs', + status: 200, + response: 'fixture:log_step_long.json', + }).as('getLogs-2'); + // wait for refresh and check for bottom focus cy.wait('@getLogs-2'); + cy.focused().should('have.attr', 'data-test', 'bottom-log-tracker-2'); }); }); @@ -222,9 +241,9 @@ context( }); it('logs data should contain helpful message', () => { - cy.get('[data-test=log-line-step-1-1]').should( + cy.get('[data-test=log-line-1-1]').should( 'contain', - 'The build has not written logs to this step yet.', + 'The build has not written anything to this log yet.', ); }); @@ -244,36 +263,36 @@ context( cy.stubServicesWithANSILogs(); cy.login('/github/octocat/1/services'); cy.get('[data-test=service-header-2]').click({ force: true }); - cy.get('[data-test=logs-1]').as('logs'); + cy.get('[data-test=logs-2]').as('logs'); cy.get('[data-test=service-header-2]').click({ force: true }); - cy.visit('/github/octocat/1/services#service:2:31'); + cy.visit('/github/octocat/1/services#2:31'); cy.reload(); cy.wait('@getLogs-2'); }); it('line should not contain ansi characters', () => { - cy.get('[data-test=log-line-service-2-1]').within(() => { + cy.get('[data-test=log-line-2-1]').within(() => { cy.get('[class=ansi-red-fg]').should('not.exist'); }); }); it('line should contain ansi color css', () => { - cy.get('[data-test=log-line-service-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-green-fg]').should('exist'); cy.get('[class=ansi-red-fg]').should('exist'); }); - cy.get('[data-test=log-line-service-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-bright-black-fg]').should('exist'); }); }); it('ansi fg classes should change css color', () => { - cy.get('[data-test=log-line-service-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-green-fg]') .should('have.css', 'color') .should('eq', 'rgb(125, 209, 35)'); }); - cy.get('[data-test=log-line-service-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[class=ansi-red-fg]') .should('have.css', 'color') .should('eq', 'rgb(235, 102, 117)'); @@ -281,7 +300,7 @@ context( }); it('line should respect ansi font style', () => { - cy.get('[data-test=log-line-service-2-3]').within(() => { + cy.get('[data-test=log-line-2-3]').within(() => { cy.get('.ansi-bold').should('exist'); }); }); @@ -428,6 +447,14 @@ context( }); it('click follow logs should focus follow new logs', () => { + // stub short logs + cy.route({ + method: 'GET', + url: 'api/v1/repos/*/*/builds/*/services/2/logs', + status: 200, + response: 'fixture:log_service_short.json', + }).as('getLogs-2'); + // verify no prior focus cy.focused().should( 'not.have.attr', @@ -435,11 +462,22 @@ context( 'bottom-log-tracker-2', ); + cy.wait('@getLogs-2'); + // follow logs cy.get('[data-test=follow-logs-2]').first().click({ force: true }); + // stub long logs to trigger follow + cy.route({ + method: 'GET', + url: 'api/v1/repos/*/*/builds/*/services/2/logs', + status: 200, + response: 'fixture:log_service_long.json', + }).as('getLogs-2'); + // wait for refresh and check for bottom focus cy.wait('@getLogs-2'); + cy.focused().should('have.attr', 'data-test', 'bottom-log-tracker-2'); }); }); @@ -472,9 +510,9 @@ context( }); it('logs data should contain helpful message', () => { - cy.get('[data-test=log-line-service-1-1]').should( + cy.get('[data-test=log-line-1-1]').should( 'contain', - 'The build has not written logs to this step yet.', + 'The build has not written anything to this log yet.', ); }); @@ -495,14 +533,14 @@ context('visit Build with steps and large logs', () => { }); it('line should contain size exceeded message', () => { - cy.get('[data-test=log-line-step-1-1]').should( + cy.get('[data-test=log-line-1-1]').should( 'contain', 'exceeds the size limit', ); }); it('second line should contain download tip', () => { - cy.get('[data-test=log-line-step-1-2]').should('contain', 'download'); + cy.get('[data-test=log-line-1-2]').should('contain', 'download'); }); it('download button should show', () => { @@ -518,57 +556,57 @@ context( cy.stubStepsWithLinkedLogs(); cy.login('/github/octocat/1'); cy.get('[data-test=step-header-2]').click({ force: true }); - cy.get('[data-test=logs-1]').as('logs'); + cy.get('[data-test=logs-2]').as('logs'); cy.get('[data-test=step-header-2]').click({ force: true }); - cy.visit('/github/octocat/1#step:2:31'); + cy.visit('/github/octocat/1#2:31'); cy.reload(); cy.wait('@getLogs-2'); }); it('lines should not contain link', () => { - cy.get('[data-test=log-line-step-2-1]').within(() => { + cy.get('[data-test=log-line-2-1]').within(() => { cy.get('[data-test=log-line-link]').should('not.exist'); }); - cy.get('[data-test=log-line-step-2-2]').within(() => { + cy.get('[data-test=log-line-2-2]').within(() => { cy.get('[data-test=log-line-link]').should('not.exist'); }); - cy.get('[data-test=log-line-step-2-3]').within(() => { + cy.get('[data-test=log-line-2-3]').within(() => { cy.get('[data-test=log-line-link]').should('not.exist'); }); }); it('lines should contain https link', () => { - cy.get('[data-test=log-line-step-2-4]').within(() => { + cy.get('[data-test=log-line-2-4]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-5]').within(() => { + cy.get('[data-test=log-line-2-5]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-6]').within(() => { + cy.get('[data-test=log-line-2-6]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-7]').within(() => { + cy.get('[data-test=log-line-2-7]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-8]').within(() => { + cy.get('[data-test=log-line-2-8]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-9]').within(() => { + cy.get('[data-test=log-line-2-9]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-10]').within(() => { + cy.get('[data-test=log-line-2-10]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-11]').within(() => { + cy.get('[data-test=log-line-2-11]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); - cy.get('[data-test=log-line-step-2-12]').within(() => { + cy.get('[data-test=log-line-2-12]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); }); }); it('line should contain ansi color and link', () => { - cy.get('[data-test=log-line-step-2-13]').within(() => { + cy.get('[data-test=log-line-2-13]').within(() => { cy.get('[data-test=log-line-link]').should('exist'); cy.get('[class=ansi-magenta-bg]').should('exist'); cy.get('[class=ansi-magenta-bg]').should( diff --git a/cypress/integration/org.spec.js b/cypress/integration/org.spec.js index 8533bbf42..433d4c845 100644 --- a/cypress/integration/org.spec.js +++ b/cypress/integration/org.spec.js @@ -39,11 +39,11 @@ context('Org', () => { cy.get('@repos').should('have.length', 5); }); - it('should show 4 action buttons for each item', () => { + it('should show 5 action buttons for each item', () => { cy.get('@repos').each(($repo, i, $list) => { cy.wrap($repo) .find('.button') - .should('have.length', 4) + .should('have.length', 5) .should('be.visible'); }); }); diff --git a/cypress/integration/pipeline.spec.js b/cypress/integration/pipeline.spec.js index 6307fe818..bb01e0533 100644 --- a/cypress/integration/pipeline.spec.js +++ b/cypress/integration/pipeline.spec.js @@ -4,7 +4,7 @@ context('Pipeline', () => { context( - 'logged in and server returning pipeline configuration and templates errors', + 'logged in and server returning pipeline configuration error and templates errors', () => { beforeEach(() => { cy.server(); @@ -95,6 +95,7 @@ context('Pipeline', () => { cy.get('[data-test=pipeline-expand-toggle]').click({ force: true, }); + cy.wait('@expand'); }); it('should update path with expand query', () => { @@ -126,60 +127,48 @@ context('Pipeline', () => { context('click line number', () => { beforeEach(() => { - cy.get('[data-test=config-line-num-0-2]').click({ force: true }); + cy.get('[data-test=config-line-num-2]').click({ force: true }); }); it('should update path with line num', () => { - cy.hash().should('eq', '#config:0:2'); + cy.hash().should('eq', '#2'); }); it('other lines should not have focus style', () => { - cy.get('[data-test=config-line-0-3]').should( + cy.get('[data-test=config-line-3]').should( 'not.have.class', '-focus', ); }); it('should set focus style on single line', () => { - cy.get('[data-test=config-line-0-2]').should( - 'have.class', - '-focus', - ); + cy.get('[data-test=config-line-2]').should('have.class', '-focus'); }); }); context('click line number, then shift click other line number', () => { beforeEach(() => { - cy.get('[data-test=config-line-num-0-2]') + cy.get('[data-test=config-line-num-2]') .type('{shift}', { release: false }) - .get('[data-test=config-line-num-0-5]') + .get('[data-test=config-line-num-5]') .click({ force: true }); }); it('should update path with range', () => { - cy.hash().should('eq', '#config:0:2:5'); + cy.hash().should('eq', '#2:5'); }); it('lines outside the range should not have focus style', () => { - cy.get('[data-test=config-line-0-6]').should( + cy.get('[data-test=config-line-6]').should( 'not.have.class', '-focus', ); }); it('lines within the range should have focus style', () => { - cy.get('[data-test=config-line-0-2]').should( - 'have.class', - '-focus', - ); - cy.get('[data-test=config-line-0-3]').should( - 'have.class', - '-focus', - ); - cy.get('[data-test=config-line-0-4]').should( - 'have.class', - '-focus', - ); + cy.get('[data-test=config-line-2]').should('have.class', '-focus'); + cy.get('[data-test=config-line-3]').should('have.class', '-focus'); + cy.get('[data-test=config-line-4]').should('have.class', '-focus'); }); }); }); @@ -193,54 +182,54 @@ context('Pipeline', () => { }); it('pipeline configuration data should respect yaml spacing', () => { - cy.get('[data-test=config-line-0-1]').should('contain', 'version:'); - cy.get('[data-test=config-line-0-2]').should('contain', 'steps:'); + cy.get('[data-test=config-line-1]').should('contain', 'version:'); + cy.get('[data-test=config-line-2]').should('contain', 'steps:'); }); context('click line number', () => { beforeEach(() => { - cy.get('[data-test=config-line-num-0-2]').click({ force: true }); + cy.get('[data-test=config-line-num-2]').click({ force: true }); }); it('should update path with line num', () => { - cy.hash().should('eq', '#config:0:2'); + cy.hash().should('eq', '#2'); }); it('other lines should not have focus style', () => { - cy.get('[data-test=config-line-0-3]').should( + cy.get('[data-test=config-line-3]').should( 'not.have.class', '-focus', ); }); it('should set focus style on single line', () => { - cy.get('[data-test=config-line-0-2]').should('have.class', '-focus'); + cy.get('[data-test=config-line-2]').should('have.class', '-focus'); }); }); context('click line number, then shift click other line number', () => { beforeEach(() => { - cy.get('[data-test=config-line-num-0-2]') + cy.get('[data-test=config-line-num-2]') .type('{shift}', { release: false }) - .get('[data-test=config-line-num-0-5]') + .get('[data-test=config-line-num-5]') .click({ force: true }); }); it('should update path with range', () => { - cy.hash().should('eq', '#config:0:2:5'); + cy.hash().should('eq', '#2:5'); }); it('lines outside the range should not have focus style', () => { - cy.get('[data-test=config-line-0-6]').should( + cy.get('[data-test=config-line-6]').should( 'not.have.class', '-focus', ); }); it('lines within the range should have focus style', () => { - cy.get('[data-test=config-line-0-2]').should('have.class', '-focus'); - cy.get('[data-test=config-line-0-3]').should('have.class', '-focus'); - cy.get('[data-test=config-line-0-4]').should('have.class', '-focus'); + cy.get('[data-test=config-line-2]').should('have.class', '-focus'); + cy.get('[data-test=config-line-3]').should('have.class', '-focus'); + cy.get('[data-test=config-line-4]').should('have.class', '-focus'); }); }); }, @@ -285,10 +274,7 @@ context('Pipeline', () => { cy.get('[data-test=alerts]').should('exist').contains('Error'); }); - it('should show pipeline configuration error', () => { - cy.get('[data-test=pipeline-configuration-error]').should( - 'be.visible', - ); + it('should not show pipeline configuration data', () => { cy.get('[data-test=pipeline-configuration-data]').should( 'not.be.visible', ); diff --git a/cypress/integration/repo_settings.spec.js b/cypress/integration/repo_settings.spec.js index 2324e60ab..16ca89e48 100644 --- a/cypress/integration/repo_settings.spec.js +++ b/cypress/integration/repo_settings.spec.js @@ -52,13 +52,11 @@ context('Repo Settings', () => { }); it('allow_push_branch checkbox should show', () => { - cy.get('[data-test=repo-checkbox-allow_push_branch]').should( - 'be.visible', - ); + cy.get('[data-test=checkbox-allow_push_branch]').should('be.visible'); }); it('clicking allow_push_branch checkbox should toggle the value', () => { - cy.get('[data-test=repo-checkbox-allow_push_branch] input').as( + cy.get('[data-test=checkbox-allow_push_branch] input').as( 'allowPushCheckbox', ); cy.get('@allowPushCheckbox').should('have.checked'); @@ -67,23 +65,21 @@ context('Repo Settings', () => { }); it('clicking access radio should toggle both values', () => { - cy.get('[data-test=repo-radio-private] input').as('accessRadio'); + cy.get('[data-test=radio-private] input').as('accessRadio'); cy.get('@accessRadio').should('not.have.checked'); cy.get('@accessRadio').click({ force: true }); cy.get('@accessRadio').should('have.checked'); }); it('clicking outside contributor approval policy should toggle', () => { - cy.get('[data-test=repo-radio-fork-no-write] input').as( - 'forkPolicyRadio', - ); + cy.get('[data-test=radio-fork-no-write] input').as('forkPolicyRadio'); cy.get('@forkPolicyRadio').should('not.have.checked'); cy.get('@forkPolicyRadio').click({ force: true }); cy.get('@forkPolicyRadio').should('have.checked'); }); it('clicking pipeline type radio should toggle all values', () => { - cy.get('[data-test=repo-radio-private] input').as('pipelineTypeRadio'); + cy.get('[data-test=radio-private] input').as('pipelineTypeRadio'); cy.get('@pipelineTypeRadio').should('not.have.checked'); cy.get('@pipelineTypeRadio').click({ force: true }); cy.get('@pipelineTypeRadio').should('have.checked'); @@ -236,7 +232,7 @@ context('Repo Settings', () => { it('should copy markdown to clipboard and alert', () => { let clipboardContent; cy.get('[data-test=copy-md]').click(); - cy.get('[data-test=alerts]').should('exist').contains('Copied'); + cy.get('[data-test=alerts]').should('exist').contains('copied'); }); it('Chown button should exist', () => { @@ -307,17 +303,6 @@ context('Repo Settings', () => { cy.get('[data-test=repo-enable]').should('exist').contains('Enable'); }); - it('successful repair enables disable button', () => { - cy.route({ - method: 'PATCH', - url: '*api/v1/repos/github/**', - response: '"Repo github/octocat repaired."', - }); - cy.get('[data-test=repo-repair]').click(); - cy.get('[data-test=alerts]').should('exist').contains('Success'); - cy.get('[data-test=repo-disable').should('exist').contains('Disable'); - }); - it('failed repair keeps enable button enabled', () => { cy.route({ method: 'PATCH', diff --git a/cypress/integration/schedule.spec.js b/cypress/integration/schedule.spec.js index fb86543b3..1cfc02a9f 100644 --- a/cypress/integration/schedule.spec.js +++ b/cypress/integration/schedule.spec.js @@ -11,7 +11,7 @@ context('Add Schedule', () => { '*api/v1/schedules/github/octocat/Daily', 'fixture:schedule.json', ); - cy.login('/github/octocat/add-schedule'); + cy.login('/github/octocat/schedules/add'); }); context( 'allowlist contains github/octocat', @@ -22,7 +22,7 @@ context('Add Schedule', () => { }, () => { it('default name placeholder should show', () => { - cy.get('[data-test=schedule-name]') + cy.get('[data-test=name]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { @@ -30,7 +30,7 @@ context('Add Schedule', () => { }); }); it('default entry value should show', () => { - cy.get('[data-test=schedule-entry]') + cy.get('[data-test=entry]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { @@ -38,15 +38,15 @@ context('Add Schedule', () => { }); }); it('default branch placeholder should show', () => { - cy.get('[data-test=schedule-branch-name]') + cy.get('[data-test=branch-name]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { expect(placeholder).to.include('Branch Name'); }); }); - it('add button should show', () => { - cy.get('[data-test=schedule-add-button]').should('exist'); + it('submit button should show', () => { + cy.get('[data-test=submit]').should('exist'); }); }, ); @@ -59,8 +59,8 @@ context('Add Schedule', () => { }, }, () => { - it('default name placeholder should show', () => { - cy.get('[data-test=schedule-name]') + it('default entry value should show', () => { + cy.get('[data-test=name]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { @@ -68,7 +68,7 @@ context('Add Schedule', () => { }); }); it('default entry value should show', () => { - cy.get('[data-test=schedule-entry]') + cy.get('[data-test=entry]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { @@ -76,15 +76,15 @@ context('Add Schedule', () => { }); }); it('default branch placeholder should show', () => { - cy.get('[data-test=schedule-branch-name]') + cy.get('[data-test=branch-name]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { expect(placeholder).to.include('Branch Name'); }); }); - it('add button should show', () => { - cy.get('[data-test=schedule-add-button]').should('exist'); + it('submit button should show', () => { + cy.get('[data-test=submit]').should('exist'); }); }, ); @@ -96,17 +96,43 @@ context('Add Schedule', () => { }, }, () => { - it('default name value should not show', () => { - cy.get('[data-test=schedule-name]').should('not.exist'); + it('default entry value should show and be disabled', () => { + cy.get('[data-test=name]') + .should('exist') + .and('have.attr', 'placeholder') + .then(placeholder => { + expect(placeholder).to.include('Schedule Name'); + }); + cy.get('[data-test=name]') + .should('exist') + .and('have.attr', 'disabled'); }); - it('default entry value should not show', () => { - cy.get('[data-test=schedule-entry]').should('not.exist'); + it('default entry value should show and be disabled', () => { + cy.get('[data-test=entry]') + .should('exist') + .and('have.attr', 'placeholder') + .then(placeholder => { + expect(placeholder).to.include('0 0 * * *'); + }); + cy.get('[data-test=entry]') + .should('exist') + .and('have.attr', 'disabled'); }); - it('default branch value should not show', () => { - cy.get('[data-test=schedule-branch-name]').should('not.exist'); + it('default branch placeholder should show and be disabled', () => { + cy.get('[data-test=branch-name]') + .should('exist') + .and('have.attr', 'placeholder') + .then(placeholder => { + expect(placeholder).to.include('Branch Name'); + }); + cy.get('[data-test=branch-name]') + .should('exist') + .and('have.attr', 'disabled'); }); - it('add button should not show', () => { - cy.get('[data-test=schedule-add-button]').should('not.exist'); + it('submit button should show and be disabled', () => { + cy.get('[data-test=submit]') + .should('exist') + .and('have.attr', 'disabled'); }); it('should show not allowed warning', () => { cy.get('[data-test=repo-schedule-not-allowed]').should('exist'); @@ -136,12 +162,12 @@ context('View/Edit Schedule', () => { }, () => { it('default name value should show', () => { - cy.get('[data-test=schedule-name]') + cy.get('[data-test=name]') .should('exist') .should('have.value', 'Daily'); }); it('default entry value should show', () => { - cy.get('[data-test=schedule-entry]') + cy.get('[data-test=entry]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { @@ -149,15 +175,15 @@ context('View/Edit Schedule', () => { }); }); it('default branch value should show', () => { - cy.get('[data-test=schedule-branch-name]') + cy.get('[data-test=branch-name]') .should('exist') .should('have.value', 'main'); }); - it('update button should show', () => { - cy.get('[data-test=schedule-update-button]').should('exist'); + it('submit button should show', () => { + cy.get('[data-test=submit]').should('exist'); }); it('delete button should show', () => { - cy.get('[data-test=schedule-delete-button]').should('exist'); + cy.get('[data-test=delete]').should('exist'); }); }, ); @@ -171,12 +197,12 @@ context('View/Edit Schedule', () => { }, () => { it('default name value should show', () => { - cy.get('[data-test=schedule-name]') + cy.get('[data-test=name]') .should('exist') .should('have.value', 'Daily'); }); it('default entry value should show', () => { - cy.get('[data-test=schedule-entry]') + cy.get('[data-test=entry]') .should('exist') .and('have.attr', 'placeholder') .then(placeholder => { @@ -184,15 +210,15 @@ context('View/Edit Schedule', () => { }); }); it('default branch value should show', () => { - cy.get('[data-test=schedule-branch-name]') + cy.get('[data-test=branch-name]') .should('exist') .should('have.value', 'main'); }); - it('update button should show', () => { - cy.get('[data-test=schedule-update-button]').should('exist'); + it('submit button should show', () => { + cy.get('[data-test=submit]').should('exist'); }); it('delete button should show', () => { - cy.get('[data-test=schedule-delete-button]').should('exist'); + cy.get('[data-test=delete]').should('exist'); }); }, ); @@ -204,20 +230,43 @@ context('View/Edit Schedule', () => { }, }, () => { - it('default name value should not show', () => { - cy.get('[data-test=schedule-name]').should('not.exist'); - }); - it('default entry value should not show', () => { - cy.get('[data-test=schedule-entry]').should('not.exist'); + it('default entry value should show and be disabled', () => { + cy.get('[data-test=name]') + .should('exist') + .and('have.attr', 'placeholder') + .then(placeholder => { + expect(placeholder).to.include('Schedule Name'); + }); + cy.get('[data-test=name]') + .should('exist') + .and('have.attr', 'disabled'); }); - it('default branch value should not show', () => { - cy.get('[data-test=schedule-branch-name]').should('not.exist'); + it('default entry value should show and be disabled', () => { + cy.get('[data-test=entry]') + .should('exist') + .and('have.attr', 'placeholder') + .then(placeholder => { + expect(placeholder).to.include('0 0 * * *'); + }); + cy.get('[data-test=entry]') + .should('exist') + .and('have.attr', 'disabled'); }); - it('update button should not show', () => { - cy.get('[data-test=schedule-update-button]').should('not.exist'); + it('default branch placeholder should show and be disabled', () => { + cy.get('[data-test=branch-name]') + .should('exist') + .and('have.attr', 'placeholder') + .then(placeholder => { + expect(placeholder).to.include('Branch Name'); + }); + cy.get('[data-test=branch-name]') + .should('exist') + .and('have.attr', 'disabled'); }); - it('delete button should not show', () => { - cy.get('[data-test=schedule-delete-button]').should('not.exist'); + it('submit button should show and be disabled', () => { + cy.get('[data-test=submit]') + .should('exist') + .and('have.attr', 'disabled'); }); it('should show not allowed warning', () => { cy.get('[data-test=repo-schedule-not-allowed]').should('exist'); diff --git a/cypress/integration/secrets.spec.js b/cypress/integration/secrets.spec.js index e51f649f5..0a4a1d18a 100644 --- a/cypress/integration/secrets.spec.js +++ b/cypress/integration/secrets.spec.js @@ -24,10 +24,8 @@ context('Secrets', () => { cy.login('/-/secrets/native/repo/github/octocat/password'); }); - it('Remove button should show', () => { - cy.get('[data-test=secret-delete-button]') - .should('exist') - .contains('Remove'); + it('delete button should show', () => { + cy.get('[data-test=delete]').should('exist').contains('Delete'); }); context( @@ -38,8 +36,8 @@ context('Secrets', () => { }, }, () => { - it('add button should show', () => { - cy.get('[data-test=repo-checkbox-schedule]').should('exist'); + it('submit button should show', () => { + cy.get('[data-test=submit]').should('exist'); }); }, ); @@ -53,49 +51,43 @@ context('Secrets', () => { }, () => { it('add button should not show', () => { - cy.get('[data-test=repo-checkbox-schedule]').should('not.exist'); + cy.get('[data-test=checkbox-schedule]').should('not.exist'); }); }, ); - context('click Remove', () => { + context('click Delete', () => { beforeEach(() => { - cy.get('[data-test=secret-delete-button]').click(); + cy.get('[data-test=delete]').click(); }); - it('Remove button should show when going to another secrets page', () => { + it('delete button should show when going to another secrets page', () => { cy.visit('/-/secrets/native/org/github/password'); - cy.get('[data-test=secret-delete-button]') - .should('exist') - .contains('Remove'); + cy.get('[data-test=delete]').should('exist').contains('Delete'); }); it('Cancel button should show', () => { - cy.get('[data-test=secret-cancel-button]') - .should('exist') - .contains('Cancel'); + cy.get('[data-test=delete-cancel]').should('exist').contains('Cancel'); }); it('Confirm button should show', () => { - cy.get('[data-test=secret-delete-button]') + cy.get('[data-test=delete-confirm]') .should('exist') .contains('Confirm'); }); context('click Cancel', () => { beforeEach(() => { - cy.get('[data-test=secret-cancel-button]').click(); + cy.get('[data-test=delete-cancel]').click(); }); - it('Cancel should revert Confirm to Remove', () => { - cy.get('[data-test=secret-delete-button]') - .should('exist') - .contains('Remove'); + it('should revert Confirm to Delete', () => { + cy.get('[data-test=delete]').should('exist').contains('Delete'); }); it('Cancel should not show', () => { - cy.get('[data-test=secret-cancel-button]').should('not.exist'); + cy.get('[data-test=delete-cancel]').should('not.exist'); }); }); context('click Confirm', () => { beforeEach(() => { - cy.get('[data-test=secret-delete-button]').click(); + cy.get('[data-test=delete-confirm]').click(); }); it('Confirm should redirect to repo secrets page', () => { @@ -108,7 +100,7 @@ context('Secrets', () => { cy.get('[data-test=alerts]') .should('exist') .contains('password') - .contains('removed') + .contains('deleted') .contains('repo'); }); }); @@ -130,8 +122,8 @@ context('Secrets', () => { response: { error: 'server error could not remove' }, }); cy.login('/-/secrets/native/repo/github/octocat/password'); - cy.get('[data-test=secret-delete-button]').click(); - cy.get('[data-test=secret-delete-button]').click(); + cy.get('[data-test=delete]').click(); + cy.get('[data-test=delete-confirm]').click(); }); it('error should show', () => { @@ -194,10 +186,10 @@ context('Secrets', () => { it('should show copy', () => { cy.get('@firstSecret').within(() => { - cy.get('[data-test=secrets-row-copy]').should('exist'); + cy.get('[data-test=copy-secret]').should('exist'); }); cy.get('@lastSecret').within(() => { - cy.get('[data-test=secrets-row-copy]').should('exist'); + cy.get('[data-test=copy-secret]').should('exist'); }); }); @@ -205,32 +197,32 @@ context('Secrets', () => { cy.get('@firstSecret').within(() => { cy.get('[data-test=copy-secret]').click(); }); - cy.get('[data-test=alerts]').should('exist').contains('Copied'); + cy.get('[data-test=alerts]').should('exist').contains('copied'); }); it('should show key', () => { cy.get('@firstSecret').within(() => { - cy.get('[data-test=secrets-row-key]').contains( - 'github/docker_username', - ); + cy.get('[data-test=cell-key]').contains('github/docker_username'); }); cy.get('@lastSecret').within(() => { - cy.get('[data-test=secrets-row-key]').contains('github/deployment'); + cy.get('[data-test=cell-key]').contains('github/deployment'); }); }); it('should show name', () => { cy.get('@firstSecret').within(() => { - cy.get('[data-test=secrets-row-name]').contains('docker_username'); + cy.get('[data-test=cell-name]').contains('docker_username'); }); cy.get('@lastSecret').within(() => { - cy.get('[data-test=secrets-row-name]').contains('deployment'); + cy.get('[data-test=cell-name]').contains('deployment'); }); }); it('clicking name should route to edit secret page', () => { cy.get('@firstSecret').within(() => { - cy.get('[data-test=secrets-row-name] > a').click({ force: true }); + cy.get('[data-test=cell-name] > .single-item > a').click({ + force: true, + }); cy.location('pathname').should( 'eq', '/-/secrets/native/org/github/docker_username', @@ -240,7 +232,9 @@ context('Secrets', () => { it('clicking name with special character should use encoded url', () => { cy.get('@lastSecret').within(() => { - cy.get('[data-test=secrets-row-name] > a').click({ force: true }); + cy.get('[data-test=cell-name] > .single-item > a').click({ + force: true, + }); cy.location('pathname').should( 'eq', '/-/secrets/native/org/github/github%2Fdeployment', diff --git a/cypress/integration/steps.spec.js b/cypress/integration/steps.spec.js index 28941fff9..9ee406738 100644 --- a/cypress/integration/steps.spec.js +++ b/cypress/integration/steps.spec.js @@ -13,7 +13,6 @@ context('Steps', () => { cy.get('[data-test=step]').as('step'); cy.clickSteps(); cy.get('[data-test=logs-1]').as('logs'); - cy.clickSteps(); }); it('steps should show', () => { @@ -45,15 +44,17 @@ context('Steps', () => { }); it('logs should be hidden', () => { + cy.get('[data-test=step-header-1]').click({ force: true }); cy.get('@logs').children().should('be.not.visible'); }); - context('click steps', () => { + + context('click steps (to hide them)', () => { beforeEach(() => { cy.clickSteps(); }); - it('should show logs', () => { - cy.get('@logs').children().should('be.visible'); + it('logs should be hidden', () => { + cy.get('@logs').children().should('be.not.visible'); }); context('click steps again', () => { @@ -61,25 +62,24 @@ context('Steps', () => { cy.clickSteps(); }); - it('should hide logs', () => { - cy.get('@logs').children().should('be.not.visible'); + it('should show logs', () => { + cy.get('@logs').children().should('be.visible'); }); }); }); - context('click first step', () => { + context('click first step twice', () => { beforeEach(() => { cy.get('[data-test=step-header-1]').click({ force: true }); + cy.get('[data-test=step-header-1]').click({ force: true }); }); it('browser path should contain first step fragment', () => { - cy.hash().should('eq', '#step:1'); + cy.hash().should('eq', '#1'); }); - context('click last step', () => { - beforeEach(() => { - cy.get('[data-test=step-header-5]').click({ force: true }); - }); - it('browser path should contain last step fragment', () => { - cy.hash().should('eq', '#step:5'); - }); + + it('browser path should contain last step fragment', () => { + cy.get('[data-test=step-header-5]').click({ force: true }); + cy.get('[data-test=step-header-5]').click({ force: true }); + cy.hash().should('eq', '#5'); }); }); context('click log line in last step', () => { @@ -88,38 +88,37 @@ context('Steps', () => { cy.get('[data-test=step-skipped]').as('lineNumber'); cy.get('@lineNumber').click({ force: true }); }); - context('click first step', () => { + context('click first step twice', () => { beforeEach(() => { cy.get('[data-test=step-header-1]').click({ force: true }); + cy.get('[data-test=step-header-1]').click({ force: true }); }); it('browser path should contain first step fragment', () => { - cy.hash().should('eq', '#step:1'); + cy.hash().should('eq', '#1'); }); }); }); context('click log line number', () => { beforeEach(() => { - cy.clickSteps(); cy.wait('@getLogs-1'); - cy.get('[data-test=log-line-step-1-3]').as('line'); - cy.get('[data-test=log-line-num-step-1-3]').as('lineNumber'); + cy.get('[data-test=log-line-1-3]').as('line'); + cy.get('[data-test=log-line-num-1-3]').as('lineNumber'); cy.get('@lineNumber').click({ force: true }); }); it('line should be highlighted', () => { - cy.clickSteps(); cy.get('@lineNumber').click({ force: true }); cy.get('@line').should('have.class', '-focus'); }); it('browser path should contain step and line fragment', () => { - cy.hash().should('eq', '#step:1:3'); + cy.hash().should('eq', '#1:3'); }); context('click other log line number', () => { beforeEach(() => { - cy.get('[data-test=log-line-step-3-2]').as('otherLine'); - cy.get('[data-test=log-line-num-step-3-2]').as('otherLineNumber'); + cy.get('[data-test=log-line-3-2]').as('otherLine'); + cy.get('[data-test=log-line-num-3-2]').as('otherLineNumber'); cy.get('@otherLineNumber').click({ force: true }); }); it('original line should not be highlighted', () => { @@ -133,44 +132,44 @@ context('Steps', () => { it('browser path should contain other step and line fragment', () => { cy.clickSteps(); - cy.hash().should('eq', '#step:3:2'); + cy.hash().should('eq', '#3:2'); }); it('browser path should contain other step and line fragment', () => { cy.get('@otherLineNumber').click({ force: true }); - cy.hash().should('eq', '#step:3:2'); + cy.hash().should('eq', '#3:2'); }); }); }); context('visit Build, then visit log line with fragment', () => { beforeEach(() => { - cy.visit('/github/octocat/1#step:2:2'); + cy.visit('/github/octocat/1#2:2'); cy.reload(); }); it('line should be highlighted', () => { cy.wait('@getLogs-2'); - cy.get('[data-test=log-line-step-2-2]').as('line2:2'); - cy.get('[data-test=log-line-num-step-2-2]').as('lineNumber2:2'); + cy.get('[data-test=log-line-2-2]').as('line2:2'); + cy.get('[data-test=log-line-num-2-2]').as('lineNumber2:2'); cy.get('@line2:2').should('have.class', '-focus'); }); }); context('visit Build, with only step fragment', () => { beforeEach(() => { - cy.visit('/github/octocat/1#step:2'); + cy.visit('/github/octocat/1#2'); cy.reload(); }); it('range start line should not be highlighted', () => { cy.wait('@getLogs-2'); - cy.get('[data-test=log-line-step-2-2]').as('line2:2'); + cy.get('[data-test=log-line-2-2]').as('line2:2'); cy.get('@line2:2').should('not.have.class', '-focus'); }); context('click line 2, shift click line 5', () => { beforeEach(() => { cy.wait('@getLogs-2'); - cy.get('[data-test=log-line-step-2-2]').as('line2:2'); - cy.get('[data-test=log-line-step-2-5]').as('line2:5'); - cy.get('[data-test=log-line-num-step-2-2]').as('lineNumber2:2'); - cy.get('[data-test=log-line-num-step-2-5]').as('lineNumber2:5'); + cy.get('[data-test=log-line-2-2]').as('line2:2'); + cy.get('[data-test=log-line-2-5]').as('line2:5'); + cy.get('[data-test=log-line-num-2-2]').as('lineNumber2:2'); + cy.get('[data-test=log-line-num-2-5]').as('lineNumber2:5'); cy.get('@lineNumber2:2') .type('{shift}', { release: false }) .get('@lineNumber2:2') @@ -179,55 +178,39 @@ context('Steps', () => { }); it('range start line should be highlighted', () => { cy.wait('@getLogs-2'); - cy.get('[data-test=log-line-step-2-2]').should( - 'have.class', - '-focus', - ); + cy.get('[data-test=log-line-2-2]').should('have.class', '-focus'); }); it('lines between range start and end should be highlighted', () => { cy.wait('@getLogs-2'); - cy.get('[data-test=log-line-step-2-3]').should( - 'have.class', - '-focus', - ); - cy.get('[data-test=log-line-step-2-4]').should( - 'have.class', - '-focus', - ); + cy.get('[data-test=log-line-2-3]').should('have.class', '-focus'); + cy.get('[data-test=log-line-2-4]').should('have.class', '-focus'); }); }); - context('click first step', () => { + context('click first step twice', () => { beforeEach(() => { cy.get('[data-test=step-header-1]').click({ force: true }); + cy.get('[data-test=step-header-1]').click({ force: true }); }); it('browser path should contain first step fragment', () => { - cy.hash().should('eq', '#step:1'); + cy.hash().should('eq', '#1'); }); }); }); context('visit Build, then visit log line range with fragment', () => { beforeEach(() => { - cy.visit('/github/octocat/1#step:2:2:5'); + cy.visit('/github/octocat/1#2:2:5'); cy.reload(); cy.wait('@getLogs-2'); }); it('range start line should be highlighted', () => { - cy.get('[data-test=log-line-step-2-2]').should('have.class', '-focus'); + cy.get('[data-test=log-line-2-2]').should('have.class', '-focus'); }); it('range end line should be highlighted', () => { - cy.get('[data-test=log-line-step-2-5]').should('have.class', '-focus'); + cy.get('[data-test=log-line-2-5]').should('have.class', '-focus'); }); it('lines between range start and end should be highlighted', () => { - cy.get('[data-test=log-line-step-2-3]').should('have.class', '-focus'); - cy.get('[data-test=log-line-step-2-4]').should('have.class', '-focus'); - }); - context('click first step', () => { - beforeEach(() => { - cy.get('[data-test=step-header-1]').click({ force: true }); - }); - it('browser path should contain first step fragment', () => { - cy.hash().should('eq', '#step:1'); - }); + cy.get('[data-test=log-line-2-3]').should('have.class', '-focus'); + cy.get('[data-test=log-line-2-4]').should('have.class', '-focus'); }); }); context( @@ -236,12 +219,12 @@ context('Steps', () => { beforeEach(() => { cy.visit('/github/octocat/1'); cy.clickSteps(); - cy.get('[data-test=log-line-step-2-2]').as('line2:2'); - cy.get('[data-test=log-line-num-step-2-2]').as('lineNumber2:2'); - cy.get('[data-test=log-line-step-3-3]').as('line3:3'); - cy.get('[data-test=log-line-num-step-3-3]').as('lineNumber3:3'); + cy.get('[data-test=log-line-2-2]').as('line2:2'); + cy.get('[data-test=log-line-num-2-2]').as('lineNumber2:2'); + cy.get('[data-test=log-line-3-3]').as('line3:3'); + cy.get('[data-test=log-line-num-3-3]').as('lineNumber3:3'); cy.get('@lineNumber3:3').click({ force: true }); - cy.visit('/github/octocat/1#step:2:2'); + cy.visit('/github/octocat/1#2:2'); cy.reload(); }); it('original line should not be highlighted', () => { @@ -252,12 +235,8 @@ context('Steps', () => { }); context('click first step', () => { beforeEach(() => { - cy.get('[data-test=step-header-1]').click({ force: true }); + cy.get('[data-test=step-header-5]').click({ force: true }); }); - it('browser path should contain first step fragment', () => { - cy.hash().should('eq', '#step:1'); - }); - it('last step should contain killed/skip', () => { cy.get('[data-test=step]').last().as('killStep'); cy.get('@killStep').should('be.visible').click(); @@ -304,7 +283,7 @@ context('Steps', () => { it('last step should not contain killed/skipped', () => { cy.get('[data-test=step]').last().as('echoStep'); - cy.get('@echoStep').should('be.visible').click({ force: true }); + cy.get('[data-test=step-header-5]').click({ force: true }); cy.get('@echoStep').should('not.contain', 'error:'); cy.get('@echoStep').should('not.contain', 'step was killed'); cy.get('@echoStep').contains('$'); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index a8738bec0..bf4790527 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -34,10 +34,7 @@ Cypress.Commands.add('loggingIn', (path = '/') => { cy.visit('/account/authenticate?code=deadbeef&state=1337', { onBeforeLoad: win => { - win.localStorage.setItem( - 'vela-redirect', - `${path}`, - ); + win.localStorage.setItem('vela-redirect', `${path}`); }, }); }); @@ -536,7 +533,7 @@ Cypress.Commands.add('stubPipelineExpand', () => { url: '*api/v1/pipelines/*/*/*/expand*', status: 200, response: '@expanded', - }); + }).as('expand'); }); Cypress.Commands.add('stubPipelineExpandErrors', () => { diff --git a/package-lock.json b/package-lock.json index 2f97b90c5..7b385d490 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@fullhuman/postcss-purgecss": "5.0.0", "@parcel/transformer-elm": "2.11.0", "@parcel/transformer-sass": "2.11.0", - "axe-core": "4.8.3", + "axe-core": "4.8.4", "cypress": "5.6.0", "cypress-axe": "0.14.0", "elm": "0.19.1-6", @@ -27,8 +27,8 @@ "make-dir-cli": "3.1.0", "ncp": "2.0.0", "parcel": "2.11.0", - "postcss": "8.4.33", - "prettier": "3.2.4", + "postcss": "8.4.35", + "prettier": "3.2.5", "rimraf": "5.0.5", "start-server-and-test": "2.0.3", "stylelint": "15.11.0", @@ -494,9 +494,9 @@ } }, "node_modules/@hpcc-js/wasm": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-2.15.3.tgz", - "integrity": "sha512-enmVW4APrv6jBCRP5V/WdIjYvxidNgBbgdWOdLpiygoE0g0ZurM1qsysBo4TbZfdS81SCdkjRSU/URWf+gpQUA==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-2.16.0.tgz", + "integrity": "sha512-e2aPTthjER80Kt/BZPHKksm6YjyMul4qUDDbIxhZYjbUvrxy9ogDr81P4pa1DSUIMzQ1f/7yCC22SBYIa3j5gg==", "dependencies": { "yargs": "17.7.2" }, @@ -2812,9 +2812,9 @@ "dev": true }, "node_modules/axe-core": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.3.tgz", - "integrity": "sha512-d5ZQHPSPkF9Tw+yfyDcRoUOc4g/8UloJJe5J8m4L5+c7AtDdjDLRxew/knnI4CxvtdxEUVgWz4x3OIQUIFiMfw==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.4.tgz", + "integrity": "sha512-CZLSKisu/bhJ2awW4kJndluz2HLZYIHh5Uy1+ZwDRkJi69811xgIXXfdU9HSLX0Th+ILrHj8qfL/5wzamsFtQg==", "dev": true, "engines": { "node": ">=4" @@ -7077,9 +7077,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -7236,9 +7236,9 @@ } }, "node_modules/prettier": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", - "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/package.json b/package.json index e9cded920..6d71d088a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@fullhuman/postcss-purgecss": "5.0.0", "@parcel/transformer-elm": "2.11.0", "@parcel/transformer-sass": "2.11.0", - "axe-core": "4.8.3", + "axe-core": "4.8.4", "cypress": "5.6.0", "cypress-axe": "0.14.0", "elm": "0.19.1-6", @@ -25,8 +25,8 @@ "make-dir-cli": "3.1.0", "ncp": "2.0.0", "parcel": "2.11.0", - "postcss": "8.4.33", - "prettier": "3.2.4", + "postcss": "8.4.35", + "prettier": "3.2.5", "rimraf": "5.0.5", "start-server-and-test": "2.0.3", "stylelint": "15.11.0", diff --git a/src/elm/Api/Operations.elm b/src/elm/Api/Operations.elm index de793f57a..957806a38 100644 --- a/src/elm/Api/Operations.elm +++ b/src/elm/Api/Operations.elm @@ -9,6 +9,7 @@ module Api.Operations exposing , addRepoSchedule , addRepoSecret , addSharedSecret + , approveBuild , cancelBuild , chownRepo , deleteOrgSecret @@ -286,6 +287,8 @@ getRepoBuilds baseUrl session options = |> withAuth session +{-| restartBuild : restarts a build +-} restartBuild : String -> Session @@ -308,6 +311,8 @@ restartBuild baseUrl session options = |> withAuth session +{-| cancelBuild : cancels a build +-} cancelBuild : String -> Session @@ -329,6 +334,30 @@ cancelBuild baseUrl session options = |> withAuth session +{-| approveBuild : approves a build +-} +approveBuild : + String + -> Session + -> + { a + | org : String + , repo : String + , buildNumber : String + } + -> Request Vela.Build +approveBuild baseUrl session options = + post baseUrl + (Api.Endpoint.ApproveBuild + options.org + options.repo + options.buildNumber + ) + Http.emptyBody + Vela.decodeBuild + |> withAuth session + + {-| getRepoDeployments : retrieves deployments for a repo -} getRepoDeployments : diff --git a/src/elm/Components/Build.elm b/src/elm/Components/Build.elm index 2474a3cad..9217f07ab 100644 --- a/src/elm/Components/Build.elm +++ b/src/elm/Components/Build.elm @@ -3,7 +3,7 @@ SPDX-License-Identifier: Apache-2.0 --} -module Components.Build exposing (view, viewActionsMenu, viewCancelBuildButton, viewRestartBuildButton) +module Components.Build exposing (view, viewActionsMenu, viewApproveButton, viewCancelButton, viewRestartButton) import Components.Svgs import DateFormat.Relative @@ -12,14 +12,10 @@ import Html exposing (Html, a, button, details, div, label, li, span, strong, su import Html.Attributes exposing (attribute, class, classList, href, id, title) import Html.Events exposing (onClick) import List.Extra -import Maybe.Extra import RemoteData exposing (WebData) -import Route import Route.Path import Shared -import Svg exposing (path) import Time -import Url import Utils.Helpers as Util import Vela @@ -28,7 +24,6 @@ type alias Props msg = { build : WebData Vela.Build , showFullTimestamps : Bool , actionsMenu : Html msg - , showActionsMenuBool : Bool } @@ -252,7 +247,6 @@ viewActionsMenu : } , build : Vela.Build , showActionsMenus : List Int - , showActionsMenuBool : Bool } -> Html msg viewActionsMenu props = @@ -260,9 +254,6 @@ viewActionsMenu props = ( org, repo ) = Util.orgRepoFromBuildLink props.build.link - isMenuOpen = - List.member props.build.id props.showActionsMenus - buildMenuBaseClassList = classList [ ( "details", True ) @@ -274,25 +265,7 @@ viewActionsMenu props = buildMenuAttributeList = Util.open (List.member props.build.id props.showActionsMenus) ++ [ id "build-actions" ] - approveBuild = - case props.build.status of - Vela.PendingApproval -> - li [ class "build-menu-item" ] - [ a - [ href "#" - , class "menu-item" - - -- , Util.onClickPreventDefault <| props.msgs.approveBuild org repo <| String.fromInt build.number - , Util.testAttribute "approve-build" - ] - [ text "Approve Build" - ] - ] - - _ -> - text "" - - restartBuild = + viewRestartLink = case props.build.status of Vela.PendingApproval -> text "" @@ -314,7 +287,7 @@ viewActionsMenu props = ] ] - cancelBuild = + viewCancelLink = case props.build.status of Vela.Running -> li [ class "build-menu-item" ] @@ -361,6 +334,24 @@ viewActionsMenu props = _ -> text "" + + viewApproveLink = + case props.build.status of + Vela.PendingApproval -> + li [ class "build-menu-item" ] + [ a + [ href "#" + , class "menu-item" + + -- , Util.onClickPreventDefault <| props.msgs.approveBuild org repo <| String.fromInt build.number + , Util.testAttribute "approve-build" + ] + [ text "Approve Build" + ] + ] + + _ -> + text "" in details (buildMenuBaseClassList :: buildMenuAttributeList) [ summary @@ -376,9 +367,9 @@ viewActionsMenu props = , attribute "aria-hidden" "true" , attribute "role" "menu" ] - [ approveBuild - , restartBuild - , cancelBuild + [ viewApproveLink + , viewRestartLink + , viewCancelLink ] ] @@ -633,3 +624,55 @@ bottomBuildNumberDashes buildNumber = _ -> "-animation-dashes-2" + + + +-- BUILD + + +{-| viewCancelButton : takes org repo and build number and renders button to cancel a build +-} +viewCancelButton : Vela.Org -> Vela.Repo -> Vela.BuildNumber -> ({ org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -> msg) -> Html msg +viewCancelButton org repo buildNumber cancelBuild = + button + [ classList + [ ( "button", True ) + , ( "-outline", True ) + ] + , onClick <| cancelBuild { org = org, repo = repo, buildNumber = buildNumber } + , Util.testAttribute "cancel-build" + ] + [ text "Cancel Build" + ] + + +{-| viewRestartButton : takes org repo and build number and renders button to restart a build +-} +viewRestartButton : Vela.Org -> Vela.Repo -> Vela.BuildNumber -> ({ org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -> msg) -> Html msg +viewRestartButton org repo buildNumber restartBuild = + button + [ classList + [ ( "button", True ) + , ( "-outline", True ) + ] + , onClick <| restartBuild { org = org, repo = repo, buildNumber = buildNumber } + , Util.testAttribute "restart-build" + ] + [ text "Restart Build" + ] + + +{-| viewApproveButton: takes org repo and build number and renders button to approve a build run +-} +viewApproveButton : Vela.Org -> Vela.Repo -> Vela.BuildNumber -> ({ org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -> msg) -> Html msg +viewApproveButton org repo buildNumber approveBuild = + button + [ classList + [ ( "button", True ) + , ( "-outline", True ) + ] + , onClick <| approveBuild { org = org, repo = repo, buildNumber = buildNumber } + , Util.testAttribute "approve-build" + ] + [ text "Approve Build" + ] diff --git a/src/elm/Components/Builds.elm b/src/elm/Components/Builds.elm index 585d9e939..a9a98237f 100644 --- a/src/elm/Components/Builds.elm +++ b/src/elm/Components/Builds.elm @@ -47,7 +47,7 @@ import Vela type alias Msgs msg = - { approveBuild : Vela.Org -> Vela.Repo -> Vela.BuildNumber -> msg + { approveBuild : { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -> msg , restartBuild : { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -> msg , cancelBuild : { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -> msg , showHideActionsMenus : Maybe Int -> Maybe Bool -> msg @@ -123,7 +123,6 @@ view shared props = { build = RemoteData.succeed build , showFullTimestamps = props.showFullTimestamps , actionsMenu = props.viewActionsMenu { build = build } - , showActionsMenuBool = True } ) builds diff --git a/src/elm/Components/Form.elm b/src/elm/Components/Form.elm index c99b36aac..3370096e6 100644 --- a/src/elm/Components/Form.elm +++ b/src/elm/Components/Form.elm @@ -19,9 +19,9 @@ import Vela viewInput : - { title : Maybe String + { id_ : String + , title : Maybe String , subtitle : Maybe (Html msg) - , id_ : String , val : String , placeholder_ : String , classList_ : List ( String, Bool ) @@ -31,7 +31,7 @@ viewInput : , disabled_ : Bool } -> Html msg -viewInput { title, subtitle, id_, val, placeholder_, classList_, rows_, wrap_, msg, disabled_ } = +viewInput { id_, title, subtitle, val, placeholder_, classList_, rows_, wrap_, msg, disabled_ } = section [ class "form-control" , class "-stack" @@ -51,15 +51,16 @@ viewInput { title, subtitle, id_, val, placeholder_, classList_, rows_, wrap_, m , Maybe.Extra.unwrap Util.attrNone wrap wrap_ , onInput msg , disabled disabled_ + , Util.testAttribute id_ ] [] ] viewTextarea : - { title : Maybe String + { id_ : String + , title : Maybe String , subtitle : Maybe (Html msg) - , id_ : String , val : String , placeholder_ : String , classList_ : List ( String, Bool ) @@ -69,7 +70,7 @@ viewTextarea : , disabled_ : Bool } -> Html msg -viewTextarea { title, subtitle, id_, val, placeholder_, classList_, rows_, wrap_, msg, disabled_ } = +viewTextarea { id_, title, subtitle, val, placeholder_, classList_, rows_, wrap_, msg, disabled_ } = section [ class "form-control" , class "-stack" @@ -89,13 +90,15 @@ viewTextarea { title, subtitle, id_, val, placeholder_, classList_, rows_, wrap_ , Maybe.Extra.unwrap Util.attrNone wrap wrap_ , onInput msg , disabled disabled_ + , Util.testAttribute id_ ] [] ] viewCheckbox : - { title : String + { id_ : String + , title : String , subtitle : Maybe (Html msg) , field : String , state : Bool @@ -103,7 +106,7 @@ viewCheckbox : , disabled_ : Bool } -> Html msg -viewCheckbox { title, subtitle, field, state, msg, disabled_ } = +viewCheckbox { id_, title, subtitle, field, state, msg, disabled_ } = div [ class "form-control" , Util.testAttribute <| "checkbox-" ++ field @@ -114,6 +117,7 @@ viewCheckbox { title, subtitle, field, state, msg, disabled_ } = , checked state , onCheck msg , disabled disabled_ + , Util.testAttribute id_ ] [] , label [ class "form-label", for <| "checkbox-" ++ field ] @@ -122,7 +126,8 @@ viewCheckbox { title, subtitle, field, state, msg, disabled_ } = viewRadio : - { title : String + { id_ : String + , title : String , subtitle : Maybe (Html msg) , value : String , field : String @@ -130,7 +135,7 @@ viewRadio : , disabled_ : Bool } -> Html msg -viewRadio { title, subtitle, value, field, msg, disabled_ } = +viewRadio { id_, title, subtitle, value, field, msg, disabled_ } = div [ class "form-control", Util.testAttribute <| "radio-" ++ field ] [ input [ type_ "radio" @@ -138,6 +143,7 @@ viewRadio { title, subtitle, value, field, msg, disabled_ } = , checked (value == field) , onClick msg , disabled disabled_ + , Util.testAttribute id_ ] [] , label [ class "form-label", for <| "radio-" ++ field ] @@ -145,23 +151,24 @@ viewRadio { title, subtitle, value, field, msg, disabled_ } = ] -viewSubtitle : Maybe (Html msg) -> Html msg -viewSubtitle subtitle = - Maybe.Extra.unwrap (text "") (\s -> span [] [ text <| " ", s ]) subtitle - - -viewButton : { msg : msg, text_ : String, classList_ : List ( String, Bool ), disabled_ : Bool } -> Html msg -viewButton { msg, text_, classList_, disabled_ } = +viewButton : { id_ : String, msg : msg, text_ : String, classList_ : List ( String, Bool ), disabled_ : Bool } -> Html msg +viewButton { id_, msg, text_, classList_, disabled_ } = button [ class "button" , class "-outline" , onClick msg , disabled disabled_ , classList classList_ + , Util.testAttribute id_ ] [ text text_ ] +viewSubtitle : Maybe (Html msg) -> Html msg +viewSubtitle subtitle = + Maybe.Extra.unwrap (text "") (\s -> span [] [ text <| " ", s ]) subtitle + + viewAllowEvents : Shared.Model -> @@ -180,6 +187,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.push.branch , msg = msg { allowEvents = allowEvents, event = "allow_push_branch" } , disabled_ = False + , id_ = "allow-events-push-branch" } , viewCheckbox { title = "Tag" @@ -188,6 +196,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.push.tag , msg = msg { allowEvents = allowEvents, event = "allow_push_tag" } , disabled_ = False + , id_ = "allow-events-push-tag" } ] , h3 [ class "settings-subtitle" ] [ text "Delete" ] @@ -199,6 +208,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.push.deleteBranch , msg = msg { allowEvents = allowEvents, event = "allow_push_delete_branch" } , disabled_ = False + , id_ = "allow-events-push-delete-branch" } , viewCheckbox { title = "Tag" @@ -207,6 +217,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.push.deleteTag , msg = msg { allowEvents = allowEvents, event = "allow_push_delete_tag" } , disabled_ = False + , id_ = "allow-events-push-delete-tag" } ] , h3 [ class "settings-subtitle" ] [ text "Pull Request" ] @@ -218,6 +229,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.pull.opened , msg = msg { allowEvents = allowEvents, event = "allow_pull_opened" } , disabled_ = False + , id_ = "allow-events-pull-opened" } , viewCheckbox { title = "Synchronize" @@ -226,6 +238,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.pull.synchronize , msg = msg { allowEvents = allowEvents, event = "allow_pull_synchronize" } , disabled_ = False + , id_ = "allow-events-pull-synchronize" } , viewCheckbox { title = "Edited" @@ -234,6 +247,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.pull.edited , msg = msg { allowEvents = allowEvents, event = "allow_pull_edited" } , disabled_ = False + , id_ = "allow-events-pull-edited" } , viewCheckbox { title = "Reopened" @@ -242,6 +256,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.pull.reopened , msg = msg { allowEvents = allowEvents, event = "allow_pull_reopened" } , disabled_ = False + , id_ = "allow-events-pull-reopened" } ] , h3 [ class "settings-subtitle" ] [ text "Deployments" ] @@ -253,6 +268,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.deploy.created , msg = msg { allowEvents = allowEvents, event = "allow_deploy_created" } , disabled_ = False + , id_ = "allow-events-deploy-created" } ] , h3 [ class "settings-subtitle" ] [ text "Comment" ] @@ -264,6 +280,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.comment.created , msg = msg { allowEvents = allowEvents, event = "allow_comment_created" } , disabled_ = False + , id_ = "allow-events-comment-created" } , viewCheckbox { title = "Edited" @@ -272,6 +289,7 @@ viewAllowEvents shared { msg, allowEvents } = , state = allowEvents.comment.edited , msg = msg { allowEvents = allowEvents, event = "allow_comment_edited" } , disabled_ = False + , id_ = "allow-events-comment-edited" } ] ] diff --git a/src/elm/Components/Logs.elm b/src/elm/Components/Logs.elm index edc59f7e6..25e01d816 100644 --- a/src/elm/Components/Logs.elm +++ b/src/elm/Components/Logs.elm @@ -163,7 +163,7 @@ viewLine pushUrlHashMsg resourceType resourceNumber shift focus logLine lineNumb ] [ div [ class "wrapper" - , Util.testAttribute <| String.join "-" [ "log", "line", resourceType, resourceNumber, String.fromInt lineNumber ] + , Util.testAttribute <| String.join "-" [ "log", "line", resourceNumber, String.fromInt lineNumber ] , class <| Focus.lineRangeStyles (String.toInt resourceNumber) lineNumber focus ] [ td [] @@ -173,7 +173,7 @@ viewLine pushUrlHashMsg resourceType resourceNumber shift focus logLine lineNumb { hash = Focus.toString <| Focus.updateLineRange shift (String.toInt resourceNumber) lineNumber focus } - , Util.testAttribute <| String.join "-" [ "log", "line", "num", resourceType, resourceNumber, String.fromInt lineNumber ] + , Util.testAttribute <| String.join "-" [ "log", "line", "num", resourceNumber, String.fromInt lineNumber ] , Focus.toAttr { group = String.toInt resourceNumber , a = Just lineNumber @@ -182,12 +182,12 @@ viewLine pushUrlHashMsg resourceType resourceNumber shift focus logLine lineNumb , class "line-number" , class "button" , class "-link" - , attribute "aria-label" <| "focus " ++ resourceType ++ " " ++ resourceNumber + , attribute "aria-label" <| "focus log for resource " ++ resourceNumber ] [ span [] [ text <| String.fromInt lineNumber ] ] ] , td [ class "break-text", class "overflow-auto" ] - [ code [ Util.testAttribute <| String.join "-" [ "log", "data", resourceType, resourceNumber, String.fromInt lineNumber ] ] + [ code [ Util.testAttribute <| String.join "-" [ "log", "data", resourceNumber, String.fromInt lineNumber ] ] [ logLine.view ] ] @@ -356,12 +356,12 @@ viewJumpToBottomButton props = , attribute "data-tooltip" "jump to bottom" , Util.testAttribute <| "jump-to-bottom-" ++ props.resourceNumber , onClick <| props.msgs.focusOn { target = Logs.bottomTrackerFocusId props.resourceNumber } - , attribute "aria-label" <| "jump to bottom of logs for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| "jump to bottom of logs for resource " ++ props.resourceNumber ] [ FeatherIcons.arrowDown |> FeatherIcons.toHtml [ attribute "role" "img" - , attribute "aria-label" <| "jump to the bottom of logs for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| "jump to the bottom of logs for resource " ++ props.resourceNumber ] ] @@ -377,12 +377,12 @@ viewJumpToTopButton props = , attribute "data-tooltip" "jump to top" , Util.testAttribute <| "jump-to-top-" ++ props.resourceNumber , onClick <| props.msgs.focusOn { target = Logs.topTrackerFocusId props.resourceNumber } - , attribute "aria-label" <| "jump to top of logs for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| "jump to top of logs for resource " ++ props.resourceNumber ] [ FeatherIcons.arrowUp |> FeatherIcons.toHtml [ attribute "role" "img" - , attribute "aria-label" <| "jump to the top of logs for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| "jump to the top of logs for resource " ++ props.resourceNumber ] ] @@ -407,9 +407,9 @@ viewDownloadButton props log = , Util.attrIf logEmpty <| Util.ariaHidden , Util.testAttribute <| "download-logs-" ++ props.resourceNumber , onClick <| props.msgs.download { filename = fileName, content = log.rawData, map = Util.base64Decode } - , attribute "aria-label" <| "download logs for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| "download logs for resource " ++ props.resourceNumber ] - [ text <| "download " ++ props.resourceType ++ " logs" ] + [ text <| "download resource logs" ] {-| viewFollowButton : renders button for following logs @@ -425,13 +425,13 @@ viewFollowButton props = ( tooltip, icon, toFollow ) = if following == 0 then - ( "start following " ++ props.resourceType ++ " logs", FeatherIcons.play, num ) + ( "start following resource logs", FeatherIcons.play, num ) else if following == num then - ( "stop following " ++ props.resourceType ++ " logs", FeatherIcons.pause, 0 ) + ( "stop following resource logs", FeatherIcons.pause, 0 ) else - ( "start following " ++ props.resourceType ++ " logs", FeatherIcons.play, num ) + ( "start following resource logs", FeatherIcons.play, num ) in button [ class "button" @@ -440,11 +440,11 @@ viewFollowButton props = , attribute "data-tooltip" tooltip , Util.testAttribute <| "follow-logs-" ++ props.resourceNumber , onClick <| props.msgs.follow { number = toFollow } - , attribute "aria-label" <| tooltip ++ " for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| tooltip ++ " for resource " ++ props.resourceNumber ] [ icon |> FeatherIcons.toHtml [ attribute "role" "img" - , attribute "aria-label" <| "follow logs for " ++ props.resourceType ++ " " ++ props.resourceNumber + , attribute "aria-label" <| "follow logs for resource " ++ props.resourceNumber ] ] diff --git a/src/elm/Components/Nav.elm b/src/elm/Components/Nav.elm index cefa48fd7..5e4bd30b8 100644 --- a/src/elm/Components/Nav.elm +++ b/src/elm/Components/Nav.elm @@ -8,22 +8,16 @@ module Components.Nav exposing (view) import Html exposing ( Html - , button + , div , nav - , text ) import Html.Attributes exposing ( attribute , class - , classList ) -import Html.Events exposing (onClick) -import RemoteData exposing (WebData) import Route exposing (Route) import Shared -import Utils.Helpers as Util -import Vela @@ -40,9 +34,9 @@ type alias Props msg = -- VIEW -view : Shared.Model -> Route () -> Props msg -> Html msg +view : Shared.Model -> Route params -> Props msg -> Html msg view shared route props = nav [ class "navigation", attribute "aria-label" "Navigation" ] - (props.crumbs - :: props.buttons - ) + [ props.crumbs + , div [ class "buttons" ] props.buttons + ] diff --git a/src/elm/Components/Repo.elm b/src/elm/Components/Repo.elm index 3f9b0043b..d8e306c88 100644 --- a/src/elm/Components/Repo.elm +++ b/src/elm/Components/Repo.elm @@ -63,7 +63,7 @@ view shared { toggleFavoriteMsg, org, repo, favorites, filtered } = [ class "button" , class "-outline" , Util.testAttribute "repo-hooks" - , Route.Path.href <| Route.Path.Org_Repo_Audit { org = org, repo = repo } + , Route.Path.href <| Route.Path.Org_Repo_Hooks { org = org, repo = repo } ] [ text "Audit" ] , a diff --git a/src/elm/Components/ScheduleForm.elm b/src/elm/Components/ScheduleForm.elm index 2d4320ff7..76e3b94f3 100644 --- a/src/elm/Components/ScheduleForm.elm +++ b/src/elm/Components/ScheduleForm.elm @@ -3,7 +3,7 @@ SPDX-License-Identifier: Apache-2.0 --} -module Components.ScheduleForm exposing (viewCronHelp, viewEnabledInput, viewHelp, viewSubmitButton) +module Components.ScheduleForm exposing (viewCronHelp, viewEnabledInput, viewHelp, viewSchedulesNotAllowedWarning, viewSubmitButton) import Components.Form import Html @@ -75,6 +75,7 @@ viewEnabledInput { msg, value, disabled_ } = , subtitle = Nothing , msg = msg "yes" , disabled_ = disabled_ + , id_ = "schedule-active-yes" } , Components.Form.viewRadio { value = Util.boolToYesNo value @@ -83,6 +84,7 @@ viewEnabledInput { msg, value, disabled_ } = , subtitle = Nothing , msg = msg "no" , disabled_ = disabled_ + , id_ = "schedule-active-no" } ] ] @@ -112,3 +114,10 @@ viewSubmitButton { msg, disabled_ } = ] [ text "Submit" ] ] + + +viewSchedulesNotAllowedWarning : Html msg +viewSchedulesNotAllowedWarning = + span [ class "not-allowed", Util.testAttribute "repo-schedule-not-allowed" ] + [ text "Sorry, Administrators have not enabled Schedules for this repository." + ] diff --git a/src/elm/Components/SecretForm.elm b/src/elm/Components/SecretForm.elm index 4b176b1e4..00a1e285d 100644 --- a/src/elm/Components/SecretForm.elm +++ b/src/elm/Components/SecretForm.elm @@ -147,6 +147,7 @@ viewAllowCommandsInput { msg, value, disabled_ } = , subtitle = Nothing , msg = msg "yes" , disabled_ = disabled_ + , id_ = "secret-allow-commands-yes" } , Components.Form.viewRadio { value = Util.boolToYesNo value @@ -155,6 +156,7 @@ viewAllowCommandsInput { msg, value, disabled_ } = , subtitle = Nothing , msg = msg "no" , disabled_ = disabled_ + , id_ = "secret-allow-commands-no" } ] ] diff --git a/src/elm/Components/Secrets.elm b/src/elm/Components/Secrets.elm index 865585751..344a64ebe 100644 --- a/src/elm/Components/Secrets.elm +++ b/src/elm/Components/Secrets.elm @@ -160,7 +160,7 @@ viewSharedSecrets shared props = ) RemoteData.Failure error -> - ( span [ Util.testAttribute "repo-secrets-error" ] + ( span [ Util.testAttribute "shared-secrets-error" ] [ text <| case error of Http.BadStatus statusCode -> @@ -196,7 +196,7 @@ viewSharedSecrets shared props = -} tableHeaders : Components.Table.Columns tableHeaders = - [ ( Just "table-icon", "" ) + [ ( Just "table-icon", "copy" ) , ( Nothing, "name" ) , ( Nothing, "key" ) , ( Nothing, "type" ) @@ -265,14 +265,25 @@ viewSecret engine type_ copyMsg secret = , scope "row" , class "break-word" ] - [ Components.Table.viewListCell (enabledAllowEventsToList secret.allowEvents) "no events" [] + [ Components.Table.viewListCell + { dataLabel = "events" + , items = enabledAllowEventsToList secret.allowEvents + , none = "no events" + , itemWrapperClassList = [] + } ] , td [ attribute "data-label" "images" , scope "row" , class "break-word" ] - [ Components.Table.viewListCell secret.images "all images" [ ( "secret-image", True ) ] ] + [ Components.Table.viewListCell + { dataLabel = "images" + , items = secret.images + , none = "all images" + , itemWrapperClassList = [ ( "secret-image", True ) ] + } + ] , Components.Table.viewItemCell { dataLabel = "allow command" , parentClassList = [] diff --git a/src/elm/Components/Table.elm b/src/elm/Components/Table.elm index 581d9a5e6..2b4899aa7 100644 --- a/src/elm/Components/Table.elm +++ b/src/elm/Components/Table.elm @@ -103,7 +103,18 @@ view { label, testLabel, noRows, columns, rows, headerElement } = , Maybe.withDefault (text "") headerElement ] ] - , thead [] [ tr [] <| List.map (\( className, col ) -> th [ class <| Maybe.withDefault "" className, scope "col" ] [ text <| String.Extra.toTitleCase col ]) columns ] + , thead [] + [ tr [] <| + List.map + (\( className, col ) -> + th + [ class <| Maybe.withDefault "" className + , scope "col" + ] + [ text <| String.Extra.toTitleCase col ] + ) + columns + ] , viewFooter noRows numRows numColumns , tbody [] <| List.map (\row_ -> row_.display row_.data) rows ] @@ -122,11 +133,12 @@ viewFooter noRows numRows numColumns = {-| viewListCell : takes list of items, text for none and className and renders a table cell -} -viewListCell : List String -> String -> List ( String, Bool ) -> Html msg -viewListCell items none itemWrapperClassList = +viewListCell : { dataLabel : String, items : List String, none : String, itemWrapperClassList : List ( String, Bool ) } -> Html msg +viewListCell { dataLabel, items, none, itemWrapperClassList } = if List.length items == 0 then span [ class "single-item" + , Util.testAttribute <| "cell-list-item-" ++ dataLabel ] [ text none ] @@ -135,7 +147,10 @@ viewListCell items none itemWrapperClassList = |> List.sort |> List.map (\item -> - div [ classList itemWrapperClassList ] + div + [ classList itemWrapperClassList + , Util.testAttribute <| "cell-list-item-" ++ dataLabel + ] [ span [ class "list-item" ] [ text item ] @@ -152,7 +167,7 @@ viewListItemCell { dataLabel, parentClassList, itemWrapperClassList, itemClassLi [ attribute "data-label" dataLabel , class "break-word" , classList parentClassList - , scope "row" + , Util.testAttribute <| "cell-" ++ dataLabel ] [ div [ classList itemWrapperClassList ] [ span @@ -172,7 +187,7 @@ viewItemCell { dataLabel, parentClassList, itemClassList, children } = [ attribute "data-label" dataLabel , class "break-word" , classList parentClassList - , scope "row" + , Util.testAttribute <| "cell-" ++ dataLabel ] [ span [ class "single-item" @@ -191,6 +206,7 @@ viewIconCell { dataLabel, parentClassList, itemWrapperClassList, itemClassList, , class "break-word" , class "table-icon" , classList parentClassList + , Util.testAttribute <| "cell-" ++ dataLabel ] [ div [ classList itemWrapperClassList diff --git a/src/elm/Components/Tabs.elm b/src/elm/Components/Tabs.elm index bbb9ff61e..2c4819632 100644 --- a/src/elm/Components/Tabs.elm +++ b/src/elm/Components/Tabs.elm @@ -228,7 +228,7 @@ viewRepoTabs shared props = , show = showSchedules } , { name = "Audit" - , toPath = Route.Path.Org_Repo_Audit { org = props.org, repo = props.repo } + , toPath = Route.Path.Org_Repo_Hooks { org = props.org, repo = props.repo } , isAlerting = auditAlerting , show = True } diff --git a/src/elm/Effect.elm b/src/elm/Effect.elm index adf2e5e3c..f8307ed9d 100644 --- a/src/elm/Effect.elm +++ b/src/elm/Effect.elm @@ -9,7 +9,7 @@ module Effect exposing , sendCmd, sendMsg , pushRoute, replaceRoute, loadExternalUrl , map, toCmd - , addAlertError, addAlertSuccess, addDeployment, addOrgSecret, addRepoSchedule, addRepoSecret, addSharedSecret, alertsUpdate, cancelBuild, chownRepo, clearRedirect, deleteOrgSecret, deleteRepoSchedule, deleteRepoSecret, deleteSharedSecret, disableRepo, downloadFile, enableRepo, expandPipelineConfig, finishAuthentication, focusOn, getBuild, getBuildGraph, getBuildServiceLog, getBuildServices, getBuildStepLog, getBuildSteps, getCurrentUser, getOrgBuilds, getOrgRepos, getOrgSecret, getOrgSecrets, getPipelineConfig, getPipelineTemplates, getRepo, getRepoBuilds, getRepoBuildsShared, getRepoDeployments, getRepoHooks, getRepoHooksShared, getRepoSchedule, getRepoSchedules, getRepoSecret, getRepoSecrets, getSharedSecret, getSharedSecrets, handleHttpError, logout, pushPath, redeliverHook, repairRepo, replacePath, replaceRouteRemoveTabHistorySkipDomFocus, restartBuild, setRedirect, setTheme, updateFavicon, updateFavorites, updateOrgSecret, updateRepo, updateRepoSchedule, updateRepoSecret, updateSharedSecret + , addAlertError, addAlertSuccess, addDeployment, addOrgSecret, addRepoSchedule, addRepoSecret, addSharedSecret, alertsUpdate, approveBuild, cancelBuild, chownRepo, clearRedirect, deleteOrgSecret, deleteRepoSchedule, deleteRepoSecret, deleteSharedSecret, disableRepo, downloadFile, enableRepo, expandPipelineConfig, finishAuthentication, focusOn, getBuild, getBuildGraph, getBuildServiceLog, getBuildServices, getBuildStepLog, getBuildSteps, getCurrentUser, getOrgBuilds, getOrgRepos, getOrgSecret, getOrgSecrets, getPipelineConfig, getPipelineTemplates, getRepo, getRepoBuilds, getRepoBuildsShared, getRepoDeployments, getRepoHooks, getRepoHooksShared, getRepoSchedule, getRepoSchedules, getRepoSecret, getRepoSecrets, getSharedSecret, getSharedSecrets, handleHttpError, logout, pushPath, redeliverHook, repairRepo, replacePath, replaceRouteRemoveTabHistorySkipDomFocus, restartBuild, setRedirect, setTheme, updateFavicon, updateFavorites, updateOrgSecret, updateRepo, updateRepoSchedule, updateRepoSecret, updateSharedSecret ) {-| @@ -445,23 +445,51 @@ getRepoDeployments options = restartBuild : - { org : String + { baseUrl : String + , session : Auth.Session.Session + , onResponse : Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build ) -> msg + , org : String , repo : String , buildNumber : String } -> Effect msg restartBuild options = - SendSharedMsg <| Shared.Msg.RestartBuild options + Api.try + options.onResponse + (Api.Operations.restartBuild options.baseUrl options.session options) + |> sendCmd cancelBuild : - { org : String + { baseUrl : String + , session : Auth.Session.Session + , onResponse : Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build ) -> msg + , org : String , repo : String , buildNumber : String } -> Effect msg cancelBuild options = - SendSharedMsg <| Shared.Msg.CancelBuild options + Api.try + options.onResponse + (Api.Operations.cancelBuild options.baseUrl options.session options) + |> sendCmd + + +approveBuild : + { baseUrl : String + , session : Auth.Session.Session + , onResponse : Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build ) -> msg + , org : String + , repo : String + , buildNumber : String + } + -> Effect msg +approveBuild options = + Api.try + options.onResponse + (Api.Operations.approveBuild options.baseUrl options.session options) + |> sendCmd addDeployment : diff --git a/src/elm/Layouts.elm b/src/elm/Layouts.elm index 8c0340ded..648a5d2f6 100644 --- a/src/elm/Layouts.elm +++ b/src/elm/Layouts.elm @@ -12,7 +12,7 @@ import Layouts.Default.Repo type Layout msg - = Default (Layouts.Default.Props msg) + = Default Layouts.Default.Props | Default_Org (Layouts.Default.Org.Props msg) | Default_Repo (Layouts.Default.Repo.Props msg) | Default_Build (Layouts.Default.Build.Props msg) @@ -22,7 +22,7 @@ map : (msg1 -> msg2) -> Layout msg1 -> Layout msg2 map fn layout = case layout of Default data -> - Default <| Layouts.Default.map fn data + Default data Default_Org data -> Default_Org <| Layouts.Default.Org.map fn data diff --git a/src/elm/Layouts/Default.elm b/src/elm/Layouts/Default.elm index 47726e36a..77f287e38 100644 --- a/src/elm/Layouts/Default.elm +++ b/src/elm/Layouts/Default.elm @@ -3,56 +3,34 @@ SPDX-License-Identifier: Apache-2.0 --} -module Layouts.Default exposing (Model, Msg, Props, layout, map) +module Layouts.Default exposing (Model, Msg, Props, layout) import Api.Endpoint exposing (Endpoint(..)) import Auth.Session exposing (Session(..)) import Components.Alerts exposing (Alert) -import Components.Crumbs -import Components.Favorites import Components.Footer import Components.Header import Components.Help -import Components.Nav -import Components.Util import Effect exposing (Effect) import Html exposing (..) -import Html.Attributes exposing (class) import Interop import Json.Decode import Layout exposing (Layout) -import Maybe.Extra -import RemoteData exposing (WebData) import Route exposing (Route) import Shared import Toasty as Alerting import Utils.Favicons as Favicons -import Utils.Favorites as Favorites import Utils.Helpers as Util import Utils.Theme as Theme import View exposing (View) -type alias Props contentMsg = - { navButtons : List (Html contentMsg) - , utilButtons : List (Html contentMsg) - , helpCommands : List Components.Help.Command - , crumbs : List Components.Crumbs.Crumb - , repo : Maybe ( String, String ) +type alias Props = + { helpCommands : List Components.Help.Command } -map : (msg1 -> msg2) -> Props msg1 -> Props msg2 -map fn props = - { navButtons = List.map (Html.map fn) props.navButtons - , utilButtons = List.map (Html.map fn) props.utilButtons - , helpCommands = props.helpCommands - , crumbs = props.crumbs - , repo = props.repo - } - - -layout : Props contentMsg -> Shared.Model -> Route () -> Layout () Model Msg contentMsg +layout : Props -> Shared.Model -> Route () -> Layout () Model Msg contentMsg layout props shared route = Layout.new { init = init shared @@ -90,8 +68,6 @@ type Msg -- HEADER | ShowHideIdentity (Maybe Bool) | ShowHideHelp (Maybe Bool) - -- FAVORITES - | ToggleFavorite String (Maybe String) -- THEME | SetTheme Theme.Theme -- ALERTS @@ -107,15 +83,6 @@ update msg model = , Effect.none ) - ToggleFavorite org maybeRepo -> - ( model - , Effect.updateFavorites - { org = org - , maybeRepo = maybeRepo - , updateType = Favorites.Toggle - } - ) - -- HEADER ShowHideIdentity show -> ( { model @@ -184,7 +151,7 @@ decodeOnThemeChange inTheme = -- VIEW -view : Props contentMsg -> Shared.Model -> Route () -> { toContentMsg : Msg -> contentMsg, content : View contentMsg, model : Model } -> View contentMsg +view : Props -> Shared.Model -> Route () -> { toContentMsg : Msg -> contentMsg, content : View contentMsg, model : Model } -> View contentMsg view props shared route { toContentMsg, model, content } = { title = if String.isEmpty content.title then @@ -198,8 +165,6 @@ view props shared route { toContentMsg, model, content } = { from = Route.toString route , theme = shared.theme , setTheme = SetTheme - - -- todo: use props for this , helpProps = { show = model.showHelp , showHide = ShowHideHelp @@ -210,25 +175,7 @@ view props shared route { toContentMsg, model, content } = , showHideIdentity = ShowHideIdentity } |> Html.map toContentMsg - , Components.Nav.view shared - route - { buttons = - props.navButtons - ++ [ props.repo - |> Maybe.Extra.unwrap (text "") - (\( org, repo ) -> - Components.Favorites.viewStarToggle - { msg = ToggleFavorite - , org = org - , repo = repo - , user = shared.user - } - |> Html.map toContentMsg - ) - ] - , crumbs = Components.Crumbs.view route.path props.crumbs - } - , main_ [ class "content-wrap" ] (Components.Util.view shared route props.utilButtons :: content.body) + , span [] content.body , Components.Footer.view { toasties = shared.toasties , copyAlertMsg = AddAlertCopiedToClipboard diff --git a/src/elm/Layouts/Default/Build.elm b/src/elm/Layouts/Default/Build.elm index 86726de43..3e7fab49f 100644 --- a/src/elm/Layouts/Default/Build.elm +++ b/src/elm/Layouts/Default/Build.elm @@ -7,13 +7,15 @@ module Layouts.Default.Build exposing (Model, Msg, Props, layout, map) import Components.Build import Components.Crumbs -import Components.Favorites import Components.Help +import Components.Nav import Components.RecentBuilds import Components.Tabs +import Components.Util import Dict exposing (Dict) import Effect exposing (Effect) -import Html exposing (Html) +import Html exposing (Html, main_, text) +import Html.Attributes exposing (class) import Http import Http.Detailed import Layout exposing (Layout) @@ -56,7 +58,7 @@ map fn props = } -layout : Props contentMsg -> Shared.Model -> Route () -> Layout (Layouts.Default.Props contentMsg) Model Msg contentMsg +layout : Props contentMsg -> Shared.Model -> Route () -> Layout Layouts.Default.Props Model Msg contentMsg layout props shared route = Layout.new { init = init props shared route @@ -66,11 +68,7 @@ layout props shared route = } |> Layout.withOnUrlChanged OnUrlChanged |> Layout.withParentProps - { navButtons = props.navButtons - , utilButtons = [] - , helpCommands = props.helpCommands - , crumbs = props.crumbs - , repo = Just ( props.org, props.repo ) + { helpCommands = props.helpCommands } @@ -115,11 +113,16 @@ init props shared route _ = type Msg - = Pls - | --BROWSER + = --BROWSER OnUrlChanged { from : Route (), to : Route () } -- BUILD | GetBuildResponse (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) + | RestartBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | RestartBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) + | CancelBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | CancelBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) + | ApproveBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | ApproveBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) -- REFRESH | Tick { time : Time.Posix, interval : Interval.Interval } @@ -127,13 +130,6 @@ type Msg update : Props contentMsg -> Shared.Model -> Route () -> Msg -> Model -> ( Model, Effect Msg ) update props shared route msg model = case msg of - Pls -> - let - _ = - Debug.log "Pls" "123" - in - ( model, Effect.none ) - -- BROWSER OnUrlChanged options -> ( { model @@ -178,6 +174,127 @@ update props shared route msg model = ] ) + RestartBuild options -> + ( model + , Effect.restartBuild + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = RestartBuildResponse options + , org = options.org + , repo = options.repo + , buildNumber = options.buildNumber + } + ) + + RestartBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + restartedBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + + newBuildNumber = + String.fromInt <| build.number + + newBuild = + String.join "/" [ "", options.org, options.repo, newBuildNumber ] + + -- todo: create new build link, add to toastie, refresh builds + in + ( model + , Effect.batch + [ Effect.getRepoBuildsShared + { pageNumber = Nothing + , perPage = Nothing + , maybeEvent = Nothing + , org = props.org + , repo = props.repo + } + , Effect.addAlertSuccess { content = restartedBuild ++ " restarted.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + + CancelBuild options -> + ( model + , Effect.cancelBuild + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = CancelBuildResponse options + , org = options.org + , repo = options.repo + , buildNumber = options.buildNumber + } + ) + + CancelBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + canceledBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + in + ( model + , Effect.batch + [ Effect.getBuild + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetBuildResponse + , org = props.org + , repo = props.repo + , buildNumber = props.buildNumber + } + , Effect.addAlertSuccess { content = canceledBuild ++ " canceled.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + + ApproveBuild options -> + ( model + , Effect.approveBuild + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = ApproveBuildResponse options + , org = options.org + , repo = options.repo + , buildNumber = options.buildNumber + } + ) + + ApproveBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + approvedBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + in + ( model + , Effect.batch + [ Effect.getBuild + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetBuildResponse + , org = props.org + , repo = props.repo + , buildNumber = props.buildNumber + } + , Effect.addAlertSuccess { content = approvedBuild ++ " approved.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + -- REFRESH Tick options -> ( model @@ -212,27 +329,91 @@ subscriptions model = view : Props contentMsg -> Shared.Model -> Route () -> { toContentMsg : Msg -> contentMsg, content : View contentMsg, model : Model } -> View contentMsg view props shared route { toContentMsg, model, content } = + let + viewRestartButton = + case model.build of + RemoteData.Success build -> + case build.status of + Vela.PendingApproval -> + text "" + + _ -> + Components.Build.viewRestartButton props.org props.repo props.buildNumber RestartBuild + |> Html.map toContentMsg + + _ -> + text "" + + viewCancelButton = + case model.build of + RemoteData.Success build -> + case build.status of + Vela.Pending -> + Components.Build.viewCancelButton props.org props.repo props.buildNumber CancelBuild + |> Html.map toContentMsg + + Vela.PendingApproval -> + Components.Build.viewCancelButton props.org props.repo props.buildNumber CancelBuild + |> Html.map toContentMsg + + Vela.Running -> + Components.Build.viewCancelButton props.org props.repo props.buildNumber CancelBuild + |> Html.map toContentMsg + + _ -> + text "" + + _ -> + text "" + + viewApproveButton = + case model.build of + RemoteData.Success build -> + case build.status of + Vela.PendingApproval -> + Components.Build.viewApproveButton props.org props.repo props.buildNumber ApproveBuild + |> Html.map toContentMsg + + _ -> + text "" + + _ -> + text "" + in { title = props.org ++ "/" ++ props.repo ++ " #" ++ props.buildNumber ++ " " ++ content.title , body = - [ Components.RecentBuilds.view shared - { builds = shared.builds - , build = model.build - , num = 10 - , toPath = props.toBuildPath - } - , Components.Build.view shared - { build = model.build - , showFullTimestamps = False - , actionsMenu = Html.div [] [] - , showActionsMenuBool = True - } - , Components.Tabs.viewBuildTabs shared - { org = props.org - , repo = props.repo - , buildNumber = props.buildNumber - , currentPath = route.path - , tabHistory = model.tabHistory + [ Components.Nav.view shared + route + { buttons = + [ viewRestartButton + , viewCancelButton + , viewApproveButton + ] + ++ props.navButtons + , crumbs = Components.Crumbs.view route.path props.crumbs } + , main_ [ class "content-wrap" ] + ([ Components.Util.view shared route props.utilButtons + , Components.RecentBuilds.view shared + { builds = shared.builds + , build = model.build + , num = 10 + , toPath = props.toBuildPath + } + , Components.Build.view shared + { build = model.build + , showFullTimestamps = False + , actionsMenu = Html.div [] [] + } + , Components.Tabs.viewBuildTabs shared + { org = props.org + , repo = props.repo + , buildNumber = props.buildNumber + , currentPath = route.path + , tabHistory = model.tabHistory + } + ] + ++ content.body + ) ] - ++ content.body } diff --git a/src/elm/Layouts/Default/Org.elm b/src/elm/Layouts/Default/Org.elm index e58c9dbec..d55c267a6 100644 --- a/src/elm/Layouts/Default/Org.elm +++ b/src/elm/Layouts/Default/Org.elm @@ -7,11 +7,13 @@ module Layouts.Default.Org exposing (Model, Msg, Props, layout, map) import Components.Crumbs import Components.Help +import Components.Nav import Components.Tabs import Components.Util import Dict exposing (Dict) import Effect exposing (Effect) -import Html exposing (..) +import Html exposing (Html, main_) +import Html.Attributes exposing (class) import Layout exposing (Layout) import Layouts.Default import Route exposing (Route) @@ -40,7 +42,7 @@ map fn props = } -layout : Props contentMsg -> Shared.Model -> Route () -> Layout (Layouts.Default.Props contentMsg) Model Msg contentMsg +layout : Props contentMsg -> Shared.Model -> Route () -> Layout Layouts.Default.Props Model Msg contentMsg layout props shared route = Layout.new { init = init shared route @@ -50,11 +52,7 @@ layout props shared route = } |> Layout.withOnUrlChanged OnUrlChanged |> Layout.withParentProps - { navButtons = props.navButtons - , utilButtons = [] - , helpCommands = props.helpCommands - , crumbs = props.crumbs - , repo = Nothing + { helpCommands = props.helpCommands } @@ -85,6 +83,7 @@ type Msg update : Shared.Model -> Route () -> Msg -> Model -> ( Model, Effect Msg ) update shared route msg model = case msg of + -- BROWSER OnUrlChanged options -> ( { model | tabHistory = @@ -107,15 +106,24 @@ view : Props contentMsg -> Shared.Model -> Route () -> { toContentMsg : Msg -> c view props shared route { toContentMsg, model, content } = { title = props.org ++ " " ++ content.title , body = - Components.Util.view shared + [ Components.Nav.view shared route - (Components.Tabs.viewOrgTabs + { buttons = props.navButtons + , crumbs = Components.Crumbs.view route.path props.crumbs + } + , main_ [ class "content-wrap" ] + (Components.Util.view shared - { org = props.org - , currentPath = route.path - , tabHistory = model.tabHistory - } - :: props.utilButtons + route + (Components.Tabs.viewOrgTabs + shared + { org = props.org + , currentPath = route.path + , tabHistory = model.tabHistory + } + :: props.utilButtons + ) + :: content.body ) - :: content.body + ] } diff --git a/src/elm/Layouts/Default/Repo.elm b/src/elm/Layouts/Default/Repo.elm index 6049f147d..01a6eb979 100644 --- a/src/elm/Layouts/Default/Repo.elm +++ b/src/elm/Layouts/Default/Repo.elm @@ -6,12 +6,15 @@ SPDX-License-Identifier: Apache-2.0 module Layouts.Default.Repo exposing (Model, Msg, Props, layout, map) import Components.Crumbs +import Components.Favorites import Components.Help +import Components.Nav import Components.Tabs import Components.Util import Dict exposing (Dict) import Effect exposing (Effect) -import Html exposing (..) +import Html exposing (Html, main_) +import Html.Attributes exposing (class) import Layout exposing (Layout) import Layouts.Default import Route exposing (Route) @@ -19,6 +22,7 @@ import Route.Path import Shared import Time import Url exposing (Url) +import Utils.Favorites as Favorites import Utils.Interval as Interval import View exposing (View) @@ -44,7 +48,7 @@ map fn props = } -layout : Props contentMsg -> Shared.Model -> Route () -> Layout (Layouts.Default.Props contentMsg) Model Msg contentMsg +layout : Props contentMsg -> Shared.Model -> Route () -> Layout Layouts.Default.Props Model Msg contentMsg layout props shared route = Layout.new { init = init props shared route @@ -54,11 +58,7 @@ layout props shared route = } |> Layout.withOnUrlChanged OnUrlChanged |> Layout.withParentProps - { navButtons = props.navButtons - , utilButtons = [] - , helpCommands = props.helpCommands - , crumbs = props.crumbs - , repo = Just ( props.org, props.repo ) + { helpCommands = props.helpCommands } @@ -99,8 +99,10 @@ init props shared route _ = type Msg - = --BROWSER + = -- BROWSER OnUrlChanged { from : Route (), to : Route () } + -- FAVORITES + | ToggleFavorite String (Maybe String) -- REFRESH | Tick { time : Time.Posix, interval : Interval.Interval } @@ -108,6 +110,7 @@ type Msg update : Props contentMsg -> Route () -> Msg -> Model -> ( Model, Effect Msg ) update props route msg model = case msg of + -- BROWSER OnUrlChanged options -> ( { model | tabHistory = @@ -116,6 +119,17 @@ update props route msg model = , Effect.replaceRouteRemoveTabHistorySkipDomFocus route ) + -- FAVORITES + ToggleFavorite org maybeRepo -> + ( model + , Effect.updateFavorites + { org = org + , maybeRepo = maybeRepo + , updateType = Favorites.Toggle + } + ) + + -- REFRESH Tick options -> ( model , Effect.batch @@ -151,16 +165,33 @@ view : Props contentMsg -> Shared.Model -> Route () -> { toContentMsg : Msg -> c view props shared route { toContentMsg, model, content } = { title = props.org ++ "/" ++ props.repo ++ " " ++ content.title , body = - Components.Util.view shared + [ Components.Nav.view shared route - (Components.Tabs.viewRepoTabs - shared - { org = props.org - , repo = props.repo - , currentPath = route.path - , tabHistory = model.tabHistory - } - :: props.utilButtons + { buttons = + (Components.Favorites.viewStarToggle + { org = props.org + , repo = props.repo + , user = shared.user + , msg = ToggleFavorite + } + |> Html.map toContentMsg + ) + :: props.navButtons + , crumbs = Components.Crumbs.view route.path props.crumbs + } + , main_ [ class "content-wrap" ] + (Components.Util.view shared + route + (Components.Tabs.viewRepoTabs + shared + { org = props.org + , repo = props.repo + , currentPath = route.path + , tabHistory = model.tabHistory + } + :: props.utilButtons + ) + :: content.body ) - :: content.body + ] } diff --git a/src/elm/Main.elm b/src/elm/Main.elm index cd6b866e9..8b78f8602 100644 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -35,13 +35,13 @@ import Pages.NotFound_ import Pages.Org_ import Pages.Org_.Builds import Pages.Org_.Repo_ -import Pages.Org_.Repo_.Audit import Pages.Org_.Repo_.Build_ import Pages.Org_.Repo_.Build_.Graph import Pages.Org_.Repo_.Build_.Pipeline import Pages.Org_.Repo_.Build_.Services import Pages.Org_.Repo_.Deployments import Pages.Org_.Repo_.Deployments.Add +import Pages.Org_.Repo_.Hooks import Pages.Org_.Repo_.Schedules import Pages.Org_.Repo_.Schedules.Add import Pages.Org_.Repo_.Schedules.Edit_ @@ -522,6 +522,30 @@ initPageAndLayout model = } ) + Route.Path.Org_Repo_Tags params -> + runWhenAuthenticatedWithLayout + model + (\user -> + let + page : Page.Page Pages.Org_.Repo_.Model Pages.Org_.Repo_.Msg + page = + Pages.Org_.Repo_.page user model.shared (Route.fromUrl params model.url) + + ( pageModel, pageEffect ) = + Page.init page () + in + { page = + Tuple.mapBoth + (Main.Pages.Model.Org_Repo_ params) + (Effect.map Main.Pages.Msg.Org_Repo_ >> fromPageEffect model) + ( pageModel, pageEffect ) + , layout = + Page.layout pageModel page + |> Maybe.map (Layouts.map (Main.Pages.Msg.Org_Repo_ >> Page)) + |> Maybe.map (initLayout model) + } + ) + Route.Path.Org_Repo_Deployments params -> runWhenAuthenticatedWithLayout model @@ -642,26 +666,26 @@ initPageAndLayout model = } ) - Route.Path.Org_Repo_Audit params -> + Route.Path.Org_Repo_Hooks params -> runWhenAuthenticatedWithLayout model (\user -> let - page : Page.Page Pages.Org_.Repo_.Audit.Model Pages.Org_.Repo_.Audit.Msg + page : Page.Page Pages.Org_.Repo_.Hooks.Model Pages.Org_.Repo_.Hooks.Msg page = - Pages.Org_.Repo_.Audit.page user model.shared (Route.fromUrl params model.url) + Pages.Org_.Repo_.Hooks.page user model.shared (Route.fromUrl params model.url) ( pageModel, pageEffect ) = Page.init page () in { page = Tuple.mapBoth - (Main.Pages.Model.Org_Repo_Audit params) - (Effect.map Main.Pages.Msg.Org_Repo_Audit >> fromPageEffect model) + (Main.Pages.Model.Org_Repo_Hooks params) + (Effect.map Main.Pages.Msg.Org_Repo_Hooks >> fromPageEffect model) ( pageModel, pageEffect ) , layout = Page.layout pageModel page - |> Maybe.map (Layouts.map (Main.Pages.Msg.Org_Repo_Audit >> Page)) + |> Maybe.map (Layouts.map (Main.Pages.Msg.Org_Repo_Hooks >> Page)) |> Maybe.map (initLayout model) } ) @@ -1360,14 +1384,14 @@ updateFromPage msg model = (Page.update (Pages.Org_.Repo_.Schedules.Edit_.page user model.shared (Route.fromUrl params model.url)) pageMsg pageModel) ) - ( Main.Pages.Msg.Org_Repo_Audit pageMsg, Main.Pages.Model.Org_Repo_Audit params pageModel ) -> + ( Main.Pages.Msg.Org_Repo_Hooks pageMsg, Main.Pages.Model.Org_Repo_Hooks params pageModel ) -> runWhenAuthenticated model (\user -> Tuple.mapBoth - (Main.Pages.Model.Org_Repo_Audit params) - (Effect.map Main.Pages.Msg.Org_Repo_Audit >> fromPageEffect model) - (Page.update (Pages.Org_.Repo_.Audit.page user model.shared (Route.fromUrl params model.url)) pageMsg pageModel) + (Main.Pages.Model.Org_Repo_Hooks params) + (Effect.map Main.Pages.Msg.Org_Repo_Hooks >> fromPageEffect model) + (Page.update (Pages.Org_.Repo_.Hooks.page user model.shared (Route.fromUrl params model.url)) pageMsg pageModel) ) ( Main.Pages.Msg.Org_Repo_Settings pageMsg, Main.Pages.Model.Org_Repo_Settings params pageModel ) -> @@ -1669,11 +1693,11 @@ toLayoutFromPage model = |> Maybe.andThen (Page.layout pageModel) |> Maybe.map (Layouts.map (Main.Pages.Msg.Org_Repo_SchedulesEdit_ >> Page)) - Main.Pages.Model.Org_Repo_Audit params pageModel -> + Main.Pages.Model.Org_Repo_Hooks params pageModel -> Route.fromUrl params model.url - |> toAuthProtectedPage model Pages.Org_.Repo_.Audit.page + |> toAuthProtectedPage model Pages.Org_.Repo_.Hooks.page |> Maybe.andThen (Page.layout pageModel) - |> Maybe.map (Layouts.map (Main.Pages.Msg.Org_Repo_Audit >> Page)) + |> Maybe.map (Layouts.map (Main.Pages.Msg.Org_Repo_Hooks >> Page)) Main.Pages.Model.Org_Repo_Settings params pageModel -> Route.fromUrl params model.url @@ -1896,11 +1920,11 @@ subscriptions model = ) (Auth.onPageLoad model.shared (Route.fromUrl () model.url)) - Main.Pages.Model.Org_Repo_Audit params pageModel -> + Main.Pages.Model.Org_Repo_Hooks params pageModel -> Auth.Action.subscriptions (\user -> - Page.subscriptions (Pages.Org_.Repo_.Audit.page user model.shared (Route.fromUrl params model.url)) pageModel - |> Sub.map Main.Pages.Msg.Org_Repo_Audit + Page.subscriptions (Pages.Org_.Repo_.Hooks.page user model.shared (Route.fromUrl params model.url)) pageModel + |> Sub.map Main.Pages.Msg.Org_Repo_Hooks |> Sub.map Page ) (Auth.onPageLoad model.shared (Route.fromUrl () model.url)) @@ -2316,11 +2340,11 @@ viewPage model = ) (Auth.onPageLoad model.shared (Route.fromUrl () model.url)) - Main.Pages.Model.Org_Repo_Audit params pageModel -> + Main.Pages.Model.Org_Repo_Hooks params pageModel -> Auth.Action.view (\user -> - Page.view (Pages.Org_.Repo_.Audit.page user model.shared (Route.fromUrl params model.url)) pageModel - |> View.map Main.Pages.Msg.Org_Repo_Audit + Page.view (Pages.Org_.Repo_.Hooks.page user model.shared (Route.fromUrl params model.url)) pageModel + |> View.map Main.Pages.Msg.Org_Repo_Hooks |> View.map Page ) (Auth.onPageLoad model.shared (Route.fromUrl () model.url)) @@ -2636,11 +2660,11 @@ toPageUrlHookCmd model routes = ) (Auth.onPageLoad model.shared (Route.fromUrl () model.url)) - Main.Pages.Model.Org_Repo_Audit params pageModel -> + Main.Pages.Model.Org_Repo_Hooks params pageModel -> Auth.Action.command (\user -> - Page.toUrlMessages routes (Pages.Org_.Repo_.Audit.page user model.shared (Route.fromUrl params model.url)) - |> List.map Main.Pages.Msg.Org_Repo_Audit + Page.toUrlMessages routes (Pages.Org_.Repo_.Hooks.page user model.shared (Route.fromUrl params model.url)) + |> List.map Main.Pages.Msg.Org_Repo_Hooks |> List.map Page |> toCommands ) @@ -2950,6 +2974,9 @@ isAuthProtected routePath = Route.Path.Org_Repo_Pulls _ -> True + Route.Path.Org_Repo_Tags _ -> + True + Route.Path.Org_Repo_Deployments _ -> True @@ -2965,7 +2992,7 @@ isAuthProtected routePath = Route.Path.Org_Repo_SchedulesEdit_ _ -> True - Route.Path.Org_Repo_Audit _ -> + Route.Path.Org_Repo_Hooks _ -> True Route.Path.Org_Repo_Settings _ -> diff --git a/src/elm/Main/Pages/Model.elm b/src/elm/Main/Pages/Model.elm index 4da551063..c989203d9 100644 --- a/src/elm/Main/Pages/Model.elm +++ b/src/elm/Main/Pages/Model.elm @@ -13,13 +13,13 @@ import Pages.NotFound_ import Pages.Org_ import Pages.Org_.Builds import Pages.Org_.Repo_ -import Pages.Org_.Repo_.Audit import Pages.Org_.Repo_.Build_ import Pages.Org_.Repo_.Build_.Graph import Pages.Org_.Repo_.Build_.Pipeline import Pages.Org_.Repo_.Build_.Services import Pages.Org_.Repo_.Deployments import Pages.Org_.Repo_.Deployments.Add +import Pages.Org_.Repo_.Hooks import Pages.Org_.Repo_.Schedules import Pages.Org_.Repo_.Schedules.Add import Pages.Org_.Repo_.Schedules.Edit_ @@ -48,7 +48,7 @@ type Model | Org_Repo_Schedules { org : String, repo : String } Pages.Org_.Repo_.Schedules.Model | Org_Repo_SchedulesAdd { org : String, repo : String } Pages.Org_.Repo_.Schedules.Add.Model | Org_Repo_SchedulesEdit_ { org : String, repo : String, name : String } Pages.Org_.Repo_.Schedules.Edit_.Model - | Org_Repo_Audit { org : String, repo : String } Pages.Org_.Repo_.Audit.Model + | Org_Repo_Hooks { org : String, repo : String } Pages.Org_.Repo_.Hooks.Model | Org_Repo_Settings { org : String, repo : String } Pages.Org_.Repo_.Settings.Model | Org_Repo_Build_ { org : String, repo : String, buildNumber : String } Pages.Org_.Repo_.Build_.Model | Org_Repo_Build_Services { org : String, repo : String, buildNumber : String } Pages.Org_.Repo_.Build_.Services.Model diff --git a/src/elm/Main/Pages/Msg.elm b/src/elm/Main/Pages/Msg.elm index 4ad904729..23ea4df78 100644 --- a/src/elm/Main/Pages/Msg.elm +++ b/src/elm/Main/Pages/Msg.elm @@ -13,13 +13,13 @@ import Pages.NotFound_ import Pages.Org_ import Pages.Org_.Builds import Pages.Org_.Repo_ -import Pages.Org_.Repo_.Audit import Pages.Org_.Repo_.Build_ import Pages.Org_.Repo_.Build_.Graph import Pages.Org_.Repo_.Build_.Pipeline import Pages.Org_.Repo_.Build_.Services import Pages.Org_.Repo_.Deployments import Pages.Org_.Repo_.Deployments.Add +import Pages.Org_.Repo_.Hooks import Pages.Org_.Repo_.Schedules import Pages.Org_.Repo_.Schedules.Add import Pages.Org_.Repo_.Schedules.Edit_ @@ -48,7 +48,7 @@ type Msg | Org_Repo_Schedules Pages.Org_.Repo_.Schedules.Msg | Org_Repo_SchedulesAdd Pages.Org_.Repo_.Schedules.Add.Msg | Org_Repo_SchedulesEdit_ Pages.Org_.Repo_.Schedules.Edit_.Msg - | Org_Repo_Audit Pages.Org_.Repo_.Audit.Msg + | Org_Repo_Hooks Pages.Org_.Repo_.Hooks.Msg | Org_Repo_Settings Pages.Org_.Repo_.Settings.Msg | Org_Repo_Build_ Pages.Org_.Repo_.Build_.Msg | Org_Repo_Build_Services Pages.Org_.Repo_.Build_.Services.Msg diff --git a/src/elm/Pages/Account/Login.elm b/src/elm/Pages/Account/Login.elm index 58dffac9d..d31f9c05f 100644 --- a/src/elm/Pages/Account/Login.elm +++ b/src/elm/Pages/Account/Login.elm @@ -7,6 +7,8 @@ module Pages.Account.Login exposing (..) import Api.Endpoint import Browser.Navigation +import Components.Crumbs +import Components.Nav import Effect exposing (Effect) import FeatherIcons import Html @@ -15,6 +17,7 @@ import Html , button , div , h1 + , main_ , p , text ) @@ -38,7 +41,7 @@ page shared route = { init = init , update = update shared , subscriptions = subscriptions - , view = view shared + , view = view shared route } |> Page.withLayout toLayout @@ -50,14 +53,7 @@ page shared route = toLayout : Model -> Layouts.Layout Msg toLayout model = Layouts.Default - { navButtons = [] - , helpCommands = [] - , utilButtons = [] - , crumbs = - [ ( "Account", Nothing ) - , ( "Login", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -114,21 +110,31 @@ subscriptions model = -- VIEW -view : Shared.Model -> Model -> View Msg -view shared model = +view : Shared.Model -> Route () -> Model -> View Msg +view shared route model = let - body = - div [ Util.testAttribute "login_" ] + crumbs = + [ ( "Account", Nothing ) + , ( "Login", Nothing ) + ] + in + { title = "Login" + , body = + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ Util.testAttribute "login_" ] [ if String.length shared.velaRedirect /= 0 then viewLogin else text "" ] - in - { title = "Login" - , body = - [ body + ] ] } diff --git a/src/elm/Pages/Account/Settings.elm b/src/elm/Pages/Account/Settings.elm index 3eeb457c7..3874333be 100644 --- a/src/elm/Pages/Account/Settings.elm +++ b/src/elm/Pages/Account/Settings.elm @@ -7,6 +7,8 @@ module Pages.Account.Settings exposing (..) import Auth import Auth.Session exposing (Session(..)) +import Components.Crumbs +import Components.Nav import DateFormat.Relative exposing (relativeTime) import Effect exposing (Effect) import FeatherIcons @@ -19,6 +21,7 @@ import Html , em , h2 , label + , main_ , p , section , text @@ -51,7 +54,7 @@ page user shared route = { init = init , update = update , subscriptions = subscriptions - , view = view shared + , view = view shared route } |> Page.withLayout (toLayout user) @@ -63,14 +66,7 @@ page user shared route = toLayout : Auth.User -> Model -> Layouts.Layout Msg toLayout user model = Layouts.Default - { navButtons = [] - , helpCommands = [] - , utilButtons = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( "My Settings", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -110,7 +106,7 @@ update msg model = -- ALERTS AddAlertCopiedToClipboard contentCopied -> ( model - , Effect.addAlertSuccess { content = contentCopied, addToastIfUnique = False } + , Effect.addAlertSuccess { content = "'" ++ contentCopied ++ "' copied to clipboard.", addToastIfUnique = False } ) @@ -127,17 +123,27 @@ subscriptions model = -- VIEW -view : Shared.Model -> Model -> View Msg -view shared model = +view : Shared.Model -> Route () -> Model -> View Msg +view shared route model = let - body = - div [ Util.testAttribute "user-settings" ] - [ viewAccountSettings shared model - ] + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( "My Settings", Nothing ) + ] in { title = "My Settings" , body = - [ body + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ Util.testAttribute "user-settings" ] + [ viewAccountSettings shared model + ] + ] ] } diff --git a/src/elm/Pages/Account/SourceRepos.elm b/src/elm/Pages/Account/SourceRepos.elm index d9a9616a8..c7ea6f7f4 100644 --- a/src/elm/Pages/Account/SourceRepos.elm +++ b/src/elm/Pages/Account/SourceRepos.elm @@ -8,7 +8,9 @@ module Pages.Account.SourceRepos exposing (..) import Api.Api as Api import Api.Operations import Auth +import Components.Crumbs import Components.Favorites +import Components.Nav import Components.Search import Dict exposing (Dict) import Effect exposing (Effect) @@ -20,6 +22,7 @@ import Html , button , details , div + , main_ , span , summary , text @@ -54,7 +57,7 @@ page user shared route = { init = init , update = update shared , subscriptions = subscriptions - , view = view shared + , view = view shared route } |> Page.withLayout (toLayout user shared) @@ -66,32 +69,7 @@ page user shared route = toLayout : Auth.User -> Shared.Model -> Model -> Layouts.Layout Msg toLayout user shared model = Layouts.Default - { navButtons = - [ button - [ classList - [ ( "button", True ) - , ( "-outline", True ) - ] - , onClick (GetUserSourceRepos True) - , disabled (model.sourceRepos == RemoteData.Loading) - , Util.testAttribute "refresh-source-repos" - ] - [ case model.sourceRepos of - RemoteData.Loading -> - text "Loadingâ€Ļ" - - _ -> - text "Refresh List" - ] - ] - , helpCommands = [] - , utilButtons = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( "Account", Nothing ) - , ( "Source Repositories", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -263,18 +241,46 @@ subscriptions model = -- VIEW -view : Shared.Model -> Model -> View Msg -view shared model = +view : Shared.Model -> Route () -> Model -> View Msg +view shared route model = let - body = - div [ Util.testAttribute "source-repos" ] - [ Components.Search.viewRepoSearchBarGlobal model.searchFilters UpdateSearchFilter - , viewSourceRepos shared model - ] + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( "Account", Nothing ) + , ( "Source Repositories", Nothing ) + ] in { title = "Source Repos" , body = - [ body + [ Components.Nav.view + shared + route + { buttons = + [ button + [ classList + [ ( "button", True ) + , ( "-outline", True ) + ] + , onClick (GetUserSourceRepos True) + , disabled (model.sourceRepos == RemoteData.Loading) + , Util.testAttribute "refresh-source-repos" + ] + [ case model.sourceRepos of + RemoteData.Loading -> + text "Loadingâ€Ļ" + + _ -> + text "Refresh List" + ] + ] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ Util.testAttribute "source-repos" ] + [ Components.Search.viewRepoSearchBarGlobal model.searchFilters UpdateSearchFilter + , viewSourceRepos shared model + ] + ] ] } diff --git a/src/elm/Pages/Home.elm b/src/elm/Pages/Home.elm index 2844988fb..473d4df1d 100644 --- a/src/elm/Pages/Home.elm +++ b/src/elm/Pages/Home.elm @@ -6,13 +6,15 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Home exposing (Model, Msg, page, view) import Auth +import Components.Crumbs +import Components.Nav import Components.Repo import Components.Search exposing ( toLowerContains , viewHomeSearchBar ) -import Components.Svgs as SvgBuilder +import Components.Svgs import Dict exposing (Dict) import Effect exposing (Effect) import FeatherIcons @@ -23,6 +25,7 @@ import Html , details , div , h1 + , main_ , p , summary , text @@ -54,7 +57,7 @@ page user shared route = { init = init shared , update = update , subscriptions = subscriptions - , view = view shared + , view = view shared route } |> Page.withLayout (toLayout user) @@ -66,19 +69,7 @@ page user shared route = toLayout : Auth.User -> Model -> Layouts.Layout Msg toLayout user model = Layouts.Default - { navButtons = - [ a - [ class "button" - , class "-outline" - , Util.testAttribute "source-repos" - , Route.Path.href Route.Path.AccountSourceRepos - ] - [ text "Source Repositories" ] - ] - , utilButtons = [] - , helpCommands = [] - , crumbs = [ ( "Overview", Nothing ) ] - , repo = Nothing + { helpCommands = [] } @@ -151,35 +142,55 @@ subscriptions model = -- VIEW -view : Shared.Model -> Model -> View Msg -view shared model = +view : Shared.Model -> Route () -> Model -> View Msg +view shared route model = + let + crumbs = + [ ( "Overview", Nothing ) ] + in { title = "Home" , body = - [ div [ Util.testAttribute "overview" ] <| - case shared.user of - RemoteData.Success u -> - if List.length u.favorites > 0 then - [ viewHomeSearchBar model.favoritesFilter SearchFavorites - , viewFavorites shared u.favorites model.favoritesFilter - ] + [ Components.Nav.view + shared + route + { buttons = + [ a + [ class "button" + , class "-outline" + , Util.testAttribute "source-repos" + , Route.Path.href Route.Path.AccountSourceRepos + ] + [ text "Source Repositories" ] + ] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ Util.testAttribute "overview" ] <| + case shared.user of + RemoteData.Success u -> + if List.length u.favorites > 0 then + [ viewHomeSearchBar model.favoritesFilter SearchFavorites + , viewFavorites shared u.favorites model.favoritesFilter + ] - else - [ div [ class "overview" ] - [ h1 [] [ text "Let's get Started!" ] - , p [] [ text "To have Vela start building your projects we need to get them enabled." ] - , p [] - [ text "To display a repository here, click the " - , SvgBuilder.star False + else + [ div [ class "overview" ] + [ h1 [] [ text "Let's get Started!" ] + , p [] [ text "To have Vela start building your projects we need to get them enabled." ] + , p [] + [ text "To display a repository here, click the " + , Components.Svgs.star False + ] + , p [] [ text "Enable repositories from your GitHub account on Vela now!" ] + , a [ class "button", Route.Path.href Route.Path.AccountSourceRepos ] [ text "Source Repositories" ] ] - , p [] [ text "Enable repositories from your GitHub account on Vela now!" ] - , a [ class "button", Route.Path.href Route.Path.AccountSourceRepos ] [ text "Source Repositories" ] ] - ] - _ -> - [ viewHomeSearchBar model.favoritesFilter SearchFavorites - , viewFavorites shared [] model.favoritesFilter - ] + _ -> + [ viewHomeSearchBar model.favoritesFilter SearchFavorites + , viewFavorites shared [] model.favoritesFilter + ] + ] ] } diff --git a/src/elm/Pages/NotFound_.elm b/src/elm/Pages/NotFound_.elm index b59d489c6..37d1bf95c 100644 --- a/src/elm/Pages/NotFound_.elm +++ b/src/elm/Pages/NotFound_.elm @@ -5,8 +5,11 @@ SPDX-License-Identifier: Apache-2.0 module Pages.NotFound_ exposing (Model, Msg, page) +import Components.Crumbs +import Components.Nav import Effect exposing (Effect) -import Html exposing (..) +import Html exposing (main_, text) +import Html.Attributes exposing (class) import Layouts import Page exposing (Page) import Route exposing (Route) @@ -21,7 +24,7 @@ page shared route = { init = init , update = update , subscriptions = subscriptions - , view = view shared + , view = view shared route } |> Page.withLayout toLayout @@ -33,11 +36,7 @@ page shared route = toLayout : Model -> Layouts.Layout Msg toLayout model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = [ ( "Overview", Just Route.Path.Home ), ( "Not Found", Nothing ) ] - , repo = Nothing + { helpCommands = [] } @@ -86,10 +85,22 @@ subscriptions model = -- VIEW -view : Shared.Model -> Model -> View Msg -view shared model = +view : Shared.Model -> Route () -> Model -> View Msg +view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ), ( "Not Found", Nothing ) ] + in { title = "Not Found" , body = - [ text "page not found" + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ text "page not found" + ] ] } diff --git a/src/elm/Pages/Org_.elm b/src/elm/Pages/Org_.elm index a9b0757e2..ce6495b82 100644 --- a/src/elm/Pages/Org_.elm +++ b/src/elm/Pages/Org_.elm @@ -175,7 +175,7 @@ subscriptions model = view : Shared.Model -> Route { org : String } -> Model -> View Msg view shared route model = - { title = "Repos" + { title = "Repos" ++ Util.pageToString (Dict.get "page" route.query) , body = [ Html.caption [ class "builds-caption" @@ -199,7 +199,19 @@ view shared route model = ] else - div [] (List.map (\repository -> viewRepo shared (RemoteData.unwrap [] .favorites shared.user) False repository.org repository.name) repos) + div [] <| + List.map + (\repository -> + Components.Repo.view + shared + { toggleFavoriteMsg = ToggleFavorite + , org = repository.org + , repo = repository.name + , favorites = RemoteData.unwrap [] .favorites shared.user + , filtered = False + } + ) + repos RemoteData.Loading -> Components.Loading.viewSmallLoader @@ -216,17 +228,3 @@ view shared route model = , Components.Pager.view model.pager Components.Pager.defaultLabels GotoPage ] } - - -{-| viewRepo : renders row of repos with action buttons --} -viewRepo : Shared.Model -> List String -> Bool -> String -> String -> Html Msg -viewRepo shared favorites filtered org repo = - Components.Repo.view - shared - { toggleFavoriteMsg = ToggleFavorite - , org = org - , repo = repo - , favorites = favorites - , filtered = False - } diff --git a/src/elm/Pages/Org_/Builds.elm b/src/elm/Pages/Org_/Builds.elm index 2ff0d0418..fedb5c568 100644 --- a/src/elm/Pages/Org_/Builds.elm +++ b/src/elm/Pages/Org_/Builds.elm @@ -104,9 +104,12 @@ type Msg -- BUILDS | GetOrgBuildsResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Build )) | GotoPage Int - | ApproveBuild Vela.Org Vela.Repo Vela.BuildNumber | RestartBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | RestartBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) | CancelBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | CancelBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) + | ApproveBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | ApproveBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) | ShowHideActionsMenus (Maybe Int) (Maybe Bool) | FilterByEvent (Maybe String) | ShowHideFullTimestamps @@ -168,27 +171,98 @@ update shared route msg model = ] ) - ApproveBuild _ _ _ -> - ( model, Effect.none ) - RestartBuild options -> ( model , Effect.restartBuild - { org = options.org + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = RestartBuildResponse options + , org = options.org , repo = options.repo , buildNumber = options.buildNumber } ) + RestartBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + restartedBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + + newBuildNumber = + String.fromInt <| build.number + + newBuild = + String.join "/" [ "", options.org, options.repo, newBuildNumber ] + + -- todo: create new build link, add to toastie, refresh builds + in + ( model + , Effect.batch + [ Effect.getOrgBuilds + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetOrgBuildsResponse + , pageNumber = Dict.get "page" route.query |> Maybe.andThen String.toInt + , perPage = Dict.get "perPage" route.query |> Maybe.andThen String.toInt + , maybeEvent = Dict.get "event" route.query + , org = route.params.org + } + , Effect.addAlertSuccess { content = restartedBuild ++ " restarted.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + CancelBuild options -> ( model , Effect.cancelBuild - { org = options.org + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = CancelBuildResponse options + , org = options.org , repo = options.repo , buildNumber = options.buildNumber } ) + CancelBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + canceledBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + in + ( model + , Effect.batch + [ Effect.getOrgBuilds + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetOrgBuildsResponse + , pageNumber = Dict.get "page" route.query |> Maybe.andThen String.toInt + , perPage = Dict.get "perPage" route.query |> Maybe.andThen String.toInt + , maybeEvent = Dict.get "event" route.query + , org = route.params.org + } + , Effect.addAlertSuccess { content = canceledBuild ++ " canceled.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + + ApproveBuild options -> + ( model, Effect.none ) + + ApproveBuildResponse options response -> + ( model, Effect.none ) + ShowHideActionsMenus build show -> let buildsOpen = @@ -287,7 +361,7 @@ view shared route model = , showHideActionsMenus = ShowHideActionsMenus } in - { title = "Builds" + { title = "Builds" ++ Util.pageToString (Dict.get "page" route.query) , body = [ caption [ class "builds-caption" @@ -316,7 +390,6 @@ view shared route model = } , build = options.build , showActionsMenus = model.showActionsMenus - , showActionsMenuBool = True } } , Components.Pager.viewIfNeeded model.pager Components.Pager.defaultLabels GotoPage model.builds diff --git a/src/elm/Pages/Org_/Repo_.elm b/src/elm/Pages/Org_/Repo_.elm index 4af84437b..4588a7eb7 100644 --- a/src/elm/Pages/Org_/Repo_.elm +++ b/src/elm/Pages/Org_/Repo_.elm @@ -54,7 +54,12 @@ toLayout user route model = Layouts.Default_Repo { navButtons = [] , utilButtons = [] - , helpCommands = [] + , helpCommands = + [ { name = "List Builds" + , content = "vela get builds --help" + , docs = Just "builds/get" + } + ] , crumbs = [ ( "Overview", Just Route.Path.Home ) , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) @@ -107,9 +112,12 @@ type Msg -- BUILDS | GetRepoBuildsResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Build )) | GotoPage Int - | ApproveBuild Vela.Org Vela.Repo Vela.BuildNumber | RestartBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | RestartBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) | CancelBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | CancelBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) + | ApproveBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } + | ApproveBuildResponse { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) | ShowHideActionsMenus (Maybe Int) (Maybe Bool) | FilterByEvent (Maybe String) | ShowHideFullTimestamps @@ -173,39 +181,100 @@ update shared route msg model = ] ) - ApproveBuild _ _ _ -> - let - _ = - Debug.log "approve build clicked" "" - - -- todo: - -- 1. write func in Effect.elm for "approveBuild" - -- 2. write Api.Operations.approveBuild that uses it - -- look at the code in Api.Operations and the other funcs in Api.Operations for inspiration - -- 3. write ApproveBuildResponse Msg in this file - -- 4. in ApproveBuildResponse, create a toasty? - -- look at how it's done in Main.elm - in - ( model, Effect.none ) - RestartBuild options -> ( model , Effect.restartBuild - { org = options.org + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = RestartBuildResponse options + , org = options.org , repo = options.repo , buildNumber = options.buildNumber } ) + RestartBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + restartedBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + + newBuildNumber = + String.fromInt <| build.number + + newBuild = + String.join "/" [ "", options.org, options.repo, newBuildNumber ] + + -- todo: create new build link, add to toastie, refresh builds + in + ( model + , Effect.batch + [ Effect.getRepoBuilds + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetRepoBuildsResponse + , pageNumber = Dict.get "page" route.query |> Maybe.andThen String.toInt + , perPage = Dict.get "perPage" route.query |> Maybe.andThen String.toInt + , maybeEvent = Dict.get "event" route.query + , org = route.params.org + , repo = route.params.repo + } + , Effect.addAlertSuccess { content = restartedBuild ++ " restarted.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + CancelBuild options -> ( model , Effect.cancelBuild - { org = options.org + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = CancelBuildResponse options + , org = options.org , repo = options.repo , buildNumber = options.buildNumber } ) + CancelBuildResponse options response -> + case response of + Ok ( _, build ) -> + let + canceledBuild = + "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] + in + ( model + , Effect.batch + [ Effect.getRepoBuilds + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetRepoBuildsResponse + , pageNumber = Dict.get "page" route.query |> Maybe.andThen String.toInt + , perPage = Dict.get "perPage" route.query |> Maybe.andThen String.toInt + , maybeEvent = Dict.get "event" route.query + , org = route.params.org + , repo = route.params.repo + } + , Effect.addAlertSuccess { content = canceledBuild ++ " canceled.", addToastIfUnique = True } + ] + ) + + Err error -> + ( model + , Effect.handleHttpError { httpError = error } + ) + + ApproveBuild options -> + ( model, Effect.none ) + + ApproveBuildResponse options response -> + ( model, Effect.none ) + ShowHideActionsMenus build show -> let buildsOpen = @@ -306,7 +375,7 @@ view shared route model = , showHideActionsMenus = ShowHideActionsMenus } in - { title = "Builds" + { title = "Builds" ++ Util.pageToString (Dict.get "page" route.query) , body = [ caption [ class "builds-caption" @@ -335,7 +404,6 @@ view shared route model = } , build = options.build , showActionsMenus = model.showActionsMenus - , showActionsMenuBool = True } } , Components.Pager.viewIfNeeded model.pager Components.Pager.defaultLabels GotoPage model.builds diff --git a/src/elm/Pages/Org_/Repo_/Build_.elm b/src/elm/Pages/Org_/Repo_/Build_.elm index 6e402a17e..d21012ea7 100644 --- a/src/elm/Pages/Org_/Repo_/Build_.elm +++ b/src/elm/Pages/Org_/Repo_/Build_.elm @@ -5,7 +5,6 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Org_.Repo_.Build_ exposing (..) -import Api.Operations exposing (cancelBuild, restartBuild) import Auth import Browser.Dom exposing (focus) import Components.Build @@ -58,10 +57,7 @@ page user shared route = toLayout : Auth.User -> Route { org : String, repo : String, buildNumber : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default_Build - { navButtons = - [ Components.Build.viewRestartBuildButton route.params.org route.params.repo route.params.buildNumber RestartBuild - , Components.Build.viewCancelBuildButton route.params.org route.params.repo route.params.buildNumber CancelBuild - ] + { navButtons = [] , utilButtons = [] , helpCommands = [] , crumbs = @@ -137,9 +133,6 @@ type Msg | OnHashChanged { from : Maybe String, to : Maybe String } | PushUrlHash { hash : String } | FocusOn { target : String } - -- BUILD - | RestartBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } - | CancelBuild { org : Vela.Org, repo : Vela.Repo, buildNumber : Vela.BuildNumber } -- STEPS | GetBuildStepsResponse { applyDomFocus : Bool } (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Step )) | GetBuildStepsRefreshResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Step )) @@ -198,25 +191,6 @@ update shared route msg model = , Effect.focusOn options ) - -- BUILD - RestartBuild options -> - ( model - , Effect.restartBuild - { org = options.org - , repo = options.repo - , buildNumber = options.buildNumber - } - ) - - CancelBuild options -> - ( model - , Effect.cancelBuild - { org = options.org - , repo = options.repo - , buildNumber = options.buildNumber - } - ) - -- STEPS GetBuildStepsResponse options response -> case response of @@ -521,14 +495,13 @@ view shared route model = ] , div [ class "steps" ] [ div [ class "-items", Util.testAttribute "steps" ] <| - List.map (viewStep shared model route) <| - List.sortBy .number <| - RemoteData.withDefault [] model.steps - - -- if hasStages steps then - -- viewStages model msgs rm steps - -- else - -- List.map viewStep<| steps + if hasStages steps then + viewStages shared model route steps + + else + List.map (viewStep shared model route) <| + List.sortBy .number <| + RemoteData.withDefault [] model.steps ] ] @@ -541,6 +514,34 @@ view shared route model = } +viewStages : Shared.Model -> Model -> Route { org : String, repo : String, buildNumber : String } -> List Vela.Step -> List (Html Msg) +viewStages shared model route steps = + steps + |> List.map .stage + |> List.Extra.unique + |> List.map + (\stage -> + steps + |> List.filter + (\step -> + (stage == "init" && (step.stage == "init" || step.stage == "clone")) + || (stage /= "clone" && step.stage == stage) + ) + |> viewStage shared model route stage + ) + + +viewStage : Shared.Model -> Model -> Route { org : String, repo : String, buildNumber : String } -> String -> List Vela.Step -> Html Msg +viewStage shared model route stage steps = + div + [ class "stage", Util.testAttribute <| "stage" ] + [ viewStageDivider stage + , steps + |> List.map (\step -> viewStep shared model route step) + |> div [ Util.testAttribute <| "stage-" ++ stage ] + ] + + viewStep : Shared.Model -> Model -> Route { org : String, repo : String, buildNumber : String } -> Vela.Step -> Html Msg viewStep shared model route step = div @@ -591,6 +592,29 @@ viewStep shared model route step = ] +{-| viewStageDivider : renders divider between stage +-} +viewStageDivider : String -> Html msg +viewStageDivider stage = + if stage /= "init" && stage /= "clone" then + div [ class "divider", Util.testAttribute <| "stage-divider-" ++ stage ] + [ div [] [ text stage ] ] + + else + text "" + + +{-| hasStages : takes steps and returns true if the pipeline contain stages +-} +hasStages : List Vela.Step -> Bool +hasStages steps = + steps + |> List.filter (\s -> s.stage /= "") + |> List.head + |> Maybe.Extra.unwrap "" .stage + |> (\stage -> stage /= "") + + viewLogs : Shared.Model -> Model -> Route { org : String, repo : String, buildNumber : String } -> Vela.Step -> WebData Vela.Log -> Html Msg viewLogs shared model route step log = case step.status of diff --git a/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm b/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm index 7bd047b46..a5a122828 100644 --- a/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm +++ b/src/elm/Pages/Org_/Repo_/Build_/Pipeline.elm @@ -403,96 +403,158 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String, buildNumber : String } -> Model -> View Msg view shared route model = + let + viewExpandToggle = + case model.build of + RemoteData.Success build -> + div [ class "action", class "expand-pipeline", Util.testAttribute "pipeline-expand" ] + [ viewExpandToggleButton model + , if model.expanding then + Components.Loading.viewSmallLoader + + else if model.expand then + div [ class "icon" ] [ FeatherIcons.checkCircle |> FeatherIcons.withSize 20 |> FeatherIcons.toHtml [] ] + + else + div [ class "icon" ] [ FeatherIcons.circle |> FeatherIcons.withSize 20 |> FeatherIcons.toHtml [] ] + , small [ class "tip" ] [ text "note: yaml fields will be sorted alphabetically when the pipeline is expanded." ] + ] + + _ -> + text "" + + downloadButton = + case model.pipeline of + RemoteData.Success pipeline -> + div [ class "action" ] + [ button + [ class "button" + , class "-link" + , Util.testAttribute <| "download-yml" + , onClick <| + DownloadPipeline + { filename = "vela.yml" + , content = pipeline.decodedData + , map = identity + } + , attribute "aria-label" <| "download pipeline configuration file for " + ] + [ text <| + if model.expand then + "download (expanded) " ++ "vela.yml" + + else + "download " ++ "vela.yml" + ] + ] + + _ -> + text "" + in { title = "Pipeline" , body = [ div [ class "pipeline" ] [ case model.templates of RemoteData.Success templates -> if not <| Dict.isEmpty templates then - templates - |> Dict.toList - |> List.map viewTemplate - |> viewTemplatesDetails (class "-success") model.showTemplates ShowHideTemplates + viewTemplatesDetails model <| + div [ class "content", class "-success" ] <| + (templates + |> Dict.toList + |> List.map viewTemplate + ) else text "" - RemoteData.Failure _ -> - text "error" + RemoteData.Failure error -> + viewTemplatesDetails model <| + div [ class "content", class "-error" ] + [ span [ Util.testAttribute "pipeline-templates-error" ] + [ text <| + case error of + Http.BadStatus statusCode -> + case statusCode of + 401 -> + "No templates found for this pipeline, most likely due to not having access to the source control repo" + + _ -> + "No templates found for this pipeline, there was an error with the server (" ++ String.fromInt statusCode ++ ")" + + _ -> + "No templates found for this pipeline, there was an error with the server" + ] + ] _ -> - Components.Loading.viewSmallLoaderWithText "loading pipeline templates" - , case model.pipeline of - RemoteData.Success pipeline -> - if String.length pipeline.decodedData > 0 then - div [ class "logs-container", class "-pipeline" ] - [ table - [ class "logs-table" - ] - [ div [ class "header" ] - [ span [] - [ text "Pipeline Configuration" - ] - ] - , let - toggle = - case model.build of - RemoteData.Success build -> - div [ class "action", class "expand-pipeline", Util.testAttribute "pipeline-expand" ] - [ viewExpandToggleButton model - , if model.expanding then - Components.Loading.viewSmallLoader - - else if model.expand then - div [ class "icon" ] [ FeatherIcons.checkCircle |> FeatherIcons.withSize 20 |> FeatherIcons.toHtml [] ] - - else - div [ class "icon" ] [ FeatherIcons.circle |> FeatherIcons.withSize 20 |> FeatherIcons.toHtml [] ] - , small [ class "tip" ] [ text "note: yaml fields will be sorted alphabetically when the pipeline is expanded." ] - ] + viewTemplatesDetails model <| + div [ class "content", class "-success" ] [ Components.Loading.viewSmallLoaderWithText "loading pipeline templates" ] + , div [ class "logs-container", class "-pipeline" ] + [ table + [ class "logs-table" + , class "pipeline" + ] + [ div [ class "header" ] + [ span [] + [ text "Pipeline Configuration" + ] + ] + , div [ class "actions" ] + [ viewExpandToggle + , downloadButton + ] + , case model.pipeline of + RemoteData.Success pipeline -> + if String.length pipeline.decodedData > 0 then + div [ class "logs", Util.testAttribute "pipeline-configuration-data" ] <| + viewLines pipeline model.focus shared.shift + + else + div [ class "no-pipeline" ] [ small [] [ code [] [ text "The pipeline found for this build/ref contains no data." ] ] ] + + RemoteData.Failure error -> + div [ class "content", class "-error" ] + [ span [ Util.testAttribute "pipeline-configuration-error" ] + [ text <| + case error of + Http.BadStatus statusCode -> + case statusCode of + 401 -> + "No pipeline configuration (.vela.yml) found for this build/ref, most likely due to not being an admin of the source control repo" + + _ -> + "No pipeline configuration (.vela.yml) found for this build/ref, there was an error with the server (" ++ String.fromInt statusCode ++ ")" _ -> - text "" - in - div [ class "actions" ] - [ toggle - , div [ class "action" ] - [ button - [ class "button" - , class "-link" - , Util.testAttribute <| "download-yml" - , onClick <| - DownloadPipeline - { filename = "vela.yml" - , content = pipeline.decodedData - , map = identity - } - , attribute "aria-label" <| "download pipeline configuration file for " - ] - [ text <| - if model.expand then - "download (expanded) " ++ "vela.yml" - - else - "download " ++ "vela.yml" - ] - ] + "No pipeline configuration (.vela.yml) found for this build/ref, there was an error with the server" ] - , div [ class "logs", Util.testAttribute "pipeline-configuration-data" ] <| - viewLines pipeline model.focus shared.shift ] - ] - - else - div [ class "no-pipeline" ] [ small [] [ code [] [ text "No pipeline found for this build." ] ] ] - _ -> - Components.Loading.viewSmallLoader + _ -> + Components.Loading.viewSmallLoader + ] + ] ] ] } +viewTemplatesDetails : Model -> Html Msg -> Html Msg +viewTemplatesDetails model body = + details + (class "details" + :: class "templates" + :: Util.testAttribute "pipeline-templates" + :: Util.open model.showTemplates + ) + [ summary [ class "summary", Util.onClickPreventDefault ShowHideTemplates ] + [ div [] [ text "Templates" ] + , FeatherIcons.chevronDown |> FeatherIcons.withSize 20 |> FeatherIcons.withClass "details-icon-expand" |> FeatherIcons.toHtml [] + ] + , body + ] + + viewExpandToggleButton : Model -> Html Msg viewExpandToggleButton model = button @@ -525,22 +587,6 @@ viewTemplate ( _, t ) = ] -viewTemplatesDetails : Html.Attribute msg -> Bool -> msg -> List (Html msg) -> Html msg -viewTemplatesDetails cls open showHide content = - details - (class "details" - :: class "templates" - :: Util.testAttribute "pipeline-templates" - :: Util.open open - ) - [ summary [ class "summary", Util.onClickPreventDefault showHide ] - [ div [] [ text "Templates" ] - , FeatherIcons.chevronDown |> FeatherIcons.withSize 20 |> FeatherIcons.withClass "details-icon-expand" |> FeatherIcons.toHtml [] - ] - , div [ class "content", cls ] content - ] - - viewLines : Vela.PipelineConfig -> Focus.Focus -> Bool -> List (Html Msg) viewLines config focus shift = config.decodedData diff --git a/src/elm/Pages/Org_/Repo_/Deployments.elm b/src/elm/Pages/Org_/Repo_/Deployments.elm index 4878e8cd6..322a6d45a 100644 --- a/src/elm/Pages/Org_/Repo_/Deployments.elm +++ b/src/elm/Pages/Org_/Repo_/Deployments.elm @@ -78,7 +78,7 @@ toLayout user route model = ] , helpCommands = [ { name = "List Deployments" - , content = "vela view deployments --help" + , content = "vela get deployments --help" , docs = Just "deployment/get" } , { name = "Add Deployment" @@ -228,7 +228,7 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String } -> Model -> View Msg view shared route model = - { title = "Deployments" + { title = "Deployments" ++ Util.pageToString (Dict.get "page" route.query) , body = [ viewDeployments shared model route , Components.Pager.view model.pager Components.Pager.defaultLabels GotoPage @@ -261,6 +261,23 @@ viewDeployments shared model route = ] ( noRowsView, rows ) = + let + viewHttpError e = + span [ Util.testAttribute "repo-deployments-error" ] + [ text <| + case e of + Http.BadStatus statusCode -> + case statusCode of + 401 -> + "No deployments found for this repo, most likely due to not having access to the source control repo" + + _ -> + "No deployments found for this repo, there was an error with the server (" ++ String.fromInt statusCode ++ ")" + + _ -> + "No deployments found for this repo, there was an error with the server" + ] + in case ( model.repo, model.deployments ) of ( RemoteData.Success r, RemoteData.Success d ) -> ( text "No deployments found for this repo" @@ -268,10 +285,10 @@ viewDeployments shared model route = ) ( RemoteData.Failure error, _ ) -> - ( viewError error, [] ) + ( viewHttpError error, [] ) ( _, RemoteData.Failure error ) -> - ( viewError error, [] ) + ( viewHttpError error, [] ) _ -> ( Components.Loading.viewSmallLoader, [] ) @@ -305,7 +322,7 @@ deploymentsToRows shared repo deployments = -} tableHeaders : Components.Table.Columns tableHeaders = - [ ( Just "table-icon", "" ) + [ ( Just "table-icon", "status" ) , ( Nothing, "number" ) , ( Nothing, "target" ) , ( Nothing, "commit" ) @@ -314,7 +331,7 @@ tableHeaders = , ( Nothing, "builds" ) , ( Nothing, "created by" ) , ( Nothing, "created at" ) - , ( Nothing, "" ) + , ( Nothing, "redeploy" ) ] @@ -460,21 +477,3 @@ viewDeploymentBuildsLinks deployment = ) |> List.intersperse (text ", ") |> div [] - - -viewError : Http.Error -> Html msg -viewError error = - span [ Util.testAttribute "repo-deployments-error" ] - [ text <| - case error of - Http.BadStatus statusCode -> - case statusCode of - 401 -> - "No deployments found for this repo, most likely due to not having access to the source control repo" - - _ -> - "No deployments found for this repo, there was an error with the server (" ++ String.fromInt statusCode ++ ")" - - _ -> - "No deployments found for this repo, there was an error with the server" - ] diff --git a/src/elm/Pages/Org_/Repo_/Deployments/Add.elm b/src/elm/Pages/Org_/Repo_/Deployments/Add.elm index d395024a7..3f34cbe60 100644 --- a/src/elm/Pages/Org_/Repo_/Deployments/Add.elm +++ b/src/elm/Pages/Org_/Repo_/Deployments/Add.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Org_.Repo_.Deployments.Add exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Dict import Effect exposing (Effect) -import Html exposing (Html, button, code, div, em, h2, label, p, section, span, strong, text) +import Html exposing (button, code, div, em, h2, label, main_, p, section, span, strong, text) import Html.Attributes exposing (class, disabled, for, id) import Html.Events exposing (onClick) import Http @@ -46,17 +48,7 @@ page user shared route = toLayout : Auth.User -> Route { org : String, repo : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) - , ( "Deployments", Just <| Route.Path.Org_Repo_Deployments { org = route.params.org, repo = route.params.repo } ) - , ( "Add", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -273,95 +265,191 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) + , ( "Deployments", Just <| Route.Path.Org_Repo_Deployments { org = route.params.org, repo = route.params.repo } ) + , ( "Add", Nothing ) + ] + in { title = "Add Deployment" , body = - [ div [ class "manage-deployment", Util.testAttribute "manage-deployment" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase <| "add deployment" ] - , div [ class "deployment-form" ] - [ case model.repo of - RemoteData.Success repo -> - if not repo.allow_deploy then - p [ class "notice" ] - [ strong [] - [ text "Deploy webhook for this repo must be enabled in settings" + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-deployment", Util.testAttribute "manage-deployment" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase <| "add deployment" ] + , div [ class "deployment-form" ] + [ case model.repo of + RemoteData.Success repo -> + if not repo.allow_deploy then + p [ class "notice" ] + [ strong [] + [ text "Deploy webhook for this repo must be enabled in settings" + ] ] - ] - else + else + text "" + + _ -> text "" + , Components.Form.viewTextarea + { title = Just "Target" + , subtitle = Nothing + , id_ = "target" + , val = model.target + , placeholder_ = "provide the name for the target deployment environment (default: \"production\")" + , classList_ = [ ( "secret-value", True ) ] + , disabled_ = False + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = TargetOnInput + } + , Components.Form.viewTextarea + { title = Just "Ref" + , subtitle = Nothing + , id_ = "ref" + , val = model.ref + , placeholder_ = + "provide the reference to deploy - this can be a branch, commit (SHA) or tag\n(default is your repo's default branch: " + ++ RemoteData.unwrap "main" .branch model.repo + ++ ")" + , classList_ = [ ( "secret-value", True ) ] + , disabled_ = False + , rows_ = Just 3 + , wrap_ = Just "soft" + , msg = RefOnInput + } + , Components.Form.viewTextarea + { title = Just "Description" + , subtitle = Nothing + , id_ = "description" + , val = model.description + , placeholder_ = "provide the description for the deployment (default: \"Deployment request from Vela\")" + , classList_ = [ ( "secret-value", True ) ] + , disabled_ = False + , rows_ = Just 5 + , wrap_ = Just "soft" + , msg = DescriptionOnInput + } + , Components.Form.viewTextarea + { title = Just "Task" + , subtitle = Nothing + , id_ = "task" + , val = model.task + , placeholder_ = "Provide the task for the deployment (default: \"deploy:vela\")" + , classList_ = [ ( "secret-value", True ) ] + , disabled_ = False + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = TaskOnInput + } + , section [] + [ div + [ id "parameter-select" + , class "form-control" + , class "-stack" + , class "parameters-container" + ] + [ label + [ for "parameter-select" + , class "form-label" + ] + [ strong [] [ text "Add Parameters" ] + , span + [ class "field-description" ] + [ em [] [ text "(Optional)" ] + ] + ] + , div [ class "parameters-inputs" ] + [ Components.Form.viewInput + { title = Nothing + , subtitle = Nothing + , id_ = "parameter-key" + , val = model.parameterKey + , placeholder_ = "key" + , classList_ = [ ( "parameter-input", True ) ] + , disabled_ = False + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ParameterKeyOnInput + } + , Components.Form.viewInput + { title = Nothing + , subtitle = Nothing + , id_ = "parameter-value" + , val = model.parameterValue + , placeholder_ = "value" + , classList_ = [ ( "parameter-input", True ) ] + , disabled_ = False + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ParameterValueOnInput + } + , button + [ class "button" + , class "-outline" + , class "add-parameter" + , onClick <| AddParameter + , Util.testAttribute "add-parameter-button" + , disabled <| String.length model.parameterKey == 0 || String.length model.parameterValue == 0 + ] + [ text "Add" + ] + ] + ] + , div [ class "parameters", Util.testAttribute "parameters-list" ] <| + if List.length model.parameters > 0 then + let + viewParameter parameter = + div [ class "parameter", class "chevron" ] + [ button + [ class "button" + , class "-outline" + , onClick <| RemoveParameter parameter + ] + [ text "remove" + ] + , div [ class "name" ] [ text (parameter.key ++ "=" ++ parameter.value) ] + ] + in + List.map viewParameter <| List.reverse model.parameters - _ -> - text "" - , Components.Form.viewTextarea - { title = Just "Target" - , subtitle = Nothing - , id_ = "target" - , val = model.target - , placeholder_ = "provide the name for the target deployment environment (default: \"production\")" - , classList_ = [ ( "secret-value", True ) ] - , disabled_ = False - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = TargetOnInput - } - , Components.Form.viewTextarea - { title = Just "Ref" - , subtitle = Nothing - , id_ = "ref" - , val = model.ref - , placeholder_ = - "provide the reference to deploy - this can be a branch, commit (SHA) or tag\n(default is your repo's default branch: " - ++ RemoteData.unwrap "main" .branch model.repo - ++ ")" - , classList_ = [ ( "secret-value", True ) ] - , disabled_ = False - , rows_ = Just 3 - , wrap_ = Just "soft" - , msg = RefOnInput - } - , Components.Form.viewTextarea - { title = Just "Description" - , subtitle = Nothing - , id_ = "description" - , val = model.description - , placeholder_ = "provide the description for the deployment (default: \"Deployment request from Vela\")" - , classList_ = [ ( "secret-value", True ) ] - , disabled_ = False - , rows_ = Just 5 - , wrap_ = Just "soft" - , msg = DescriptionOnInput - } - , Components.Form.viewTextarea - { title = Just "Task" - , subtitle = Nothing - , id_ = "task" - , val = model.task - , placeholder_ = "Provide the task for the deployment (default: \"deploy:vela\")" - , classList_ = [ ( "secret-value", True ) ] - , disabled_ = False - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = TaskOnInput - } - , viewParametersInput model - , div [ class "help" ] - [ text "Need help? Visit our " - , Html.a - [ Html.Attributes.href <| shared.velaDocsURL ++ "/usage/deployments/" - , Html.Attributes.target "_blank" + else + [ div [ class "no-parameters" ] + [ div + [ class "none" + ] + [ code [] [ text "no parameters defined" ] ] + ] + ] ] - [ text "docs" ] - , text "!" - ] - , div [ class "buttons" ] - [ div [ class "form-action" ] - [ button - [ class "button" - , class "-outline" - , onClick SubmitForm + , div [ class "help" ] + [ text "Need help? Visit our " + , Html.a + [ Html.Attributes.href <| shared.velaDocsURL ++ "/usage/deployments/" + , Html.Attributes.target "_blank" + ] + [ text "docs" ] + , text "!" + ] + , div [ class "buttons" ] + [ div [ class "form-action" ] + [ button + [ class "button" + , class "-outline" + , onClick SubmitForm + ] + [ text "Submit" ] ] - [ text "Submit" ] ] ] ] @@ -369,88 +457,3 @@ view shared route model = ] ] } - - -viewParametersInput : Model -> Html Msg -viewParametersInput model = - section [] - [ div - [ id "parameter-select" - , class "form-control" - , class "-stack" - , class "parameters-container" - ] - [ label - [ for "parameter-select" - , class "form-label" - ] - [ strong [] [ text "Add Parameters" ] - , span - [ class "field-description" ] - [ em [] [ text "(Optional)" ] - ] - ] - , div [ class "parameters-inputs" ] - [ Components.Form.viewInput - { title = Nothing - , subtitle = Nothing - , id_ = "parameter-key" - , val = model.parameterKey - , placeholder_ = "key" - , classList_ = [ ( "parameter-input", True ) ] - , disabled_ = False - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ParameterKeyOnInput - } - , Components.Form.viewInput - { title = Nothing - , subtitle = Nothing - , id_ = "parameter-value" - , val = model.parameterValue - , placeholder_ = "value" - , classList_ = [ ( "parameter-input", True ) ] - , disabled_ = False - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ParameterValueOnInput - } - , button - [ class "button" - , class "-outline" - , class "add-parameter" - , onClick <| AddParameter - , Util.testAttribute "add-parameter-button" - , disabled <| String.length model.parameterKey == 0 || String.length model.parameterValue == 0 - ] - [ text "Add" - ] - ] - ] - , div [ class "parameters", Util.testAttribute "parameters-list" ] <| - if List.length model.parameters > 0 then - List.map viewParameter <| List.reverse model.parameters - - else - [ div [ class "no-parameters" ] - [ div - [ class "none" - ] - [ code [] [ text "no parameters defined" ] ] - ] - ] - ] - - -viewParameter : Vela.KeyValuePair -> Html Msg -viewParameter parameter = - div [ class "parameter", class "chevron" ] - [ button - [ class "button" - , class "-outline" - , onClick <| RemoveParameter parameter - ] - [ text "remove" - ] - , div [ class "name" ] [ text (parameter.key ++ "=" ++ parameter.value) ] - ] diff --git a/src/elm/Pages/Org_/Repo_/Audit.elm b/src/elm/Pages/Org_/Repo_/Hooks.elm similarity index 97% rename from src/elm/Pages/Org_/Repo_/Audit.elm rename to src/elm/Pages/Org_/Repo_/Hooks.elm index 4b6b3ad6d..a259ace1c 100644 --- a/src/elm/Pages/Org_/Repo_/Audit.elm +++ b/src/elm/Pages/Org_/Repo_/Hooks.elm @@ -3,7 +3,7 @@ SPDX-License-Identifier: Apache-2.0 --} -module Pages.Org_.Repo_.Audit exposing (..) +module Pages.Org_.Repo_.Hooks exposing (..) import Ansi.Log import Api.Pagination @@ -45,6 +45,7 @@ import Route.Path import Shared import Time import Utils.Ansi +import Utils.Errors import Utils.Helpers as Util import Utils.Interval as Interval import Vela @@ -138,8 +139,8 @@ update shared route msg model = ) Err error -> - ( model - , Effect.none + ( { model | hooks = Utils.Errors.toFailure error } + , Effect.handleHttpError { httpError = error } ) RedeliverRepoHook options -> @@ -217,7 +218,7 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String } -> Model -> View Msg view shared route model = - { title = "Audit" + { title = "Audit" ++ Util.pageToString (Dict.get "page" route.query) , body = [ viewHooks shared model model.hooks , Components.Pager.view model.pager Components.Pager.defaultLabels GotoPage @@ -287,7 +288,7 @@ hooksToRows now hooks redeliverHook = -} tableHeaders : Components.Table.Columns tableHeaders = - [ ( Just "table-icon", "" ) + [ ( Just "table-icon", "status" ) , ( Nothing, "source" ) , ( Nothing, "created" ) , ( Nothing, "host" ) diff --git a/src/elm/Pages/Org_/Repo_/Schedules.elm b/src/elm/Pages/Org_/Repo_/Schedules.elm index 74f0a83bd..168a22d66 100644 --- a/src/elm/Pages/Org_/Repo_/Schedules.elm +++ b/src/elm/Pages/Org_/Repo_/Schedules.elm @@ -9,6 +9,7 @@ import Api.Pagination import Auth import Components.Loading import Components.Pager +import Components.ScheduleForm import Components.Table import Dict import Effect exposing (Effect) @@ -71,6 +72,7 @@ toLayout user route model = type alias Model = { schedules : WebData (List Vela.Schedule) , pager : List WebLink + , repoSchedulesAllowed : Bool } @@ -78,6 +80,7 @@ init : Shared.Model -> Route { org : String, repo : String } -> () -> ( Model, E init shared route () = ( { schedules = RemoteData.Loading , pager = [] + , repoSchedulesAllowed = Util.checkScheduleAllowlist route.params.org route.params.repo shared.velaScheduleAllowlist } , Effect.batch [ Effect.getRepoSchedules @@ -175,7 +178,7 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String } -> Model -> View Msg view shared route model = - { title = "Schedules" + { title = "Schedules" ++ Util.pageToString (Dict.get "page" route.query) , body = [ viewRepoSchedules shared model route.params.org route.params.repo , Components.Pager.view model.pager Components.Pager.defaultLabels GotoPage @@ -188,11 +191,8 @@ view shared route model = viewRepoSchedules : Shared.Model -> Model -> String -> String -> Html Msg viewRepoSchedules shared model org repo = let - schedulesAllowed = - Util.checkScheduleAllowlist org repo shared.velaScheduleAllowlist - actions = - if schedulesAllowed then + if model.repoSchedulesAllowed then Just <| div [ class "buttons" ] [ a @@ -218,7 +218,7 @@ viewRepoSchedules shared model org repo = Nothing ( noRowsView, rows ) = - if schedulesAllowed then + if model.repoSchedulesAllowed then case model.schedules of RemoteData.Success s -> ( text "No schedules found for this repo" @@ -247,7 +247,7 @@ viewRepoSchedules shared model org repo = ( Components.Loading.viewSmallLoader, [] ) else - ( viewSchedulesNotAllowedSpan + ( Components.ScheduleForm.viewSchedulesNotAllowedWarning , [] ) @@ -359,12 +359,3 @@ viewSchedule zone org repo schedule = addKey : Vela.Schedule -> Vela.Schedule addKey schedule = { schedule | org = schedule.org ++ "/" ++ schedule.repo ++ "/" ++ schedule.name } - - -{-| viewSchedulesNotAllowedSpan : renders a warning that schedules have not been enabled for the current repository. --} -viewSchedulesNotAllowedSpan : Html msg -viewSchedulesNotAllowedSpan = - span [ class "not-allowed", Util.testAttribute "repo-schedule-not-allowed" ] - [ text "Sorry, Administrators have not enabled Schedules for this repository." - ] diff --git a/src/elm/Pages/Org_/Repo_/Schedules/Add.elm b/src/elm/Pages/Org_/Repo_/Schedules/Add.elm index b7b20342e..3ef8b1458 100644 --- a/src/elm/Pages/Org_/Repo_/Schedules/Add.elm +++ b/src/elm/Pages/Org_/Repo_/Schedules/Add.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Org_.Repo_.Schedules.Add exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.ScheduleForm import Effect exposing (Effect) -import Html exposing (div, em, h2, span, text) +import Html exposing (div, em, h2, main_, span, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -27,7 +29,7 @@ import View exposing (View) page : Auth.User -> Shared.Model -> Route { org : String, repo : String } -> Page Model Msg page user shared route = Page.new - { init = init shared + { init = init shared route , update = update shared route , subscriptions = subscriptions , view = view shared route @@ -42,17 +44,7 @@ page user shared route = toLayout : Auth.User -> Route { org : String, repo : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) - , ( "Schedules", Just <| Route.Path.Org_Repo_Schedules { org = route.params.org, repo = route.params.repo } ) - , ( "Add", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -65,15 +57,17 @@ type alias Model = , entry : String , enabled : Bool , branch : String + , repoSchedulesAllowed : Bool } -init : Shared.Model -> () -> ( Model, Effect Msg ) -init shared () = +init : Shared.Model -> Route { org : String, repo : String } -> () -> ( Model, Effect Msg ) +init shared route () = ( { name = "" , entry = "" , enabled = True , branch = "" + , repoSchedulesAllowed = Util.checkScheduleAllowlist route.params.org route.params.repo shared.velaScheduleAllowlist } , Effect.none ) @@ -176,63 +170,91 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String } -> Model -> View Msg view shared route model = + let + formDisabled = + not model.repoSchedulesAllowed + + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) + , ( "Schedules", Just <| Route.Path.Org_Repo_Schedules { org = route.params.org, repo = route.params.repo } ) + , ( "Add", Nothing ) + ] + in { title = "Add Schedule" , body = - [ div [ class "manage-schedule", Util.testAttribute "manage-schedule" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase <| "add repo schedule" ] - , div [ class "schedule-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = model.name - , placeholder_ = "Schedule Name" - , classList_ = [ ( "schedule-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = NameOnInput - , disabled_ = False - } - , Components.Form.viewTextarea - { title = Just "Cron Expression" - , subtitle = Just <| Components.ScheduleForm.viewCronHelp shared.time - , id_ = "cron" - , val = model.entry - , placeholder_ = "0 0 * * * (runs at 12:00 AM in UTC)" - , classList_ = [ ( "schedule-cron", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = EntryOnInput - , disabled_ = False - } - , Components.ScheduleForm.viewEnabledInput - { msg = EnabledOnClick - , value = model.enabled - , disabled_ = False - } - , Components.Form.viewInput - { title = Just "Branch" - , subtitle = - Just <| - span - [ class "field-description" ] - [ em [] [ text "(Leave blank to use default branch)" ] - ] - , id_ = "branch" - , val = model.branch - , placeholder_ = "Branch Name" - , classList_ = [ ( "branch-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = BranchOnInput - , disabled_ = False - } - , Components.ScheduleForm.viewHelp shared.velaDocsURL - , Components.ScheduleForm.viewSubmitButton - { msg = SubmitForm - , disabled_ = False - } + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-schedule", Util.testAttribute "manage-schedule" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase <| "add repo schedule" ] + , if not model.repoSchedulesAllowed then + Components.ScheduleForm.viewSchedulesNotAllowedWarning + + else + text "" + , div [ class "schedule-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = model.name + , placeholder_ = "Schedule Name" + , classList_ = [ ( "schedule-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = NameOnInput + , disabled_ = formDisabled + } + , Components.Form.viewTextarea + { title = Just "Cron Expression" + , subtitle = Just <| Components.ScheduleForm.viewCronHelp shared.time + , id_ = "entry" + , val = model.entry + , placeholder_ = "0 0 * * * (runs at 12:00 AM in UTC)" + , classList_ = [ ( "schedule-cron", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = EntryOnInput + , disabled_ = formDisabled + } + , Components.ScheduleForm.viewEnabledInput + { msg = EnabledOnClick + , value = model.enabled + , disabled_ = formDisabled + } + , Components.Form.viewInput + { title = Just "Branch" + , subtitle = + Just <| + span + [ class "field-description" ] + [ em [] [ text "(Leave blank to use default branch)" ] + ] + , id_ = "branch-name" + , val = model.branch + , placeholder_ = "Branch Name" + , classList_ = [ ( "branch-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = BranchOnInput + , disabled_ = formDisabled + } + , Components.ScheduleForm.viewHelp shared.velaDocsURL + , Components.Form.viewButton + { msg = SubmitForm + , id_ = "submit" + , text_ = "Submit" + , classList_ = [] + , disabled_ = formDisabled + } + ] ] ] ] diff --git a/src/elm/Pages/Org_/Repo_/Schedules/Edit_.elm b/src/elm/Pages/Org_/Repo_/Schedules/Edit_.elm index c989cc264..b3dab72c0 100644 --- a/src/elm/Pages/Org_/Repo_/Schedules/Edit_.elm +++ b/src/elm/Pages/Org_/Repo_/Schedules/Edit_.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Org_.Repo_.Schedules.Edit_ exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.ScheduleForm import Effect exposing (Effect) -import Html exposing (div, em, h2, span, text) +import Html exposing (div, em, h2, main_, span, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -43,25 +45,7 @@ page user shared route = toLayout : Auth.User -> Route { org : String, repo : String, name : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) - , ( "Schedules", Just <| Route.Path.Org_Repo_Schedules { org = route.params.org, repo = route.params.repo } ) - , ( "Edit", Nothing ) - , ( route.params.name - , Just <| - Route.Path.Org_Repo_SchedulesEdit_ - { org = route.params.org - , repo = route.params.repo - , name = route.params.name - } - ) - ] - , repo = Nothing + { helpCommands = [] } @@ -76,6 +60,7 @@ type alias Model = , enabled : Bool , branch : String , confirmingDelete : Bool + , repoSchedulesAllowed : Bool } @@ -87,15 +72,20 @@ init shared route () = , enabled = True , branch = "" , confirmingDelete = False + , repoSchedulesAllowed = Util.checkScheduleAllowlist route.params.org route.params.repo shared.velaScheduleAllowlist } - , Effect.getRepoSchedule - { baseUrl = shared.velaAPIBaseURL - , session = shared.session - , onResponse = GetRepoScheduleResponse - , org = route.params.org - , repo = route.params.repo - , name = route.params.name - } + , if Util.checkScheduleAllowlist route.params.org route.params.repo shared.velaScheduleAllowlist then + Effect.getRepoSchedule + { baseUrl = shared.velaAPIBaseURL + , session = shared.session + , onResponse = GetRepoScheduleResponse + , org = route.params.org + , repo = route.params.repo + , name = route.params.name + } + + else + Effect.none ) @@ -263,91 +253,136 @@ subscriptions model = view : Shared.Model -> Route { org : String, repo : String, name : String } -> Model -> View Msg view shared route model = + let + schedulesAllowed = + Util.checkScheduleAllowlist route.params.org route.params.repo shared.velaScheduleAllowlist + + formDisabled = + not schedulesAllowed || (not <| RemoteData.isSuccess model.schedule) + + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) + , ( "Schedules", Just <| Route.Path.Org_Repo_Schedules { org = route.params.org, repo = route.params.repo } ) + , ( "Edit", Nothing ) + , ( route.params.name + , Just <| + Route.Path.Org_Repo_SchedulesEdit_ + { org = route.params.org + , repo = route.params.repo + , name = route.params.name + } + ) + ] + in { title = "Add Schedule" , body = - [ div [ class "manage-schedule", Util.testAttribute "manage-schedule" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase <| "update repo schedule" ] - , div [ class "schedule-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = RemoteData.unwrap "" .name model.schedule - , placeholder_ = "loading..." - , classList_ = [ ( "schedule-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = \_ -> NoOp - , disabled_ = True - } - , Components.Form.viewTextarea - { title = Just "Cron Expression" - , subtitle = Just <| Components.ScheduleForm.viewCronHelp shared.time - , id_ = "cron" - , val = model.entry - , placeholder_ = "0 0 * * * (runs at 12:00 AM in UTC)" - , classList_ = [ ( "schedule-cron", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = EntryOnInput - , disabled_ = not <| RemoteData.isSuccess model.schedule - } - , Components.ScheduleForm.viewEnabledInput - { msg = EnabledOnClick - , value = model.enabled - , disabled_ = not <| RemoteData.isSuccess model.schedule - } - , Components.Form.viewInput - { title = Just "Branch" - , subtitle = - Just <| - span - [ class "field-description" ] - [ em [] [ text "(Leave blank to use default branch)" ] - ] - , id_ = "branch" - , val = model.branch - , placeholder_ = "Branch Name" - , classList_ = [ ( "branch-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = BranchOnInput - , disabled_ = not <| RemoteData.isSuccess model.schedule - } - , Components.ScheduleForm.viewHelp shared.velaDocsURL - , div [ class "buttons" ] - [ Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] - , disabled_ = not <| RemoteData.isSuccess model.schedule + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-schedule", Util.testAttribute "manage-schedule" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase <| "update repo schedule" ] + , if not model.repoSchedulesAllowed then + Components.ScheduleForm.viewSchedulesNotAllowedWarning + + else + text "" + , div [ class "schedule-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = RemoteData.unwrap "" .name model.schedule + , placeholder_ = + if model.repoSchedulesAllowed then + "loading..." + + else + "Schedule Name" + , classList_ = [ ( "schedule-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = \_ -> NoOp + , disabled_ = True } - , if not model.confirmingDelete then - Components.Form.viewButton - { msg = ClickDelete - , text_ = "Delete" - , classList_ = [] - , disabled_ = not <| RemoteData.isSuccess model.schedule - } - - else - Components.Form.viewButton - { msg = CancelDelete - , text_ = "Cancel" + , Components.Form.viewTextarea + { title = Just "Cron Expression" + , subtitle = Just <| Components.ScheduleForm.viewCronHelp shared.time + , id_ = "entry" + , val = model.entry + , placeholder_ = "0 0 * * * (runs at 12:00 AM in UTC)" + , classList_ = [ ( "schedule-cron", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = EntryOnInput + , disabled_ = formDisabled + } + , Components.ScheduleForm.viewEnabledInput + { msg = EnabledOnClick + , value = model.enabled + , disabled_ = formDisabled + } + , Components.Form.viewInput + { title = Just "Branch" + , subtitle = + Just <| + span + [ class "field-description" ] + [ em [] [ text "(Leave blank to use default branch)" ] + ] + , id_ = "branch-name" + , val = model.branch + , placeholder_ = "Branch Name" + , classList_ = [ ( "branch-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = BranchOnInput + , disabled_ = formDisabled + } + , Components.ScheduleForm.viewHelp shared.velaDocsURL + , div [ class "buttons" ] + [ Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" , classList_ = [] - , disabled_ = not <| RemoteData.isSuccess model.schedule + , disabled_ = formDisabled + , id_ = "submit" } - , if model.confirmingDelete then - Components.Form.viewButton - { msg = ConfirmDelete - , text_ = "Confirm" - , classList_ = [ ( "-secret-delete-confirm", True ) ] - , disabled_ = not <| RemoteData.isSuccess model.schedule - } - - else - text "" + , if not model.confirmingDelete then + Components.Form.viewButton + { msg = ClickDelete + , text_ = "Delete" + , classList_ = [] + , disabled_ = formDisabled + , id_ = "delete" + } + + else + Components.Form.viewButton + { msg = CancelDelete + , text_ = "Cancel" + , classList_ = [] + , disabled_ = formDisabled + , id_ = "delete-cancel" + } + , if model.confirmingDelete then + Components.Form.viewButton + { msg = ConfirmDelete + , text_ = "Confirm" + , classList_ = [ ( "-secret-delete-confirm", True ) ] + , disabled_ = formDisabled + , id_ = "delete-confirm" + } + + else + text "" + ] ] ] ] diff --git a/src/elm/Pages/Org_/Repo_/Settings.elm b/src/elm/Pages/Org_/Repo_/Settings.elm index 7606634af..d760ec903 100644 --- a/src/elm/Pages/Org_/Repo_/Settings.elm +++ b/src/elm/Pages/Org_/Repo_/Settings.elm @@ -102,7 +102,7 @@ type Msg | DisableRepo { repo : Vela.Repository } | DisableRepoResponse { repo : Vela.Repository } (Result (Http.Detailed.Error String) ( Http.Metadata, String )) | RepairRepo { repo : Vela.Repository } - | RepairRepoResponse (Result (Http.Detailed.Error String) ( Http.Metadata, String )) + | RepairRepoResponse { repo : Vela.Repository } (Result (Http.Detailed.Error String) ( Http.Metadata, String )) | ChownRepo { repo : Vela.Repository } | ChownRepoResponse (Result (Http.Detailed.Error String) ( Http.Metadata, String )) | AllowEventsUpdate { allowEvents : Vela.AllowEvents, event : String } Bool @@ -356,13 +356,13 @@ update shared route msg model = , Effect.repairRepo { baseUrl = shared.velaAPIBaseURL , session = shared.session - , onResponse = RepairRepoResponse + , onResponse = RepairRepoResponse options , org = route.params.org , repo = route.params.repo } ) - RepairRepoResponse response -> + RepairRepoResponse options response -> case response of Ok ( _, result ) -> ( model @@ -584,7 +584,7 @@ update shared route msg model = -- ALERTS AddAlertCopiedToClipboard contentCopied -> ( model - , Effect.addAlertSuccess { content = contentCopied, addToastIfUnique = False } + , Effect.addAlertSuccess { content = "'" ++ contentCopied ++ "' copied to clipboard.", addToastIfUnique = False } ) -- REFRESH @@ -620,21 +620,7 @@ view shared route model = [ case model.repo of RemoteData.Success repo -> div [ class "repo-settings", Util.testAttribute "repo-settings" ] - [ section [ class "settings", Util.testAttribute "repo-settings-events" ] - ([ h2 [ class "settings-title" ] [ text "Webhook Events" ] - , p [ class "settings-description" ] - [ text "Control which events on Git will trigger Vela pipelines." - , br [] [] - , em [] [ text "Active repositories must have at least one event enabled." ] - ] - ] - ++ Components.Form.viewAllowEvents - shared - { msg = AllowEventsUpdate - , allowEvents = repo.allowEvents - , disabled_ = False - } - ) + [ viewAllowEvents shared repo AllowEventsUpdate , viewAccess repo AccessUpdate , viewForkPolicy repo ForkPolicyUpdate , viewLimit shared repo model.inLimit BuildLimitUpdate BuildLimitOnInput @@ -651,7 +637,28 @@ view shared route model = } -{-| viewAccess : takes model and repo and renders the settings category for updating repo access +{-| viewAllowEvents : takes shared model and repo and renders the settings category for updating repo allow events +-} +viewAllowEvents : Shared.Model -> Vela.Repository -> ({ allowEvents : Vela.AllowEvents, event : String } -> Bool -> msg) -> Html msg +viewAllowEvents shared repo msg = + section [ class "settings", Util.testAttribute "repo-settings-events" ] + ([ h2 [ class "settings-title" ] [ text "Webhook Events" ] + , p [ class "settings-description" ] + [ text "Control which events on Git will trigger Vela pipelines." + , br [] [] + , em [] [ text "Active repositories must have at least one event enabled." ] + ] + ] + ++ Components.Form.viewAllowEvents + shared + { msg = msg + , allowEvents = repo.allowEvents + , disabled_ = False + } + ) + + +{-| viewAccess : takes shared model and repo and renders the settings category for updating repo access -} viewAccess : Vela.Repository -> (String -> msg) -> Html msg viewAccess repo msg = @@ -666,6 +673,7 @@ viewAccess repo msg = , field = "private" , msg = msg "private" , disabled_ = False + , id_ = "access-private" } , Components.Form.viewRadio { title = "Any" @@ -674,6 +682,7 @@ viewAccess repo msg = , field = "public" , msg = msg "public" , disabled_ = False + , id_ = "access-public" } ] ] @@ -694,6 +703,7 @@ viewForkPolicy repo msg = , field = "fork-always" , msg = msg "fork-always" , disabled_ = False + , id_ = "fork-policy-always" } , Components.Form.viewRadio { title = "Require Admin Approval When Contributor Is Read Only" @@ -702,6 +712,7 @@ viewForkPolicy repo msg = , field = "fork-no-write" , msg = msg "fork-no-write" , disabled_ = False + , id_ = "fork-no-write" } , Components.Form.viewRadio { title = "Never Require Admin Approval" @@ -710,6 +721,7 @@ viewForkPolicy repo msg = , field = "never" , msg = msg "never" , disabled_ = False + , id_ = "fork-policy-never" } ] ] @@ -1100,7 +1112,6 @@ viewEnableButton disableRepoMsg enableRepoMsg repo = , baseTestAttribute , class "repo-disable-confirm" , onClick disableRepoMsg - , class "-secret-delete-confirm" ] [ text "Really Disable?" ] @@ -1177,6 +1188,7 @@ viewPipelineType repo msg = , subtitle = Nothing , msg = msg "yaml" , disabled_ = False + , id_ = "type-yaml" } , Components.Form.viewRadio { value = repo.pipeline_type @@ -1185,6 +1197,7 @@ viewPipelineType repo msg = , subtitle = Nothing , msg = msg "go" , disabled_ = False + , id_ = "type-go" } , Components.Form.viewRadio { value = repo.pipeline_type @@ -1193,6 +1206,7 @@ viewPipelineType repo msg = , subtitle = Nothing , msg = msg "starlark" , disabled_ = False + , id_ = "type-starlark" } ] ] diff --git a/src/elm/Pages/PageTemplate.elm b/src/elm/Pages/PageTemplate.elm index 2e97d3248..178bb4783 100644 --- a/src/elm/Pages/PageTemplate.elm +++ b/src/elm/Pages/PageTemplate.elm @@ -6,11 +6,15 @@ SPDX-License-Identifier: Apache-2.0 module Pages.PageTemplate exposing (Model, Msg, page, view) import Auth +import Components.Crumbs +import Components.Nav import Effect exposing (Effect) import Html exposing - ( text + ( main_ + , text ) +import Html.Attributes exposing (class) import Layouts import Page exposing (Page) import Route exposing (Route) @@ -25,7 +29,7 @@ page user shared route = { init = init shared , update = update , subscriptions = subscriptions - , view = view shared + , view = view shared route } |> Page.withLayout (toLayout user) @@ -37,16 +41,7 @@ page user shared route = toLayout : Auth.User -> Model -> Layouts.Layout Msg toLayout user model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Home", Just Route.Path.Home ) - , ( "Crumbs", Nothing ) - , ( "Go", Nothing ) - , ( "Here", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -95,22 +90,38 @@ subscriptions model = -- VIEW -view : Shared.Model -> Model -> View Msg -view shared model = +view : Shared.Model -> Route { org : String, repo : String } -> Model -> View Msg +view shared route model = + let + crumbs = + [ ( "Home", Just Route.Path.Home ) + , ( "Crumbs", Nothing ) + , ( "Go", Nothing ) + , ( "Here", Nothing ) + ] + in { title = "Pages.PageTemplate" , body = - [ Html.ul [] - [ Html.li [] [ text "this is a template page\n" ] - , Html.li [] [ text "add page specific data to the Model like API resources\n" ] - , Html.li [] [ text "apply any Layouts to the page in toLayout\n" ] - , Html.li [] [ text "add html to the view to trigger page messages\n" ] - , Html.li [] [ text "re-use components first, but add new components when necessary\n" ] - , Html.li [] [ text "add page messages and pass them as components args\n" ] - , Html.li [] [ text "use page messages to dispatch shared Effects and handle API calls\n" ] - , Html.li [] [ text "use page messages and shared Effects to dispatch API calls\n" ] - , Html.li [] [ text "add page specific subscriptions like page refresh and favicon updates\n" ] - , Html.li [] [ text "try to implement things in the page!\n" ] - , Html.li [] [ text "avoid using Shared.Model whenever possible!\n" ] + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ Html.ul [] + [ Html.li [] [ text "this is a template page\n" ] + , Html.li [] [ text "add page specific data to the Model like API resources\n" ] + , Html.li [] [ text "apply any Layouts to the page in toLayout\n" ] + , Html.li [] [ text "add html to the view to trigger page messages\n" ] + , Html.li [] [ text "re-use components first, but add new components when necessary\n" ] + , Html.li [] [ text "add page messages and pass them as components args\n" ] + , Html.li [] [ text "use page messages to dispatch shared Effects and handle API calls\n" ] + , Html.li [] [ text "use page messages and shared Effects to dispatch API calls\n" ] + , Html.li [] [ text "add page specific subscriptions like page refresh and favicon updates\n" ] + , Html.li [] [ text "try to implement things in the page!\n" ] + , Html.li [] [ text "avoid using Shared.Model whenever possible!\n" ] + ] ] ] } diff --git a/src/elm/Pages/Secrets/Engine_/Org/Org_.elm b/src/elm/Pages/Secrets/Engine_/Org/Org_.elm index cd3f83eeb..cb6e4f7d7 100644 --- a/src/elm/Pages/Secrets/Engine_/Org/Org_.elm +++ b/src/elm/Pages/Secrets/Engine_/Org/Org_.elm @@ -174,7 +174,7 @@ update shared route msg model = -- ALERTS AddAlertCopiedToClipboard contentCopied -> ( model - , Effect.addAlertSuccess { content = contentCopied, addToastIfUnique = False } + , Effect.addAlertSuccess { content = "'" ++ contentCopied ++ "' copied to clipboard.", addToastIfUnique = False } ) -- REFRESH @@ -219,7 +219,7 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String } -> Model -> View Msg view shared route model = - { title = "Secrets" + { title = "Secrets" ++ Util.pageToString (Dict.get "page" route.query) , body = [ Components.Secrets.viewOrgSecrets shared { msgs = diff --git a/src/elm/Pages/Secrets/Engine_/Org/Org_/Add.elm b/src/elm/Pages/Secrets/Engine_/Org/Org_/Add.elm index f9ad19304..b019d2b0e 100644 --- a/src/elm/Pages/Secrets/Engine_/Org/Org_/Add.elm +++ b/src/elm/Pages/Secrets/Engine_/Org/Org_/Add.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Secrets.Engine_.Org.Org_.Add exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.SecretForm import Effect exposing (Effect) -import Html exposing (div, h2, text) +import Html exposing (div, h2, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -43,16 +45,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( "Secrets", Just <| Route.Path.SecretsEngine_OrgOrg_ { org = route.params.org, engine = route.params.engine } ) - , ( "Add", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -210,62 +203,79 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( "Org Secrets", Just <| Route.Path.SecretsEngine_OrgOrg_ { org = route.params.org, engine = route.params.engine } ) + , ( "Add", Nothing ) + ] + in { title = "Add Secret" , body = - [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase "add org secret" ] - , div [ class "secret-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = model.name - , placeholder_ = "Secret Name" - , classList_ = [ ( "secret-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = NameOnInput - , disabled_ = False - } - , Components.Form.viewTextarea - { title = Just "Value" - , subtitle = Nothing - , id_ = "value" - , val = model.value - , placeholder_ = "secret-value" - , classList_ = [ ( "secret-value", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ValueOnInput - , disabled_ = False - } - , Components.SecretForm.viewAllowEventsSelect - shared - { msg = AllowEventsUpdate - , allowEvents = model.allowEvents - , disabled_ = False - } - , Components.SecretForm.viewImagesInput - { onInput_ = ImageOnInput - , addImage = AddImage - , removeImage = RemoveImage - , images = model.images - , imageValue = model.image - , disabled_ = False - } - , Components.SecretForm.viewAllowCommandsInput - { msg = AllowCommandsOnClick - , value = model.allowCommand - , disabled_ = False - } - , Components.SecretForm.viewHelp shared.velaDocsURL - , Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] - , disabled_ = False - } + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase "add org secret" ] + , div [ class "secret-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = model.name + , placeholder_ = "Secret Name" + , classList_ = [ ( "secret-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = NameOnInput + , disabled_ = False + } + , Components.Form.viewTextarea + { title = Just "Value" + , subtitle = Nothing + , id_ = "value" + , val = model.value + , placeholder_ = "secret-value" + , classList_ = [ ( "secret-value", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ValueOnInput + , disabled_ = False + } + , Components.SecretForm.viewAllowEventsSelect + shared + { msg = AllowEventsUpdate + , allowEvents = model.allowEvents + , disabled_ = False + } + , Components.SecretForm.viewImagesInput + { onInput_ = ImageOnInput + , addImage = AddImage + , removeImage = RemoveImage + , images = model.images + , imageValue = model.image + , disabled_ = False + } + , Components.SecretForm.viewAllowCommandsInput + { msg = AllowCommandsOnClick + , value = model.allowCommand + , disabled_ = False + } + , Components.SecretForm.viewHelp shared.velaDocsURL + , Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" + , classList_ = [] + , disabled_ = False + , id_ = "submit" + } + ] ] ] ] diff --git a/src/elm/Pages/Secrets/Engine_/Org/Org_/Edit_.elm b/src/elm/Pages/Secrets/Engine_/Org/Org_/Edit_.elm index 25b06381e..a3d885b1e 100644 --- a/src/elm/Pages/Secrets/Engine_/Org/Org_/Edit_.elm +++ b/src/elm/Pages/Secrets/Engine_/Org/Org_/Edit_.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Secrets.Engine_.Org.Org_.Edit_ exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.SecretForm import Effect exposing (Effect) -import Html exposing (div, h2, text) +import Html exposing (div, h2, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -44,17 +46,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String, name : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( "Secrets", Just <| Route.Path.SecretsEngine_OrgOrg_ { org = route.params.org, engine = route.params.engine } ) - , ( "Edit", Nothing ) - , ( route.params.name, Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -291,88 +283,109 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, name : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( "Org Secrets", Just <| Route.Path.SecretsEngine_OrgOrg_ { org = route.params.org, engine = route.params.engine } ) + , ( "Edit", Nothing ) + , ( route.params.name, Nothing ) + ] + in { title = "Edit Secret" , body = - [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase "edit org secret" ] - , div [ class "secret-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = RemoteData.unwrap "" .name model.secret - , placeholder_ = "loading..." - , classList_ = [ ( "secret-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = NameOnInput - , disabled_ = True - } - , Components.Form.viewTextarea - { title = Just "Value" - , subtitle = Nothing - , id_ = "value" - , val = model.value - , placeholder_ = RemoteData.unwrap "loading..." (\_ -> "") model.secret - , classList_ = [ ( "secret-value", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ValueOnInput - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewAllowEventsSelect - shared - { msg = AllowEventsUpdate - , allowEvents = model.allowEvents - , disabled_ = False - } - , Components.SecretForm.viewImagesInput - { onInput_ = ImageOnInput - , addImage = AddImage - , removeImage = RemoveImage - , images = model.images - , imageValue = model.image - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewAllowCommandsInput - { msg = AllowCommandsOnClick - , value = model.allowCommand - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewHelp shared.velaDocsURL - , div [ class "buttons" ] - [ Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase "edit org secret" ] + , div [ class "secret-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = RemoteData.unwrap "" .name model.secret + , placeholder_ = "loading..." + , classList_ = [ ( "secret-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = NameOnInput + , disabled_ = True + } + , Components.Form.viewTextarea + { title = Just "Value" + , subtitle = Nothing + , id_ = "value" + , val = model.value + , placeholder_ = RemoteData.unwrap "loading..." (\_ -> "") model.secret + , classList_ = [ ( "secret-value", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ValueOnInput , disabled_ = not <| RemoteData.isSuccess model.secret } - , if not model.confirmingDelete then - Components.Form.viewButton - { msg = ClickDelete - , text_ = "Delete" - , classList_ = [] - , disabled_ = not <| RemoteData.isSuccess model.secret - } - - else - Components.Form.viewButton - { msg = CancelDelete - , text_ = "Cancel" + , Components.SecretForm.viewAllowEventsSelect + shared + { msg = AllowEventsUpdate + , allowEvents = model.allowEvents + , disabled_ = False + } + , Components.SecretForm.viewImagesInput + { onInput_ = ImageOnInput + , addImage = AddImage + , removeImage = RemoveImage + , images = model.images + , imageValue = model.image + , disabled_ = not <| RemoteData.isSuccess model.secret + } + , Components.SecretForm.viewAllowCommandsInput + { msg = AllowCommandsOnClick + , value = model.allowCommand + , disabled_ = not <| RemoteData.isSuccess model.secret + } + , Components.SecretForm.viewHelp shared.velaDocsURL + , div [ class "buttons" ] + [ Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" , classList_ = [] , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "submit" } - , if model.confirmingDelete then - Components.Form.viewButton - { msg = ConfirmDelete - , text_ = "Confirm" - , classList_ = [ ( "-secret-delete-confirm", True ) ] - , disabled_ = not <| RemoteData.isSuccess model.secret - } - - else - text "" + , if not model.confirmingDelete then + Components.Form.viewButton + { msg = ClickDelete + , text_ = "Delete" + , classList_ = [] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete" + } + + else + Components.Form.viewButton + { msg = CancelDelete + , text_ = "Cancel" + , classList_ = [] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete-cancel" + } + , if model.confirmingDelete then + Components.Form.viewButton + { msg = ConfirmDelete + , text_ = "Confirm" + , classList_ = [ ( "-secret-delete-confirm", True ) ] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete-confirm" + } + + else + text "" + ] ] ] ] diff --git a/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_.elm b/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_.elm index 49b2982d8..422761663 100644 --- a/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_.elm +++ b/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_.elm @@ -177,7 +177,7 @@ update shared route msg model = -- ALERTS AddAlertCopiedToClipboard contentCopied -> ( model - , Effect.addAlertSuccess { content = contentCopied, addToastIfUnique = False } + , Effect.addAlertSuccess { content = "'" ++ contentCopied ++ "' copied to clipboard.", addToastIfUnique = False } ) -- REFRESH @@ -222,7 +222,7 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, repo : String } -> Model -> View Msg view shared route model = - { title = "Secrets" + { title = "Secrets" ++ Util.pageToString (Dict.get "page" route.query) , body = [ Components.Secrets.viewRepoSecrets shared { msgs = diff --git a/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Add.elm b/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Add.elm index 92257bf2c..9b934f060 100644 --- a/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Add.elm +++ b/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Add.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Secrets.Engine_.Repo.Org_.Repo_.Add exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.SecretForm import Effect exposing (Effect) -import Html exposing (div, h2, text) +import Html exposing (div, h2, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -43,17 +45,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String, repo : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) - , ( "Secrets", Just <| Route.Path.SecretsEngine_RepoOrg_Repo_ { org = route.params.org, repo = route.params.repo, engine = route.params.engine } ) - , ( "Add", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -212,62 +204,80 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, repo : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) + , ( "Repo Secrets", Just <| Route.Path.SecretsEngine_RepoOrg_Repo_ { org = route.params.org, repo = route.params.repo, engine = route.params.engine } ) + , ( "Add", Nothing ) + ] + in { title = "Add Secret" , body = - [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase "add repo secret" ] - , div [ class "secret-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = model.name - , placeholder_ = "Secret Name" - , classList_ = [ ( "secret-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = NameOnInput - , disabled_ = False - } - , Components.Form.viewTextarea - { title = Just "Value" - , subtitle = Nothing - , id_ = "value" - , val = model.value - , placeholder_ = "secret-value" - , classList_ = [ ( "secret-value", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ValueOnInput - , disabled_ = False - } - , Components.SecretForm.viewAllowEventsSelect - shared - { msg = AllowEventsUpdate - , allowEvents = model.allowEvents - , disabled_ = False - } - , Components.SecretForm.viewImagesInput - { onInput_ = ImageOnInput - , addImage = AddImage - , removeImage = RemoveImage - , images = model.images - , imageValue = model.image - , disabled_ = False - } - , Components.SecretForm.viewAllowCommandsInput - { msg = AllowCommandsOnClick - , value = model.allowCommand - , disabled_ = False - } - , Components.SecretForm.viewHelp shared.velaDocsURL - , Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] - , disabled_ = False - } + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase "add repo secret" ] + , div [ class "secret-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = model.name + , placeholder_ = "Secret Name" + , classList_ = [ ( "secret-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = NameOnInput + , disabled_ = False + } + , Components.Form.viewTextarea + { title = Just "Value" + , subtitle = Nothing + , id_ = "value" + , val = model.value + , placeholder_ = "secret-value" + , classList_ = [ ( "secret-value", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ValueOnInput + , disabled_ = False + } + , Components.SecretForm.viewAllowEventsSelect + shared + { msg = AllowEventsUpdate + , allowEvents = model.allowEvents + , disabled_ = False + } + , Components.SecretForm.viewImagesInput + { onInput_ = ImageOnInput + , addImage = AddImage + , removeImage = RemoveImage + , images = model.images + , imageValue = model.image + , disabled_ = False + } + , Components.SecretForm.viewAllowCommandsInput + { msg = AllowCommandsOnClick + , value = model.allowCommand + , disabled_ = False + } + , Components.SecretForm.viewHelp shared.velaDocsURL + , Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" + , classList_ = [] + , disabled_ = False + , id_ = "submit" + } + ] ] ] ] diff --git a/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Edit_.elm b/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Edit_.elm index eb6edc42f..f8fda9bfd 100644 --- a/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Edit_.elm +++ b/src/elm/Pages/Secrets/Engine_/Repo/Org_/Repo_/Edit_.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Secrets.Engine_.Repo.Org_.Repo_.Edit_ exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.SecretForm import Effect exposing (Effect) -import Html exposing (div, h2, text) +import Html exposing (div, h2, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -44,18 +46,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String, repo : String, name : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) - , ( "Secrets", Just <| Route.Path.SecretsEngine_RepoOrg_Repo_ { org = route.params.org, repo = route.params.repo, engine = route.params.engine } ) - , ( "Edit", Nothing ) - , ( route.params.name, Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -294,88 +285,110 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, repo : String, name : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.repo, Just <| Route.Path.Org_Repo_ { org = route.params.org, repo = route.params.repo } ) + , ( "Repo Secrets", Just <| Route.Path.SecretsEngine_RepoOrg_Repo_ { org = route.params.org, repo = route.params.repo, engine = route.params.engine } ) + , ( "Edit", Nothing ) + , ( route.params.name, Nothing ) + ] + in { title = "Edit Secret" , body = - [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase "edit repo secret" ] - , div [ class "secret-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = RemoteData.unwrap "" .name model.secret - , placeholder_ = "loading..." - , classList_ = [ ( "secret-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = \_ -> NoOp - , disabled_ = True - } - , Components.Form.viewTextarea - { title = Just "Value" - , subtitle = Nothing - , id_ = "value" - , val = model.value - , placeholder_ = RemoteData.unwrap "loading..." (\_ -> "") model.secret - , classList_ = [ ( "secret-value", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ValueOnInput - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewAllowEventsSelect - shared - { msg = AllowEventsUpdate - , allowEvents = model.allowEvents - , disabled_ = False - } - , Components.SecretForm.viewImagesInput - { onInput_ = ImageOnInput - , addImage = AddImage - , removeImage = RemoveImage - , images = model.images - , imageValue = model.image - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewAllowCommandsInput - { msg = AllowCommandsOnClick - , value = model.allowCommand - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewHelp shared.velaDocsURL - , div [ class "buttons" ] - [ Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase "edit repo secret" ] + , div [ class "secret-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = RemoteData.unwrap "" .name model.secret + , placeholder_ = "loading..." + , classList_ = [ ( "secret-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = \_ -> NoOp + , disabled_ = True + } + , Components.Form.viewTextarea + { title = Just "Value" + , subtitle = Nothing + , id_ = "value" + , val = model.value + , placeholder_ = RemoteData.unwrap "loading..." (\_ -> "") model.secret + , classList_ = [ ( "secret-value", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ValueOnInput , disabled_ = not <| RemoteData.isSuccess model.secret } - , if not model.confirmingDelete then - Components.Form.viewButton - { msg = ClickDelete - , text_ = "Delete" - , classList_ = [] - , disabled_ = not <| RemoteData.isSuccess model.secret - } - - else - Components.Form.viewButton - { msg = CancelDelete - , text_ = "Cancel" + , Components.SecretForm.viewAllowEventsSelect + shared + { msg = AllowEventsUpdate + , allowEvents = model.allowEvents + , disabled_ = False + } + , Components.SecretForm.viewImagesInput + { onInput_ = ImageOnInput + , addImage = AddImage + , removeImage = RemoveImage + , images = model.images + , imageValue = model.image + , disabled_ = not <| RemoteData.isSuccess model.secret + } + , Components.SecretForm.viewAllowCommandsInput + { msg = AllowCommandsOnClick + , value = model.allowCommand + , disabled_ = not <| RemoteData.isSuccess model.secret + } + , Components.SecretForm.viewHelp shared.velaDocsURL + , div [ class "buttons" ] + [ Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" , classList_ = [] , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "submit" } - , if model.confirmingDelete then - Components.Form.viewButton - { msg = ConfirmDelete - , text_ = "Confirm" - , classList_ = [ ( "-secret-delete-confirm", True ) ] - , disabled_ = not <| RemoteData.isSuccess model.secret - } - - else - text "" + , if not model.confirmingDelete then + Components.Form.viewButton + { msg = ClickDelete + , text_ = "Delete" + , classList_ = [] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete" + } + + else + Components.Form.viewButton + { msg = CancelDelete + , text_ = "Cancel" + , classList_ = [] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete-cancel" + } + , if model.confirmingDelete then + Components.Form.viewButton + { msg = ConfirmDelete + , text_ = "Confirm" + , classList_ = [ ( "-secret-delete-confirm", True ) ] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete-confirm" + } + + else + text "" + ] ] ] ] diff --git a/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_.elm b/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_.elm index 5848d047a..8bfb44470 100644 --- a/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_.elm +++ b/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_.elm @@ -7,12 +7,14 @@ module Pages.Secrets.Engine_.Shared.Org_.Team_ exposing (Model, Msg, page, view) import Api.Pagination import Auth +import Components.Crumbs +import Components.Nav import Components.Pager import Components.Secrets import Dict import Effect exposing (Effect) import FeatherIcons -import Html exposing (a, text) +import Html exposing (a, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -50,16 +52,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String, team : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.team, Nothing ) - , ( "Secrets", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -149,7 +142,7 @@ update shared route msg model = -- ALERTS AddAlertCopiedToClipboard contentCopied -> ( model - , Effect.addAlertSuccess { content = contentCopied, addToastIfUnique = False } + , Effect.addAlertSuccess { content = "'" ++ contentCopied ++ "' copied to clipboard.", addToastIfUnique = False } ) -- REFRESH @@ -183,32 +176,48 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, team : String } -> Model -> View Msg view shared route model = - { title = "Secrets" + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.team, Nothing ) + , ( "Secrets", Nothing ) + ] + in + { title = "Secrets" ++ Util.pageToString (Dict.get "page" route.query) , body = - [ Components.Secrets.viewSharedSecrets shared - { msgs = - { showCopyAlert = AddAlertCopiedToClipboard - } - , engine = route.params.engine - , key = route.params.org ++ "/" ++ route.params.team - , secrets = model.sharedSecrets - , tableButtons = - Just - [ a - [ class "button" - , class "-outline" - , class "button-with-icon" - , Util.testAttribute "add-shared-secret" - , Route.Path.href <| - Route.Path.SecretsEngine_SharedOrg_Team_Add { engine = route.params.engine, org = route.params.org, team = route.params.team } - ] - [ text "Add Shared Secret" - , FeatherIcons.plus - |> FeatherIcons.withSize 18 - |> FeatherIcons.toHtml [ Svg.Attributes.class "button-icon" ] - ] - , Components.Pager.view model.pager Components.Pager.defaultLabels GotoPage - ] + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs } + , main_ [ class "content-wrap" ] + [ Components.Secrets.viewSharedSecrets shared + { msgs = + { showCopyAlert = AddAlertCopiedToClipboard + } + , engine = route.params.engine + , key = route.params.org ++ "/" ++ route.params.team + , secrets = model.sharedSecrets + , tableButtons = + Just + [ a + [ class "button" + , class "-outline" + , class "button-with-icon" + , Util.testAttribute "add-shared-secret" + , Route.Path.href <| + Route.Path.SecretsEngine_SharedOrg_Team_Add { engine = route.params.engine, org = route.params.org, team = route.params.team } + ] + [ text "Add Shared Secret" + , FeatherIcons.plus + |> FeatherIcons.withSize 18 + |> FeatherIcons.toHtml [ Svg.Attributes.class "button-icon" ] + ] + , Components.Pager.view model.pager Components.Pager.defaultLabels GotoPage + ] + } + ] ] } diff --git a/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Add.elm b/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Add.elm index 059a5e551..f24c73c16 100644 --- a/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Add.elm +++ b/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Add.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Secrets.Engine_.Shared.Org_.Team_.Add exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.SecretForm import Effect exposing (Effect) -import Html exposing (div, h2, text) +import Html exposing (div, h2, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -44,17 +46,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String, team : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.team, Nothing ) - , ( "Secrets", Just <| Route.Path.SecretsEngine_SharedOrg_Team_ { engine = route.params.engine, org = route.params.org, team = route.params.team } ) - , ( "Add", Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -226,74 +218,92 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, team : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.team, Nothing ) + , ( "Shared Secrets", Just <| Route.Path.SecretsEngine_SharedOrg_Team_ { engine = route.params.engine, org = route.params.org, team = route.params.team } ) + , ( "Add", Nothing ) + ] + in { title = "Add Secret" , body = - [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase "add shared secret" ] - , div [ class "secret-form" ] - [ Components.Form.viewInput - { title = Just "Team" - , subtitle = Nothing - , id_ = "team" - , val = model.team - , placeholder_ = "GitHub Team" - , classList_ = [ ( "secret-team", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = TeamOnInput - , disabled_ = False - } - , Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = model.name - , placeholder_ = "Secret Name" - , classList_ = [ ( "secret-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = NameOnInput - , disabled_ = False - } - , Components.Form.viewTextarea - { title = Just "Value" - , subtitle = Nothing - , id_ = "value" - , val = model.value - , placeholder_ = "secret-value" - , classList_ = [ ( "secret-value", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ValueOnInput - , disabled_ = False - } - , Components.SecretForm.viewAllowEventsSelect - shared - { msg = AllowEventsUpdate - , allowEvents = model.allowEvents - , disabled_ = False - } - , Components.SecretForm.viewImagesInput - { onInput_ = ImageOnInput - , addImage = AddImage - , removeImage = RemoveImage - , images = model.images - , imageValue = model.image - , disabled_ = False - } - , Components.SecretForm.viewAllowCommandsInput - { msg = AllowCommandsOnClick - , value = model.allowCommand - , disabled_ = False - } - , Components.SecretForm.viewHelp shared.velaDocsURL - , Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] - , disabled_ = False - } + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase "add shared secret" ] + , div [ class "secret-form" ] + [ Components.Form.viewInput + { title = Just "Team" + , subtitle = Nothing + , id_ = "team" + , val = model.team + , placeholder_ = "GitHub Team" + , classList_ = [ ( "secret-team", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = TeamOnInput + , disabled_ = False + } + , Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = model.name + , placeholder_ = "Secret Name" + , classList_ = [ ( "secret-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = NameOnInput + , disabled_ = False + } + , Components.Form.viewTextarea + { title = Just "Value" + , subtitle = Nothing + , id_ = "value" + , val = model.value + , placeholder_ = "secret-value" + , classList_ = [ ( "secret-value", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ValueOnInput + , disabled_ = False + } + , Components.SecretForm.viewAllowEventsSelect + shared + { msg = AllowEventsUpdate + , allowEvents = model.allowEvents + , disabled_ = False + } + , Components.SecretForm.viewImagesInput + { onInput_ = ImageOnInput + , addImage = AddImage + , removeImage = RemoveImage + , images = model.images + , imageValue = model.image + , disabled_ = False + } + , Components.SecretForm.viewAllowCommandsInput + { msg = AllowCommandsOnClick + , value = model.allowCommand + , disabled_ = False + } + , Components.SecretForm.viewHelp shared.velaDocsURL + , Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" + , classList_ = [] + , disabled_ = False + , id_ = "submit" + } + ] ] ] ] diff --git a/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Edit_.elm b/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Edit_.elm index c2a5e09eb..f8c0b5233 100644 --- a/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Edit_.elm +++ b/src/elm/Pages/Secrets/Engine_/Shared/Org_/Team_/Edit_.elm @@ -6,10 +6,12 @@ SPDX-License-Identifier: Apache-2.0 module Pages.Secrets.Engine_.Shared.Org_.Team_.Edit_ exposing (Model, Msg, page, view) import Auth +import Components.Crumbs import Components.Form +import Components.Nav import Components.SecretForm import Effect exposing (Effect) -import Html exposing (div, h2, text) +import Html exposing (div, h2, main_, text) import Html.Attributes exposing (class) import Http import Http.Detailed @@ -44,18 +46,7 @@ page user shared route = toLayout : Auth.User -> Route { engine : String, org : String, team : String, name : String } -> Model -> Layouts.Layout Msg toLayout user route model = Layouts.Default - { navButtons = [] - , utilButtons = [] - , helpCommands = [] - , crumbs = - [ ( "Overview", Just Route.Path.Home ) - , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) - , ( route.params.team, Nothing ) - , ( "Secrets", Just <| Route.Path.SecretsEngine_SharedOrg_Team_ { engine = route.params.engine, org = route.params.org, team = route.params.team } ) - , ( "Edit", Nothing ) - , ( route.params.name, Nothing ) - ] - , repo = Nothing + { helpCommands = [] } @@ -294,88 +285,110 @@ subscriptions model = view : Shared.Model -> Route { engine : String, org : String, team : String, name : String } -> Model -> View Msg view shared route model = + let + crumbs = + [ ( "Overview", Just Route.Path.Home ) + , ( route.params.org, Just <| Route.Path.Org_ { org = route.params.org } ) + , ( route.params.team, Nothing ) + , ( "Shared Secrets", Just <| Route.Path.SecretsEngine_SharedOrg_Team_ { engine = route.params.engine, org = route.params.org, team = route.params.team } ) + , ( "Edit", Nothing ) + , ( route.params.name, Nothing ) + ] + in { title = "Edit Secret" , body = - [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] - [ div [] - [ h2 [] [ text <| String.Extra.toTitleCase "edit shared secret" ] - , div [ class "secret-form" ] - [ Components.Form.viewInput - { title = Just "Name" - , subtitle = Nothing - , id_ = "name" - , val = RemoteData.unwrap "" .name model.secret - , placeholder_ = "loading..." - , classList_ = [ ( "secret-name", True ) ] - , rows_ = Nothing - , wrap_ = Nothing - , msg = \_ -> NoOp - , disabled_ = True - } - , Components.Form.viewTextarea - { title = Just "Value" - , subtitle = Nothing - , id_ = "value" - , val = model.value - , placeholder_ = RemoteData.unwrap "loading..." (\_ -> "") model.secret - , classList_ = [ ( "secret-value", True ) ] - , rows_ = Just 2 - , wrap_ = Just "soft" - , msg = ValueOnInput - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewAllowEventsSelect - shared - { msg = AllowEventsUpdate - , allowEvents = model.allowEvents - , disabled_ = False - } - , Components.SecretForm.viewImagesInput - { onInput_ = ImageOnInput - , addImage = AddImage - , removeImage = RemoveImage - , images = model.images - , imageValue = model.image - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewAllowCommandsInput - { msg = AllowCommandsOnClick - , value = model.allowCommand - , disabled_ = not <| RemoteData.isSuccess model.secret - } - , Components.SecretForm.viewHelp shared.velaDocsURL - , div [ class "buttons" ] - [ Components.Form.viewButton - { msg = SubmitForm - , text_ = "Submit" - , classList_ = [] + [ Components.Nav.view + shared + route + { buttons = [] + , crumbs = Components.Crumbs.view route.path crumbs + } + , main_ [ class "content-wrap" ] + [ div [ class "manage-secret", Util.testAttribute "manage-secret" ] + [ div [] + [ h2 [] [ text <| String.Extra.toTitleCase "edit shared secret" ] + , div [ class "secret-form" ] + [ Components.Form.viewInput + { title = Just "Name" + , subtitle = Nothing + , id_ = "name" + , val = RemoteData.unwrap "" .name model.secret + , placeholder_ = "loading..." + , classList_ = [ ( "secret-name", True ) ] + , rows_ = Nothing + , wrap_ = Nothing + , msg = \_ -> NoOp + , disabled_ = True + } + , Components.Form.viewTextarea + { title = Just "Value" + , subtitle = Nothing + , id_ = "value" + , val = model.value + , placeholder_ = RemoteData.unwrap "loading..." (\_ -> "") model.secret + , classList_ = [ ( "secret-value", True ) ] + , rows_ = Just 2 + , wrap_ = Just "soft" + , msg = ValueOnInput , disabled_ = not <| RemoteData.isSuccess model.secret } - , if not model.confirmingDelete then - Components.Form.viewButton - { msg = ClickDelete - , text_ = "Delete" - , classList_ = [] - , disabled_ = not <| RemoteData.isSuccess model.secret - } - - else - Components.Form.viewButton - { msg = CancelDelete - , text_ = "Cancel" + , Components.SecretForm.viewAllowEventsSelect + shared + { msg = AllowEventsUpdate + , allowEvents = model.allowEvents + , disabled_ = False + } + , Components.SecretForm.viewImagesInput + { onInput_ = ImageOnInput + , addImage = AddImage + , removeImage = RemoveImage + , images = model.images + , imageValue = model.image + , disabled_ = not <| RemoteData.isSuccess model.secret + } + , Components.SecretForm.viewAllowCommandsInput + { msg = AllowCommandsOnClick + , value = model.allowCommand + , disabled_ = not <| RemoteData.isSuccess model.secret + } + , Components.SecretForm.viewHelp shared.velaDocsURL + , div [ class "buttons" ] + [ Components.Form.viewButton + { msg = SubmitForm + , text_ = "Submit" , classList_ = [] , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "submit" } - , if model.confirmingDelete then - Components.Form.viewButton - { msg = ConfirmDelete - , text_ = "Confirm" - , classList_ = [ ( "-secret-delete-confirm", True ) ] - , disabled_ = not <| RemoteData.isSuccess model.secret - } - - else - text "" + , if not model.confirmingDelete then + Components.Form.viewButton + { msg = ClickDelete + , text_ = "Delete" + , classList_ = [] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete" + } + + else + Components.Form.viewButton + { msg = CancelDelete + , text_ = "Cancel" + , classList_ = [] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete-cancel" + } + , if model.confirmingDelete then + Components.Form.viewButton + { msg = ConfirmDelete + , text_ = "Confirm" + , classList_ = [ ( "-secret-delete-confirm", True ) ] + , disabled_ = not <| RemoteData.isSuccess model.secret + , id_ = "delete-confirm" + } + + else + text "" + ] ] ] ] diff --git a/src/elm/Route/Path.elm b/src/elm/Route/Path.elm index 96ba74d07..a5d0ca30a 100644 --- a/src/elm/Route/Path.elm +++ b/src/elm/Route/Path.elm @@ -22,12 +22,13 @@ type Path | Org_Builds { org : String } | Org_Repo_ { org : String, repo : String } | Org_Repo_Pulls { org : String, repo : String } + | Org_Repo_Tags { org : String, repo : String } | Org_Repo_Deployments { org : String, repo : String } | Org_Repo_DeploymentsAdd { org : String, repo : String } | Org_Repo_Schedules { org : String, repo : String } | Org_Repo_SchedulesAdd { org : String, repo : String } | Org_Repo_SchedulesEdit_ { org : String, repo : String, name : String } - | Org_Repo_Audit { org : String, repo : String } + | Org_Repo_Hooks { org : String, repo : String } | Org_Repo_Settings { org : String, repo : String } | Org_Repo_Build_ { org : String, repo : String, buildNumber : String } | Org_Repo_Build_Services { org : String, repo : String, buildNumber : String } @@ -134,8 +135,8 @@ fromString urlPath = } |> Just - org :: repo :: "audit" :: [] -> - Org_Repo_Audit + org :: repo :: "hooks" :: [] -> + Org_Repo_Hooks { org = org , repo = repo } @@ -155,6 +156,13 @@ fromString urlPath = } |> Just + org :: repo :: "tags" :: [] -> + Org_Repo_Tags + { org = org + , repo = repo + } + |> Just + org :: repo :: buildNumber :: [] -> Org_Repo_Build_ { org = org @@ -304,6 +312,9 @@ toString path = Org_Repo_Pulls params -> [ params.org, params.repo, "?event=pull_request" ] + Org_Repo_Tags params -> + [ params.org, params.repo, "?event=tag" ] + Org_Repo_Deployments params -> [ params.org, params.repo, "deployments" ] @@ -319,8 +330,8 @@ toString path = Org_Repo_SchedulesEdit_ params -> [ params.org, params.repo, "schedules", params.name ] - Org_Repo_Audit params -> - [ params.org, params.repo, "audit" ] + Org_Repo_Hooks params -> + [ params.org, params.repo, "hooks" ] Org_Repo_Settings params -> [ params.org, params.repo, "settings" ] diff --git a/src/elm/Shared.elm b/src/elm/Shared.elm index 1f782453b..a9fb5ec51 100644 --- a/src/elm/Shared.elm +++ b/src/elm/Shared.elm @@ -516,71 +516,6 @@ update route msg model = , Effect.handleHttpError { httpError = error } ) - -- BUILD - Shared.Msg.RestartBuild options -> - let - _ = - Debug.log "restart a build" options - in - ( model - , Api.try - (Shared.Msg.RestartBuildResponse options) - (Api.Operations.restartBuild model.velaAPIBaseURL model.session options) - |> Effect.sendCmd - ) - - Shared.Msg.RestartBuildResponse options response -> - case response of - Ok ( _, build ) -> - let - restartedBuild = - "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] - - newBuildNumber = - String.fromInt <| build.number - - newBuild = - String.join "/" [ "", options.org, options.repo, newBuildNumber ] - - -- todo: create new build link, add to toastie, refresh builds - in - ( model - , Effect.addAlertSuccess { content = restartedBuild ++ " restarted.", addToastIfUnique = True } - ) - - Err error -> - ( model - , Effect.handleHttpError { httpError = error } - ) - - Shared.Msg.CancelBuild options -> - let - _ = - Debug.log "cancel a build" options - in - ( model - , Api.try - (Shared.Msg.CancelBuildResponse options) - (Api.Operations.cancelBuild model.velaAPIBaseURL model.session options) - |> Effect.sendCmd - ) - - Shared.Msg.CancelBuildResponse options response -> - case response of - Ok _ -> - let - canceledBuild = - "Build " ++ String.join "/" [ options.org, options.repo, options.buildNumber ] - in - ( model - , Effect.addAlertSuccess { content = canceledBuild ++ " canceled.", addToastIfUnique = True } - ) - - Err error -> - ( model - , Effect.handleHttpError { httpError = error } - ) - -- HOOKS Shared.Msg.GetRepoHooks options -> ( model diff --git a/src/elm/Shared/Msg.elm b/src/elm/Shared/Msg.elm index 80f4e6cdf..06e7f6a96 100644 --- a/src/elm/Shared/Msg.elm +++ b/src/elm/Shared/Msg.elm @@ -49,11 +49,6 @@ type Msg -- BUILDS | GetRepoBuilds { org : String, repo : String, pageNumber : Maybe Int, perPage : Maybe Int, maybeEvent : Maybe String } | GetRepoBuildsResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Build )) - -- BUILD - | RestartBuild { org : String, repo : String, buildNumber : String } - | RestartBuildResponse { org : String, repo : String, buildNumber : String } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) - | CancelBuild { org : String, repo : String, buildNumber : String } - | CancelBuildResponse { org : String, repo : String, buildNumber : String } (Result (Http.Detailed.Error String) ( Http.Metadata, Vela.Build )) -- HOOKS | GetRepoHooks { org : String, repo : String, pageNumber : Maybe Int, perPage : Maybe Int, maybeEvent : Maybe String } | GetRepoHooksResponse (Result (Http.Detailed.Error String) ( Http.Metadata, List Vela.Hook )) diff --git a/src/elm/Utils/Helpers.elm b/src/elm/Utils/Helpers.elm index 3efd92da4..f09af9904 100644 --- a/src/elm/Utils/Helpers.elm +++ b/src/elm/Utils/Helpers.elm @@ -532,18 +532,19 @@ base64Decode inStr = {-| pageToString : small helper to turn page number to a string to display in crumbs -} -pageToString : Maybe Int -> String +pageToString : Maybe String -> String pageToString maybePage = - case maybePage of - Nothing -> - "" - - Just num -> - if num > 1 then - " (page " ++ String.fromInt num ++ ")" + maybePage + |> Maybe.map String.toInt + |> Maybe.withDefault Nothing + |> Maybe.Extra.unwrap "" + (\num -> + if num > 1 then + " (page " ++ String.fromInt num ++ ")" - else - "" + else + "" + ) {-| buildRefURL : drops '.git' off the clone url and concatenates tree + ref diff --git a/src/scss/_pipelines.scss b/src/scss/_pipelines.scss index 8ad32698d..12c0a2fb7 100644 --- a/src/scss/_pipelines.scss +++ b/src/scss/_pipelines.scss @@ -154,7 +154,7 @@ .logs-table { padding-top: 0; - padding-bottom: 1rem; + padding-bottom: 0; } .logs-table.-error { @@ -175,4 +175,8 @@ .logs-table .line ~ .line { margin-top: 0; } + + .logs-table .logs { + padding-bottom: 1rem; + } } diff --git a/src/scss/_table.scss b/src/scss/_table.scss index ff7b6e729..2df4b7b91 100644 --- a/src/scss/_table.scss +++ b/src/scss/_table.scss @@ -99,7 +99,7 @@ td .key .list-item { } .table-base .source-id .list-item { - max-width: 22rem; + max-width: 24rem; } .table-base tr.-success {