Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions .agents/skills/qovery-console-standards/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,18 @@ libs/
- `data-access` libs contain API wiring only.
- `feature` libs contain business components.
- `ui` libs contain reusable presentation components.
- Utilities that can be shared must live in `@qovery/shared/util-js` and be imported directly (never re-export via data-access).
- Reusable generic helpers belong in the shared library that owns their concern; use `@qovery/shared/util-js` when no specialized shared library fits. Import them directly, never re-export them via data-access.
- Respect Nx architecture (no circular imports).
- Use Nx generators to create new components/libs.

### Code Placement

- Before adding a folder or abstraction, follow comparable code in the same domain. A generic name alone does not make a helper shared.
- Domain feature libs: hooks in `src/lib/hooks/use-<name>/`, domain-specific pure helpers in `src/lib/utils/`. Do not create a second `hooks/` directory inside a feature folder.
- For generic helpers, prefer the existing shared library that owns their concern; otherwise use `@qovery/shared/util-js`. Reuse across callers in one domain does not make a domain-specific helper generic.
- Create a dedicated library only for a cohesive concept (types, behavior, or UI) reused across domains with no existing owner. Choose its Nx layer from its real dependencies and callers.
- `@qovery/shared/console-shared` is legacy: add nothing new to it. Existing usages can stay; do not migrate them as part of an unrelated change.

---

## 2. React & TypeScript Standards
Expand All @@ -56,7 +64,7 @@ libs/
- Destructure props directly in parameters.
- Props interface named `<ComponentName>Props`.
- Named exports only (no default exports).
- Use `clsx` or `twMerge` from `@qovery/shared/util-js` for conditional classes.
- Use `clsx` (from `clsx`) for conditional classes and `twMerge` (from `@qovery/shared/util-js`) to resolve conflicting Tailwind classes, such as defaults and a caller's `className`.
- Prefer Radix UI components when available.

### Route Parameters
Expand Down Expand Up @@ -94,7 +102,7 @@ libs/
- TailwindCSS only (no CSS modules or styled-components in new code).
- Use utilities from `@qovery/shared/ui` when available.
- Mobile-first responsive design.
- Use `clsx` or `twMerge` from `@qovery/shared/util-js` for conditional classes.
- Use `clsx` (from `clsx`) for conditional classes and `twMerge` (from `@qovery/shared/util-js`) to resolve conflicting Tailwind classes, such as defaults and a caller's `className`.

### Component Styling

Expand Down Expand Up @@ -249,3 +257,36 @@ Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
- All tests must pass.
- Code must be properly formatted.
- Snapshot changes must be intentional and reviewed.

---

## 8. Implementation & Review Decisions

### Keep Shared Changes Necessary

- Every new prop, abstraction, or shared-component change needs a concrete caller or behavior today. No props or tokens for hypothetical future needs.
- Prefer the existing API when it covers the need. When extending, keep existing callers working and check the behavior in their flows, not only in the new screen.

### Separate Business Rules from Rendering

- Keep payload construction, API parsing, and business transformations in domain hooks or helpers that can be tested on their own, not in rendering components.
- Prefer typed API fields over parsing opaque payloads. If the contract cannot change, parse and validate once at the domain boundary.
- An action's label, icon, and description must describe the same operation. A neutral icon shared by several operations is fine.
- Name helpers after the resource or capability they identify, not after a version, rollout, or feature flag.

### Choose State Ownership Explicitly

- Derive values from props or state instead of copying them into state.
- Use controlled inputs when the caller owns the value; use default values only for initialization.
- Reserve effects for synchronization with external systems. See the `react-useeffect` skill for alternatives.

### Validate and Review Proportionally

- Test the behavior and boundaries the change affects, including invalid or legacy API data when relevant. Extract complex pure logic for direct tests; small expressions do not need extraction.
- Classify each finding as a **bug** (give the concrete trigger and consequence), a **convention** departure (cite the rule or comparable code), or an optional **suggestion**. Only bugs are blocking by default.
- Check current callers and comparable implementations before recommending a new abstraction or shared change.
- Treat automated review findings and "resolved" labels as leads, not proof: check the fix against the current diff before closing a finding.

### Write Actionable Review Rules

When adding a rule, state its scope, the behavior to flag, why it matters, and legitimate exceptions. Prefer checks lint does not already enforce, and avoid generalizing a rule from a single PR.
35 changes: 24 additions & 11 deletions .agents/skills/qovery-console-standards/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ metadata:

# Qovery Console Standards

Comprehensive coding standards for the Qovery Console React/TypeScript monorepo. Contains 7 rules covering architecture, React patterns, styling, testing, naming, development workflow, and pre-commit quality gates.
Comprehensive coding standards for the Qovery Console React/TypeScript monorepo. Contains 8 rules covering architecture, React patterns, styling, testing, naming, development workflow, pre-commit quality gates, and implementation and review decisions.

## When to Apply

Expand All @@ -24,15 +24,16 @@ Reference these guidelines when:

## Rule Categories

| Rule | File | Scope |
| -------------------- | ------------------------------- | --------------------------------------------------------- |
| Project Architecture | `project-architecture.md` | Nx monorepo structure, lib organization, layer boundaries |
| React & TypeScript | `react-typescript-standards.md` | Import rules, component patterns, state management |
| Styling | `styling-standards.md` | TailwindCSS, Radix UI, responsive design, accessibility |
| Testing | `testing-standards.md` | Test setup, structure, mocking, coverage |
| Naming Conventions | `naming-conventions.md` | File, variable, component, and constant naming |
| Development Commands | `development-commands.md` | Daily workflow, Nx commands, package management, git |
| Pre-commit Workflow | `pre-commit-workflow.md` | Conventional commits, format/test/lint checks |
| Rule | File | Scope |
| ----------------------- | ------------------------------- | --------------------------------------------------------------- |
| Project Architecture | `project-architecture.md` | Nx monorepo structure, lib organization, layer boundaries |
| React & TypeScript | `react-typescript-standards.md` | Import rules, component patterns, state management |
| Styling | `styling-standards.md` | TailwindCSS, Radix UI, responsive design, accessibility |
| Testing | `testing-standards.md` | Test setup, structure, mocking, coverage |
| Naming Conventions | `naming-conventions.md` | File, variable, component, and constant naming |
| Development Commands | `development-commands.md` | Daily workflow, Nx commands, package management, git |
| Pre-commit Workflow | `pre-commit-workflow.md` | Conventional commits, format/test/lint checks |
| Implementation & Review | `implementation-review.md` | Code placement, shared changes, API boundaries, state ownership |

## Quick Reference

Expand All @@ -41,13 +42,14 @@ Reference these guidelines when:
- Nx monorepo: `apps/` and `libs/` (domains, pages, shared)
- `data-access` = API logic, `feature` = business components, `ui` = reusable presentation
- No circular imports; use Nx generators for new libs
- Domain hooks in `src/lib/hooks/use-<name>/`, helpers in `src/lib/utils/`; nothing new in `shared/console-shared` (legacy)

### React & TypeScript

- Inline type imports: `import { type Foo, bar } from './module'`
- Functional components only, no `React.FC`, destructure props in signature
- Route-scoped components read URL identifiers with TanStack Router `useParams({ strict: false })`; do not forward route IDs through props solely for data access
- React Query for server state, `clsx`/`twMerge` for conditional classes
- React Query for server state, `clsx` for conditional classes, `twMerge` for conflicting Tailwind classes

### Styling

Expand All @@ -66,6 +68,12 @@ Reference these guidelines when:
- Snake-case files (`user-profile.tsx`), PascalCase components
- `Props` suffix for interfaces, `UPPER_SNAKE_CASE` for constants

### Implementation & Review

- Shared changes need a concrete caller; keep existing callers working
- Business transformations in domain hooks/helpers, not in rendering
- Classify review findings: bug (trigger + consequence), convention, or optional suggestion

### Workflow

- Yarn Berry only (never npm), `nvm use`
Expand All @@ -80,6 +88,11 @@ Read individual rule files for detailed guidelines:
rules/project-architecture.md
rules/react-typescript-standards.md
rules/styling-standards.md
rules/testing-standards.md
rules/naming-conventions.md
rules/development-commands.md
rules/pre-commit-workflow.md
rules/implementation-review.md
```

## Full Compiled Document
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: Implementation and Review Decisions
impact: HIGH
tags: [architecture, review, api, state]
---

# Implementation and Review Decisions

Code placement and library ownership are covered in [project-architecture.md](project-architecture.md#code-placement).

## Keep Shared Changes Necessary

- Every new prop, abstraction, or shared-component change needs a concrete caller or behavior today. No props or tokens for hypothetical future needs.
- Prefer the existing API when it covers the need. When extending, keep existing callers working and check the behavior in their flows, not only in the new screen.

## Separate Business Rules from Rendering

- Keep payload construction, API parsing, and business transformations in domain hooks or helpers that can be tested on their own, not in rendering components.
- Prefer typed API fields over parsing opaque payloads. If the contract cannot change, parse and validate once at the domain boundary.
- An action's label, icon, and description must describe the same operation. A neutral icon shared by several operations is fine.
- Name helpers after the resource or capability they identify, not after a version, rollout, or feature flag.

## Choose State Ownership Explicitly

- Derive values from props or state instead of copying them into state.
- Use controlled inputs when the caller owns the value; use default values only for initialization.
- Reserve effects for synchronization with external systems. See the `react-useeffect` skill for alternatives.

## Validate and Review Proportionally

- Test the behavior and boundaries the change affects, including invalid or legacy API data when relevant. Extract complex pure logic for direct tests; small expressions do not need extraction.
- Classify each finding as a **bug** (give the concrete trigger and consequence), a **convention** departure (cite the rule or comparable code), or an optional **suggestion**. Only bugs are blocking by default.
- Check current callers and comparable implementations before recommending a new abstraction or shared change.
- Treat automated review findings and "resolved" labels as leads, not proof: check the fix against the current diff before closing a finding.

## Write Actionable Review Rules

When adding a rule, state its scope, the behavior to flag, why it matters, and legitimate exceptions. Prefer checks lint does not already enforce, and avoid generalizing a rule from a single PR.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,15 @@ libs/
- `data-access` libs contain API wiring only
- `feature` libs contain business components
- `ui` libs contain reusable presentation components
- Utilities that can be shared must live in `@qovery/shared/util-js` and be imported directly (never re-export via data-access)
- Reusable generic helpers belong in the shared library that owns their concern; use `@qovery/shared/util-js` when no specialized shared library fits. Import them directly, never re-export them via data-access

## Code Placement

- Before adding a folder or abstraction, follow comparable code in the same domain. A generic name alone does not make a helper shared.
- Domain feature libs: hooks in `src/lib/hooks/use-<name>/`, domain-specific pure helpers in `src/lib/utils/`. Do not create a second `hooks/` directory inside a feature folder.
- For generic helpers, prefer the existing shared library that owns their concern; otherwise use `@qovery/shared/util-js`. Reuse across callers in one domain does not make a domain-specific helper generic.
- Create a dedicated library only for a cohesive concept (types, behavior, or UI) reused across domains with no existing owner. Choose its Nx layer from its real dependencies and callers.
- `@qovery/shared/console-shared` is legacy: add nothing new to it. Existing usages can stay; do not migrate them as part of an unrelated change.

## Important Rules

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tags: [react, typescript, components, imports, state]
- Destructure props directly in parameters
- Props interface named `<ComponentName>Props`
- Named exports only (no default exports)
- Use `clsx` or `twMerge` from `@qovery/shared/util-js` for conditional classes
- Use `clsx` (from `clsx`) for conditional classes and `twMerge` (from `@qovery/shared/util-js`) to resolve conflicting Tailwind classes, such as defaults and a caller's `className`
- Prefer Radix UI components when available

## Route Parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ tags: [tailwind, css, styling, accessibility, radix, responsive]
- TailwindCSS only (no CSS modules or styled-components in new code)
- Use utilities from `@qovery/shared/ui` when available
- Mobile-first responsive design
- Use `clsx` or `twMerge` from `@qovery/shared/util-js` for conditional classes
- Use `clsx` (from `clsx`) for conditional classes and `twMerge` (from `@qovery/shared/util-js`) to resolve conflicting Tailwind classes, such as defaults and a caller's `className`

## Component Styling

Expand Down
6 changes: 5 additions & 1 deletion .agents/skills/qovery-ui/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ Design review and implementation guidance for the Qovery Console. Read `.claude/

## /audit

**Load:** `references/colors.md`, `references/spacing-layout.md`, `references/anti-patterns.md`, `references/accessibility.md`
**Load:** `references/colors.md`, `references/spacing-layout.md`, `references/anti-patterns.md`, `references/accessibility.md`, `references/components.md`

Four-phase review. Label every finding:

- `[blocking]` — ships broken, violates semantic rules, or breaks accessibility
- `[important]` — degrades experience, likely to cause design review rejection
- `[nit]` — minor inconsistency, low impact

A component substitution or refactor is not `[blocking]` just because it is preferred: state the user impact and check that the proposed shared component fits the interaction.

**Phase 1 — Token hygiene:** hardcoded values, legacy tokens, raw Tailwind palette classes used semantically.
**Phase 2 — Semantic color:** status colors used decoratively, color-only state, brand as page wash.
**Phase 3 — Spacing & density:** off-grid values, whitespace inflation, button height overrides.
Expand Down Expand Up @@ -81,6 +83,8 @@ Fix without changing functionality. Output a Before / After / Why table, one row

**Load:** `references/components.md`

Also use this reference when extending shared components or reviewing a new interactive flow. Follow its reuse, compatibility, Storybook, and interaction checks for the states affected by the change.

1. Identify if a shared component already exists — always prefer it
2. Return the import path and correct usage with props
3. If nothing fits, explain why before scaffolding new
Expand Down
17 changes: 17 additions & 0 deletions .agents/skills/qovery-ui/references/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

All shared components live in `libs/shared/ui/src/lib/components/`. Check here before building anything new.

## Reuse and Extend Shared UI

- Check `shared/ui` and its existing callers first. Reuse a component when its **behavior** fits; visual similarity alone does not justify swapping in a component with different interaction semantics.
- Nothing new in `shared/console-shared` (legacy). Generic presentation goes to `shared/ui`, business UI stays in its domain. Do not move business logic into the design system just to avoid `console-shared`.
- Extend a shared component only for a concrete need its API cannot cover. Keep the extension generic and existing callers working.
- Multiple styling variants: follow the existing CVA pattern. Simple conditions do not need a variant.
- Classes: `clsx` for conditional composition, `twMerge` (`@qovery/shared/util-js`) when defaults can conflict with a caller's `className`. No nested template strings.
- Add or update Storybook stories for new shared components and new visual states or variants.

## Verify the Interaction

- Loading, failure, validation, and saving states give clear feedback.
- Clickable controls look clickable. "Draft being checked" is distinguishable from "changes ready to save".
- Read-only fields stay read-only in nested editors and dialogs.
- Leaving with unsaved changes has deliberate behavior; leave-page protection must not block navigation within the draft.
- Test tooltips and popovers from their actual keyboard focus target, including controls nested in clickable rows or links. A focusable parent does not make a hover tooltip keyboard-accessible.

---

## Decision Guide
Expand Down
13 changes: 13 additions & 0 deletions apps/console/src/app/components/auth-page/auth-page-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { authPageBeforeLoad, clearAuthEntry, readAuthEntry } from './auth-page-u
describe('auth entry', () => {
beforeEach(() => {
sessionStorage.clear()
localStorage.clear()
})

afterEach(() => {
Expand All @@ -22,6 +23,18 @@ describe('auth entry', () => {
expect(readAuthEntry()).toEqual({ screen: 'signup', redirect: '/organization/123/overview' })
})

it('remembers SAML SSO as the last used login of a `?connection=` auto-login', async () => {
localStorage.setItem('lastUsedLogin', 'github')

await authPageBeforeLoad({
page: 'login',
auth: { isAuthenticated: false, login: jest.fn().mockResolvedValue(undefined) },
search: { connection: 'acme' },
})

expect(localStorage.getItem('lastUsedLogin')).toBe('saml_sso')
})

it('falls back to the login page and the root when nothing was remembered', () => {
expect(readAuthEntry()).toEqual({ screen: 'login', redirect: '/' })
})
Expand Down
Loading
Loading