Skip to content

build(dashboard): vendor cozystack-ui into the monorepo - #2963

Merged
myasnikovdaniil merged 6 commits into
mainfrom
feat/vendor-cozystack-ui
Jun 26, 2026
Merged

myasnikovdaniil merged 6 commits into
mainfrom
feat/vendor-cozystack-ui

Conversation

@myasnikovdaniil

Copy link
Copy Markdown
Contributor

What this PR does

Moves the cozystack-ui console SPA into this monorepo so the UI versions and ships in lockstep with the platform — removing the cross-repo digest-bump PRs and the version drift between the two repositories.

The source is vendored at packages/system/dashboard/images/console/ via git subtree, preserving full upstream history. This mirrors the existing in-tree images/token-proxy/ pattern, and .helmignore already excludes images/*/* so the source never enters the Helm chart.

Changes

  • Source: cozystack-ui main merged under packages/system/dashboard/images/console/ (history preserved as a merge parent).
  • Build: packages/system/dashboard/Makefile builds the console image from the in-tree context instead of cloning the external repo at build time. Dropped clone-console/clean-console and the CONSOLE_REPO/CONSOLE_BRANCH variables. The built digest is still pinned into values.yaml by update-values-console — now in the same commit as the source change rather than a separate follow-up PR.
  • CI: new .github/workflows/ui-test.yaml runs pnpm typecheck + unit tests, path-gated to images/console/**. The container image continues to build via make build.

Verification

  • pnpm install --frozen-lockfile → pnpm typecheck clean → production build produces dist/.
  • 313 unit tests pass.
  • helm template renders; console + token-proxy image references unchanged.

Follow-ups (after merge)

  • Archive the standalone cozystack-ui repository and redirect contributions here.
  • Update remaining documentation pointers that reference the old repo path.

Screenshots

N/A — no UI behavior change. This relocates the UI source and rewires the build; the shipped image and digest are unchanged.

Release note

build(dashboard): vendor the cozystack-ui console into the monorepo and build it from in-tree source; the shipped image and digest are unchanged for cluster operators

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 199 files, which is 49 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 624c0a3e-d903-483d-897f-e0dd60407fc6

📥 Commits

Reviewing files that changed from the base of the PR and between 82b8c46 and ea0c0ab.

⛔ Files ignored due to path filters (3)
  • packages/system/dashboard/images/console/apps/console/public/favicon.svg is excluded by !**/*.svg
  • packages/system/dashboard/images/console/packages/ui/src/assets/logo.svg is excluded by !**/*.svg
  • packages/system/dashboard/images/console/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (199)
  • .github/workflows/ui-test.yaml
  • packages/system/dashboard/.gitignore
  • packages/system/dashboard/Makefile
  • packages/system/dashboard/images/console/.gitignore
  • packages/system/dashboard/images/console/.npmrc
  • packages/system/dashboard/images/console/CLAUDE.md
  • packages/system/dashboard/images/console/Containerfile
  • packages/system/dashboard/images/console/README.md
  • packages/system/dashboard/images/console/apps/console/index.html
  • packages/system/dashboard/images/console/apps/console/package.json
  • packages/system/dashboard/images/console/apps/console/src/App.tsx
  • packages/system/dashboard/images/console/apps/console/src/__tests__/k8s-client/client.subresource.test.ts
  • packages/system/dashboard/images/console/apps/console/src/__tests__/k8s-client/provider.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/__tests__/k8s-client/useApiGroupAvailable.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/__tests__/k8s-client/useK8sSubresource.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/__tests__/k8s-client/useSelfSubjectAccessReview.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesEditor.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesField.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesField.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/AdditionalPropertiesWidget.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/AppCard.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/Breadcrumb.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/CustomObjectFieldTemplate.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/DynamicOptionsWidget.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/DynamicOptionsWidget.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/InstanceCard.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/KeyValueEditor.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/QuotaDisplay.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/ResourceQuotasField.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/SchemaForm.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/SchemaForm.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/SensitiveStringWidget.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/SensitiveStringWidget.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/SourceField.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/SourceWidget.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/WorkloadCell.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/YamlEditor.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterStorageSection.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterStorageSection.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterUsageAggregates.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterUsageAggregates.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterUsageGauges.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterUsageTable.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/cluster-usage/ClusterUsageTable.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/command-palette/command-palette-provider.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/command-palette/command-palette.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/command-palette/types.ts
  • packages/system/dashboard/images/console/apps/console/src/components/command-palette/use-command-items.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/command-palette/use-hotkey.ts
  • packages/system/dashboard/images/console/apps/console/src/components/command-palette/use-keyboard-nav.ts
  • packages/system/dashboard/images/console/apps/console/src/components/rjsf-templates.tsx
  • packages/system/dashboard/images/console/apps/console/src/components/schema-form.css
  • packages/system/dashboard/images/console/apps/console/src/components/ui/dialog.tsx
  • packages/system/dashboard/images/console/apps/console/src/hooks/use-is-mac.ts
  • packages/system/dashboard/images/console/apps/console/src/hooks/useBackupClassAdminAccess.ts
  • packages/system/dashboard/images/console/apps/console/src/hooks/useClusterUsageAccess.ts
  • packages/system/dashboard/images/console/apps/console/src/hooks/useClusterUsageData.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/hooks/useClusterUsageData.tsx
  • packages/system/dashboard/images/console/apps/console/src/index.css
  • packages/system/dashboard/images/console/apps/console/src/lib/app-definitions.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/app-definitions.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/app-resource.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/backup-utils.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/aggregate.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/aggregate.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/extended-resources.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/extended-resources.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/per-node.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/per-node.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/cluster-usage/types.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/config.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/constants.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/crd-option-sources.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/crd-option-sources.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/dynamic-options.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/dynamic-options.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/humanize.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/immutable-paths.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/immutable-paths.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/k8s-quantity.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/k8s-quantity.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/keys-order.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/keys-order.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/labels.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/monaco-setup.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/monaco-setup.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/prepare-update.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/prepare-update.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/sensitive-fields.test.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/sensitive-fields.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/sidebar-icons.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/lib/sidebar-icons.tsx
  • packages/system/dashboard/images/console/apps/console/src/lib/status.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/tenant-context.tsx
  • packages/system/dashboard/images/console/apps/console/src/lib/use-crd-schema.ts
  • packages/system/dashboard/images/console/apps/console/src/lib/workload.ts
  • packages/system/dashboard/images/console/apps/console/src/main.tsx
  • packages/system/dashboard/images/console/apps/console/src/novnc.d.ts
  • packages/system/dashboard/images/console/apps/console/src/routes/AdminPage.routing.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/AdminPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ApplicationListPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ApplicationOrderPage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ApplicationOrderPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupClassAdminGuard.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupClassAdminGuard.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupClassCreatePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupClassDetailPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupClassEditPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupClassListPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupCreatePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupCreatePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupJobCreatePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupJobCreatePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupPlanCreatePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupPlanCreatePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupResourceEditPage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupResourceEditPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupResourceListPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupRestoreJobCreatePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/BackupRestoreJobCreatePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/CapacityAdminGuard.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/CapacityAdminGuard.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ClusterUsagePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ClusterUsagePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ClusterUsageResourcePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ClusterUsageResourcePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ConsoleOverview.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ConsolePage.routing.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ConsolePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ExternalIpsPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/InfoRedirect.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/MarketplaceHome.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/MarketplaceList.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/MarketplacePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/ModulesPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/NodesPage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/NodesPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/StorageClassUsagePage.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/StorageClassUsagePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/StoragePage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/TenantsPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/ApplicationDetailPage.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/ApplicationEditRoute.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/EventsTab.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/EventsTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/IngressesTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/OverviewTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/SecretsTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/ServicesTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/VMPowerControls.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/VMPowerControls.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/VncTab.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/VncTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/WorkloadsTab.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/detail/tabs.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/sidebar-sections.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/routes/sidebar-sections.tsx
  • packages/system/dashboard/images/console/apps/console/src/test-utils/fixtures/node-metrics.ts
  • packages/system/dashboard/images/console/apps/console/src/test-utils/fixtures/nodes.ts
  • packages/system/dashboard/images/console/apps/console/src/test-utils/fixtures/pods.ts
  • packages/system/dashboard/images/console/apps/console/src/test-utils/mock-k8s-client.ts
  • packages/system/dashboard/images/console/apps/console/src/test-utils/render.test.tsx
  • packages/system/dashboard/images/console/apps/console/src/test-utils/render.tsx
  • packages/system/dashboard/images/console/apps/console/test/setup.ts
  • packages/system/dashboard/images/console/apps/console/tsconfig.json
  • packages/system/dashboard/images/console/apps/console/vite.config.ts
  • packages/system/dashboard/images/console/apps/console/vitest.config.ts
  • packages/system/dashboard/images/console/eslint.config.js
  • packages/system/dashboard/images/console/package.json
  • packages/system/dashboard/images/console/packages/k8s-client/package.json
  • packages/system/dashboard/images/console/packages/k8s-client/src/client.ts
  • packages/system/dashboard/images/console/packages/k8s-client/src/hooks.ts
  • packages/system/dashboard/images/console/packages/k8s-client/src/index.ts
  • packages/system/dashboard/images/console/packages/k8s-client/src/provider.tsx
  • packages/system/dashboard/images/console/packages/k8s-client/src/useApiGroupAvailable.ts
  • packages/system/dashboard/images/console/packages/k8s-client/src/useSelfSubjectAccessReview.ts
  • packages/system/dashboard/images/console/packages/k8s-client/tsconfig.json
  • packages/system/dashboard/images/console/packages/types/package.json
  • packages/system/dashboard/images/console/packages/types/src/cozystack.ts
  • packages/system/dashboard/images/console/packages/types/src/index.ts
  • packages/system/dashboard/images/console/packages/types/tsconfig.json
  • packages/system/dashboard/images/console/packages/ui/package.json
  • packages/system/dashboard/images/console/packages/ui/src/assets.d.ts
  • packages/system/dashboard/images/console/packages/ui/src/components/Dropdown.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/Logo.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/Section.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/Spinner.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/StatusBadge.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/layout/AppShell.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/layout/Header.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/layout/Sidebar.tsx
  • packages/system/dashboard/images/console/packages/ui/src/components/ui/button.tsx
  • packages/system/dashboard/images/console/packages/ui/src/index.css
  • packages/system/dashboard/images/console/packages/ui/src/index.ts
  • packages/system/dashboard/images/console/packages/ui/src/lib/utils.ts
  • packages/system/dashboard/images/console/packages/ui/tsconfig.json
  • packages/system/dashboard/images/console/pnpm-workspace.yaml
  • packages/system/dashboard/images/console/tsconfig.base.json
  • packages/system/dashboard/images/console/tsconfig.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vendor-cozystack-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added area/dashboard Issues or PRs related to the dashboard / UI size/XXL This PR changes 1000+ lines, ignoring generated files labels Jun 22, 2026
@dosubot dosubot Bot added area/build Issues or PRs related to image build infrastructure, multi-arch support kind/feature Categorizes issue or PR as related to a new feature labels Jun 22, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request consolidates the cozystack-ui console into the main monorepo. By vendoring the source code directly, we eliminate version drift and the need for cross-repository pull requests when updating the UI. The build process has been updated to use the in-tree source, and new CI automation ensures the console remains tested and verified within the monorepo environment.

Highlights

  • Vendoring cozystack-ui: Moved the cozystack-ui console SPA into the monorepo under packages/system/dashboard/images/console/ to ensure UI versions ship in lockstep with the platform.
  • Build System Update: Updated packages/system/dashboard/Makefile to build the console image from the in-tree source, removing external cloning steps and associated variables.
  • CI/CD Integration: Added a new GitHub Actions workflow ui-test.yaml to handle pnpm typechecking and unit tests for the console, triggered by changes in the console directory.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Ignored Files
  • Ignored by pattern: **/images/*.json (11)
    • packages/system/dashboard/images/console/apps/console/package.json
    • packages/system/dashboard/images/console/apps/console/tsconfig.json
    • packages/system/dashboard/images/console/package.json
    • packages/system/dashboard/images/console/packages/k8s-client/package.json
    • packages/system/dashboard/images/console/packages/k8s-client/tsconfig.json
    • packages/system/dashboard/images/console/packages/types/package.json
    • packages/system/dashboard/images/console/packages/types/tsconfig.json
    • packages/system/dashboard/images/console/packages/ui/package.json
    • packages/system/dashboard/images/console/packages/ui/tsconfig.json
    • packages/system/dashboard/images/console/tsconfig.base.json
    • packages/system/dashboard/images/console/tsconfig.json
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/ui-test.yaml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@myasnikovdaniil myasnikovdaniil self-assigned this Jun 22, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request integrates the Cozystack dashboard UI source code directly into the repository under packages/system/dashboard/images/console and updates the Makefile to build from this in-tree context. The review feedback identifies several critical issues: missing enabled gates on useK8sList queries in DynamicOptionsWidget and ExternalIpsPage that can cause 403 errors on initial load, a potential runtime crash in ResourceQuotasField when parsing numeric quota values, and a potential crash in SecretsTab when attempting to copy to the clipboard in non-secure HTTP contexts.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +43 to +48
const { data: optionList, isLoading } = useK8sList<OptionObject>({
apiGroup: "core.cozystack.io",
apiVersion: "v1alpha1",
plural: "options",
namespace: tenantNamespace ?? undefined,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The useK8sList query is missing the enabled: !!tenantNamespace option. When tenantNamespace is null (e.g., during initial load), it will trigger a cluster-wide query to /apis/core.cozystack.io/v1alpha1/options, which will fail with a 403 Forbidden error for non-admin users. Gating the query with enabled: !!tenantNamespace prevents this unnecessary and failing request.

Suggested change
const { data: optionList, isLoading } = useK8sList<OptionObject>({
apiGroup: "core.cozystack.io",
apiVersion: "v1alpha1",
plural: "options",
namespace: tenantNamespace ?? undefined,
})
const { data: optionList, isLoading } = useK8sList<OptionObject>(
{
apiGroup: "core.cozystack.io",
apiVersion: "v1alpha1",
plural: "options",
namespace: tenantNamespace ?? undefined,
},
{ enabled: !!tenantNamespace },
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — gated the query with enabled: !!tenantNamespace so it no longer issues a cluster-wide request to the core.cozystack.io options endpoint while the tenant namespace is still resolving. Fixed in d4b0b52.

Comment on lines +23 to +31
*/
export function ExternalIpsPage() {
const { tenantNamespace } = useTenantContext()
const { data, isLoading } = useK8sList<K8sResource<ServiceSpec, ServiceStatus>>(
{
apiGroup: "",
apiVersion: "v1",
plural: "services",
namespace: tenantNamespace ?? undefined,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The useK8sList query is missing the enabled: !!tenantNamespace option. When tenantNamespace is null, it will trigger a cluster-wide query to /api/v1/services, which will fail with a 403 Forbidden error for non-admin users. Gating the query with enabled: !!tenantNamespace prevents this unnecessary and failing request.

Suggested change
*/
export function ExternalIpsPage() {
const { tenantNamespace } = useTenantContext()
const { data, isLoading } = useK8sList<K8sResource<ServiceSpec, ServiceStatus>>(
{
apiGroup: "",
apiVersion: "v1",
plural: "services",
namespace: tenantNamespace ?? undefined,
const { data, isLoading } = useK8sList<K8sResource<ServiceSpec, ServiceStatus>>(
{
apiGroup: "",
apiVersion: "v1",
plural: "services",
namespace: tenantNamespace ?? undefined,
},
{ fieldSelector: "spec.type=LoadBalancer", enabled: !!tenantNamespace },
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — added enabled: !!tenantNamespace to the services query so it won't hit /api/v1/services cluster-wide before the tenant namespace is known. Fixed in fbbcc2e.

Comment on lines +28 to +37
function parseSize(val: string, units: readonly string[]): { num: string; unit: string } {
// Match longest suffix first to avoid partial matches
const sorted = [...units].sort((a, b) => b.length - a.length)
for (const u of sorted) {
if (val.endsWith(u)) return { num: val.slice(0, -u.length), unit: u }
}
// Strip any trailing alphabetic chars so we don't corrupt data on save
const numericPart = val.replace(/[A-Za-z]+$/, "")
return { num: numericPart || val, unit: units[0] ?? "" }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

parseSize expects val to be a string and calls val.endsWith(u). However, if val is a number (e.g., cpu: 10 in user-authored YAML), it will throw TypeError: val.endsWith is not a function and crash the page when loading the edit form. Coercing val to a string first prevents this runtime crash.

Suggested change
function parseSize(val: string, units: readonly string[]): { num: string; unit: string } {
// Match longest suffix first to avoid partial matches
const sorted = [...units].sort((a, b) => b.length - a.length)
for (const u of sorted) {
if (val.endsWith(u)) return { num: val.slice(0, -u.length), unit: u }
}
// Strip any trailing alphabetic chars so we don't corrupt data on save
const numericPart = val.replace(/[A-Za-z]+$/, "")
return { num: numericPart || val, unit: units[0] ?? "" }
}
function parseSize(val: string | number, units: readonly string[]): { num: string; unit: string } {
const valStr = String(val)
// Match longest suffix first to avoid partial matches
const sorted = [...units].sort((a, b) => b.length - a.length)
for (const u of sorted) {
if (valStr.endsWith(u)) return { num: valStr.slice(0, -u.length), unit: u }
}
// Strip any trailing alphabetic chars so we don't corrupt data on save
const numericPart = valStr.replace(/[A-Za-z]+$/, "")
return { num: numericPart || valStr, unit: units[0] ?? "" }
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right — parseSize now coerces val to a string first, so a numeric quota from user-authored YAML (e.g. cpu: 10) no longer throws val.endsWith is not a function. Fixed in 83a8479.

Comment on lines +97 to +105
<button
type="button"
disabled={!revealed}
onClick={() => navigator.clipboard.writeText(fullValue)}
className="rounded p-1 text-slate-500 hover:bg-slate-100 disabled:opacity-30"
title="Copy"
>
<Copy className="size-3.5" />
</button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

navigator.clipboard.writeText is called without checking if navigator.clipboard is defined. In non-secure contexts (HTTP), navigator.clipboard is undefined, and clicking the copy button will crash the app with a TypeError. We should guard the button's disabled state and the click handler to prevent this crash.

Suggested change
<button
type="button"
disabled={!revealed}
onClick={() => navigator.clipboard.writeText(fullValue)}
className="rounded p-1 text-slate-500 hover:bg-slate-100 disabled:opacity-30"
title="Copy"
>
<Copy className="size-3.5" />
</button>
<button
type="button"
disabled={!revealed || !navigator.clipboard}
onClick={() => navigator.clipboard?.writeText(fullValue)}
className="rounded p-1 text-slate-500 hover:bg-slate-100 disabled:opacity-30"
title="Copy"
>
<Copy className="size-3.5" />
</button>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a guard: the copy button is now disabled and the handler is a no-op when navigator.clipboard is unavailable (plain-HTTP contexts), so it no longer throws. Fixed in ea0c0ab.

myasnikovdaniil and others added 6 commits June 23, 2026 12:19
Move the console SPA source into the monorepo at
packages/system/dashboard/images/console so the UI versions and ships in
lockstep with the platform, removing the cross-repo digest-bump PRs and the
version drift between the two repositories.

Snapshot of cozystack/cozystack-ui main at 8ef45a4; full history remains in
that repository (to be archived). The Makefile now builds the console image
from this in-tree context instead of cloning the external repo at build time:
dropped clone-console/clean-console and the CONSOLE_REPO/CONSOLE_BRANCH
variables; the built digest is still pinned into values.yaml by
update-values-console. .helmignore already excludes images/*/* so the source
never enters the Helm chart.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Mirrors the former cozystack-ui test workflow: pnpm typecheck + unit tests,
gated to changes under packages/system/dashboard/images/console.

pnpm/action-setup reads the pnpm version from package_json_file (the console
workspace manifest) since the monorepo root has no package.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on DynamicOptionsWidget.tsx:
gate the useK8sList options query with enabled: !!tenantNamespace so it does
not fire a cluster-wide request to core.cozystack.io options (403 for
non-admin tenants) while the tenant namespace is still resolving.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on ExternalIpsPage.tsx: add
enabled: !!tenantNamespace to the services useK8sList so it does not issue a
cluster-wide /api/v1/services request (403 for non-admin tenants) before the
tenant namespace is known.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on ResourceQuotasField.tsx:
parseSize coerces val to a string first so a numeric quota from user-authored
YAML (e.g. cpu: 10) no longer crashes the edit form with "val.endsWith is not
a function".

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Address review feedback from gemini-code-assist on SecretsTab.tsx: guard the
copy button on navigator.clipboard so it is disabled and a no-op when the API
is unavailable (plain-HTTP contexts), instead of throwing a TypeError.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@myasnikovdaniil
myasnikovdaniil force-pushed the feat/vendor-cozystack-ui branch from 1c1f0b7 to ea0c0ab Compare June 23, 2026 07:24
@myasnikovdaniil

Copy link
Copy Markdown
Contributor Author

Re: the force-push — this branch originally vendored the UI with git subtree, preserving the full cozystack-ui history (178 commits). Those commits are authored by multiple contributors and lack Signed-off-by trailers, so the DCO check failed — and those trailers can't be added retroactively on other authors' behalf. I've squashed the import into one signed-off snapshot commit (source: cozystack-ui 8ef45a4) plus the build/CI and review-fix commits, so every commit in the PR is now signed off. The complete upstream history remains in the cozystack-ui repository, which will be archived after this lands.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the relocation is complete and coherent: the build, CI, and Helm packaging all wire to the in-tree source, no secrets or build artifacts are committed, and the shipped image/digest are unchanged. The findings below are pre-existing upstream UI behaviors carried in verbatim; none block the move.

Business context: moves the cozystack-ui console SPA into the monorepo under packages/system/dashboard/images/console/ so the UI ships and versions in lockstep with the platform, replacing the build-time git clone of the external repo.

Vendoring-correctness checklist (all pass):

  • Import is complete and coherent — Containerfile, pnpm-workspace.yaml, pnpm-lock.yaml, and all four workspace packages (apps/console, packages/{ui,types,k8s-client}) are present, and the build filter @cozystack/console resolves to apps/console/package.json. Evidence: packages/system/dashboard/images/console/Containerfile, .../apps/console/package.json:2.
  • Build is rewired to the in-tree context and drops the clone — docker buildx build images/console --file images/console/Containerfile, the OCI source label is repointed to the monorepo, and the digest is still pinned by update-values-console in the same commit. Evidence: packages/system/dashboard/Makefile (build-console / update-values-console).
  • Source is excluded from the Helm chart — .helmignore already carries images/*/*. Evidence: packages/system/dashboard/.helmignore:3.
  • No secrets/credentials/.env/private keys committed — a full-diff scan returns only credential-masking UI logic and test fixtures. Evidence: git diff origin/main...HEAD (no real secret material; images/console/.gitignore excludes node_modules, dist, .env).
  • License is intact — the code is now first-party under the repo-wide Apache-2.0 LICENSE. Evidence: LICENSE:1.
  • CI builds/validates from the vendored source — new path-gated ui-test.yaml runs pnpm typecheck + pnpm test, and the image continues to build via make build. Evidence: .github/workflows/ui-test.yaml.
  • The four earlier review findings are addressed with surgical, single-file commits. Evidence: commits d4b0b527b, fbbcc2ee8, 83a847916, ea0c0ab1a.

Blockers: none.

Non-blocking (pre-existing upstream behavior carried in verbatim — fine to land the move now and fix in follow-ups, now that the code is trackable in-tree):

  1. Watch never reconnects when the resource version is unchanged. The watch error callback only calls invalidateQueries; if the re-list returns the same resourceVersion, the effect dependencies do not change and the watch is never re-established, leaving views permanently stale after a transient proxy/network drop. Evidence: packages/system/dashboard/images/console/packages/k8s-client/src/hooks.ts:88-113.

  2. Detail page renders an infinite spinner on a failed GET. if (isLoading || !instance || !ad) precedes the if (error) branch, so a 404/403/5xx leaves instance undefined and renders the spinner forever; the error branch is unreachable. Same class as the loading-state issues already fixed in this PR. Evidence: packages/system/dashboard/images/console/apps/console/src/routes/detail/ApplicationDetailPage.tsx:72.

  3. Application list renders an infinite spinner for an unknown plural. if (defsLoading || (!ad && plural)) stays true for any unrecognized URL after definitions finish loading, so the "Unknown application type" branch is unreachable. Evidence: packages/system/dashboard/images/console/apps/console/src/routes/ApplicationListPage.tsx:29.

  4. The image build does not use the pinned pnpm. package.json sets packageManager: [email protected] and CI honors it, but the Containerfile runs corepack prepare pnpm@latest --activate, so the container build can drift to a newer pnpm major. --frozen-lockfile guards the lockfile, but pinning the builder to the same version would make the image build reproducible. Evidence: packages/system/dashboard/images/console/Containerfile:9 vs .../package.json:5.

  5. No .gitattributes marks the imported tree. This is acceptable and not a blocker: the code is now first-party (the plan archives the external repo), the repo has no .gitattributes convention, the existing in-tree images/token-proxy/ precedent is unmarked, and pnpm-lock.yaml is auto-detected as generated by Linguist. Marking images/console/** linguist-vendored would also hide it from future review diffs, which is undesirable for first-party source. Optionally mark only pnpm-lock.yaml linguist-generated if you want the one-time import to collapse in the diff view. Evidence: no .gitattributes in repo; packages/system/dashboard/images/token-proxy/ unmarked.

myasnikovdaniil added a commit to cozystack/cozystack-ui that referenced this pull request Jun 26, 2026
The console UI now lives in cozystack/cozystack under packages/system/dashboard/images/console (cozystack/cozystack#2963). Tracked in cozystack/cozystack#3097.
myasnikovdaniil added a commit to cozystack/cozystack-ui that referenced this pull request Jun 26, 2026
The console UI now lives in cozystack/cozystack under packages/system/dashboard/images/console (cozystack/cozystack#2963). Tracked in cozystack/cozystack#3097.

Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Jun 29, 2026
…t vendor doc link (#3099)

## What this PR does

Two post-vendor cleanups from the #2963 review:

- **Pin the console image builder to pnpm 11.1.2.** The `Containerfile`
ran `corepack prepare pnpm@latest`, so the container build could drift
to a newer pnpm major than the `[email protected]` pinned in `package.json`
and used by CI. `--frozen-lockfile` guards the lockfile, but pinning the
builder makes the image build reproducible and consistent with CI.
- **Repoint a stale doc link.** `docs/storage-immutability.md`
referenced the now-vendored `cozystack/cozystack-ui#6`; it now points at
the in-tree console path.

## Verification

- No source change; `pnpm typecheck` + `pnpm test` unaffected.
- Builder pnpm version now matches `package.json`'s `packageManager`
field.

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Clarified the storage immutability documentation by updating the UI
reference to an in-repo path.

* **Chores**
* Standardized the build environment to use a specific pnpm version,
helping keep installs and builds consistent.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Jun 29, 2026
…finite spinner (#3098)

## What this PR does

Two console pages ordered their loading guard ahead of the terminal
state, making the latter unreachable. Both are pre-existing upstream
behaviors carried in by the cozystack-ui vendoring (#2963) and flagged
as non-blocking findings in that PR's review.

- **`ApplicationDetailPage`** checked `isLoading || !instance || !ad`
before `if (error)`. On a failed GET (403/404/5xx) `isLoading` is false
and `instance` stays undefined, so the page rendered the loading spinner
forever and never reached the "not found" branch. The error branch now
runs first.
- **`ApplicationListPage`**'s `defsLoading || (!ad && plural)` guard
stayed true for any unrecognized `plural` once definitions had loaded,
so the "Unknown application type." branch was unreachable. The guard now
only spins while definitions are loading.

## Verification

- Added co-located regression tests for the failed-GET (detail) and
unknown-plural (list) paths.
- `pnpm typecheck` clean; full console suite green (`pnpm test`: 317
passed, +4 new).

```release-note
fix(dashboard): the console now shows a not-found message on a failed application GET and an unknown-type message for an unrecognized resource, instead of spinning forever
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved loading and error handling on application pages so users see
the correct message at the right time.
* “Unknown application type” now appears only after application
definitions finish loading, instead of being masked by a loading state.
* Application detail pages now show a “not found” message when the
requested app can’t be loaded, rather than continuing to display a
spinner.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Jun 29, 2026
…lently

When the kc-access cookie expires, k8s API calls return 401 and the SPA was left stuck on a stale page until the user manually cleared the cache. K8sClient now treats 401 as an auth boundary: a one-shot top-level navigation to /oauth2/start?rd=<current-url> (the oauth2-proxy re-auth convention). Overridable via K8sClientConfig.onUnauthorized; no-op outside the browser. A unauthorizedHandled latch prevents repeated redirects on concurrent 401s.

Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Related: #2788. A unit regression test is a follow-up — the k8s-client workspace package has no test harness yet.

Co-authored-by: IvanHunters <[email protected]>
Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Jun 29, 2026
In the four backup create pages (Backup, BackupJob, BackupPlan, BackupRestoreJob), run the RJSF SchemaForm.validate() call right after the name check and before the page-level required-field alerts. The submit button lives outside RJSF and bypasses its validation, so each page triggers validate() explicitly; it previously ran only after the manual alert checks, so a schema-required field left empty exited via an alert without RJSF ever rendering its inline errors. Validating first makes inline errors show consistently; the page-level alerts then cover only page-only requirements.

Re-applied from cozystack/cozystack-ui#26 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Scope is the validation-ordering change only — the original PR's VMDisk/StorageClass parts were obsoleted by the DynamicOptionsWidget refactor (#30) already in the vendored snapshot. Includes the original per-page tests.

Co-authored-by: Aleksei Sviridkin <[email protected]>
Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Jun 29, 2026
The cozystack-ui SPA was vendored into the monorepo (#2963) and now lives under packages/system/dashboard. Map its likely PR title scopes to the existing area/dashboard so UI PRs no longer fall through to area/uncategorized.

Assisted-By: Claude <[email protected]>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Jun 29, 2026
…3117)

## What this PR does

The cozystack-ui SPA was vendored into the monorepo in #2963 and now
lives under `packages/system/dashboard`. PRs touching the UI are likely
to use `cozystack-ui` or `console` as their Conventional Commits scope,
neither of which the PR auto-labeler recognizes today — so they fall
through to `area/uncategorized`.

This maps both scopes to the existing `area/dashboard` (already
described as "dashboard / UI"). No new label is introduced;
`.github/labels.yml` is unchanged.

### Release note

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Expanded automatic PR labeling to add the `area/dashboard` label when
a PR title scope is `cozystack-ui` or `console`.
* Existing label rules and parsing behavior remain unchanged for all
other scopes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
pull Bot pushed a commit to medampudi/cozystack that referenced this pull request Jun 29, 2026
…finite spinner

Two console pages ordered their loading guard ahead of the terminal state, making the latter unreachable:

- ApplicationDetailPage checked `isLoading || !instance || !ad` before `if (error)`. On a failed GET, isLoading is false and instance stays undefined, so the page spun forever and never showed the not-found message. The error branch now runs first.

- ApplicationListPage's `defsLoading || (!ad && plural)` guard stayed true for any unrecognized plural once definitions had loaded, so 'Unknown application type.' was unreachable. The guard now only spins while definitions are loading.

Both are pre-existing upstream behaviors carried in by the cozystack-ui vendoring (cozystack#2963). Adds co-located regression tests for the failed-GET and unknown-plural paths.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
pull Bot pushed a commit to medampudi/cozystack that referenced this pull request Jun 29, 2026
…t vendor doc link

The console Containerfile ran `corepack prepare pnpm@latest`, so the image build could drift to a newer pnpm major than the `[email protected]` pinned in package.json and used by CI. `--frozen-lockfile` guards the lockfile, but pinning the builder makes the image build reproducible and consistent with CI.

Also repoints docs/storage-immutability.md off the now-vendored cozystack-ui#6 link to the in-tree console path (follow-up from cozystack#2963).

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Aug 15, 2026
…lently

When the kc-access cookie expires, k8s API calls return 401 and the SPA was left stuck on a stale page until the user manually cleared the cache. K8sClient now treats 401 as an auth boundary: a one-shot top-level navigation to /oauth2/start?rd=<current-url> (the oauth2-proxy re-auth convention). Overridable via K8sClientConfig.onUnauthorized; no-op outside the browser. A unauthorizedHandled latch prevents repeated redirects on concurrent 401s.

Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Related: #2788. A unit regression test is a follow-up — the k8s-client workspace package has no test harness yet.

Co-authored-by: IvanHunters <[email protected]>
Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Aug 15, 2026
In the four backup create pages (Backup, BackupJob, BackupPlan, BackupRestoreJob), run the RJSF SchemaForm.validate() call right after the name check and before the page-level required-field alerts. The submit button lives outside RJSF and bypasses its validation, so each page triggers validate() explicitly; it previously ran only after the manual alert checks, so a schema-required field left empty exited via an alert without RJSF ever rendering its inline errors. Validating first makes inline errors show consistently; the page-level alerts then cover only page-only requirements.

Re-applied from cozystack/cozystack-ui#26 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Scope is the validation-ordering change only — the original PR's VMDisk/StorageClass parts were obsoleted by the DynamicOptionsWidget refactor (#30) already in the vendored snapshot. Includes the original per-page tests.

Co-authored-by: Aleksei Sviridkin <[email protected]>
Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Aug 17, 2026
#3116)

## What this PR does

Re-applies cozystack/cozystack-ui#26 (by @lexfrei) onto the in-tree
console, after the UI was vendored into the monorepo (#2963) and the
standalone repo is being archived. Part of the cozystack-ui retirement
tracked in #3097.

In the four backup create pages (Backup, BackupJob, BackupPlan,
BackupRestoreJob), the RJSF `SchemaForm.validate()` call now runs right
after the name check and before the page-level required-field alerts.
The submit button lives outside RJSF and bypasses its validation, so
each page triggers `validate()` explicitly; it previously ran only after
the manual alert checks, so a schema-required field left empty exited
via an alert without RJSF ever rendering its inline errors. Validating
first makes inline errors show consistently; the page-level alerts then
cover only page-only requirements (application/backup reference,
strategy, timestamp).

Scope is the validation-ordering change only — the original PR's
VMDisk/StorageClass parts were obsoleted by the `DynamicOptionsWidget`
refactor (#30) already present in the vendored snapshot.

## Verification

- Includes the original per-page regression tests.
- `pnpm typecheck` clean; full console suite green (317 passed).

Original PR: cozystack/cozystack-ui#26 — credited via `Co-authored-by`.

```release-note
NONE
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved submission validation across backup creation, backup job,
backup plan, and backup restore job pages.
  * Inline required-field errors now appear before page-level alerts.
* Invalid submissions are prevented from sending requests or triggering
unnecessary alerts.

* **Tests**
  * Expanded coverage for validation order and submission blocking.
  * Improved form test scenarios for invalid and missing data.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Aleksei Sviridkin (lexfrei) pushed a commit that referenced this pull request Oct 7, 2026
…lently

When the kc-access cookie expires, k8s API calls return 401 and the SPA was left stuck on a stale page until the user manually cleared the cache. K8sClient now treats 401 as an auth boundary: a one-shot top-level navigation to /oauth2/start?rd=<current-url> (the oauth2-proxy re-auth convention). Overridable via K8sClientConfig.onUnauthorized; no-op outside the browser. A unauthorizedHandled latch prevents repeated redirects on concurrent 401s.

Re-applied from cozystack/cozystack-ui#34 onto the in-tree console after the UI was vendored (#2963); the source repo is being archived. Related: #2788. A unit regression test is a follow-up — the k8s-client workspace package has no test harness yet.

Co-authored-by: IvanHunters <[email protected]>
Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <[email protected]>
Aleksei Sviridkin (lexfrei) pushed a commit that referenced this pull request Oct 7, 2026
…lently

When the kc-access cookie expires, k8s API calls return 401 and the
SPA stays on a stale page until the user clears the cache by hand.
K8sClient now treats 401 as an auth boundary and makes one top-level
navigation into the oauth2-proxy sign-in flow, passing the current
location as rd so the user comes back to the same page. The handler
can be replaced through K8sClientConfig.onUnauthorized and does
nothing outside the browser. The unauthorizedHandled latch keeps
concurrent 401s from redirecting more than once.

Re-applied from cozystack/cozystack-ui#34 onto the in-tree console
after the UI was vendored (#2963), since the source repo is being
archived. Related: #2788.

Co-authored-by: IvanHunters <[email protected]>
Assisted-by: LLM
Signed-off-by: Myasnikov Daniil <[email protected]>
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Oct 7, 2026
…3115)

## What this PR does

When the dashboard session expires, the console now sends the user back
to sign-in instead of sitting on a stale page. This re-applies
cozystack/cozystack-ui#34 by @IvanHunters onto the in-tree console,
since the UI was vendored into the monorepo (#2963) and the standalone
repo is being archived. It is part of the cozystack-ui retirement
tracked in #3097.

When the `kc-access` cookie expires, k8s API calls return 401.
`K8sClient` now treats a 401 as an auth boundary and makes one top-level
navigation to `/oauth2/sign_in?rd=<current-url>`. That path is the
sign-in entry both gatekeeper modes serve. With OIDC enabled,
oauth2-proxy goes straight to the provider (`--skip-provider-button`)
and returns the user to `rd`. Without OIDC, token-proxy renders its
token form there, but it does not honour `rd` yet, so the user lands on
`/` after sign-in.

The handler can be replaced through `K8sClientConfig.onUnauthorized` and
does nothing outside the browser. A latch keeps concurrent 401s from
redirecting more than once, and any 2xx from either fetch site re-arms
it, so a handler that re-authenticates in place still sees the next
expiry.

Related: #2788.

### Verification

`client.unauthorized.test.ts` covers the hook on the request and watch
paths, the latch across concurrent 401s, the re-arm after a success, and
the default handler's target and relative `rd`. `pnpm typecheck` and the
console test suite pass.

Original PR: cozystack/cozystack-ui#34, credited via `Co-authored-by`.

### Screenshots

Not a UI change.

### Downstream repositories

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

```release-note
fix(dashboard): on a 401 (expired session) the console now sends the user to sign in again instead of silently showing a stale page. With OIDC enabled the user returns to the page they were on; without OIDC they land on the start page after entering a token.
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/build Issues or PRs related to image build infrastructure, multi-arch support area/dashboard Issues or PRs related to the dashboard / UI kind/feature Categorizes issue or PR as related to a new feature size/XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants