Skip to content

Feat: Websites can be organized into hierarchical groups#4371

Open
SniperCZE wants to merge 11 commits into
umami-software:devfrom
SniperCZE:hierarchical-groups
Open

Feat: Websites can be organized into hierarchical groups#4371
SniperCZE wants to merge 11 commits into
umami-software:devfrom
SniperCZE:hierarchical-groups

Conversation

@SniperCZE

@SniperCZE SniperCZE commented Jul 1, 2026

Copy link
Copy Markdown

Summary

Websites can be organized into hierarchical groups with custom names. A group can contain other groups and/or websites. Each website can belong to at most one group, or to no group at all (group_id is NULL by default).

Groups use the same ownership model as websites: they belong either to a user or to a team, never both.

What changed

Database

  • Added migration 21_add_website_groups.
  • New table website_group stores group metadata: name, optional parent_id (nested groups), user_id / team_id, and created_by.
  • Extended website with nullable group_id referencing a group.
  • No ClickHouse or tracker changes.
  • Fully backward compatible: existing websites and API consumers keep working; new fields are optional.

API

  • New CRUD endpoints: GET/POST /api/website-groups, GET/POST/DELETE /api/website-groups/[groupId]
  • New tree endpoints: GET /api/me/website-tree, GET /api/teams/[teamId]/website-tree
  • Extended website create/update to accept optional groupId.
  • Flat website list responses now include groupId and groupPath (e.g. "My project / LAB").
  • Deleting a group reparents its contents one level up (subgroups and websites move to the parent group, or to root if the deleted group was at root).
  • Website transfer between user and team clears groupId.
  • Validation ensures group/website ownership matches and prevents cyclic parent relationships.

Permissions

  • Added website group permissions aligned with existing website permissions (view/create/update/delete for personal and team context).

UI

  • /websites shows an expandable tree of groups and websites (subgroups first, then websites; both sorted A–Z within each level).
  • Added Add group on /websites, with create/edit/delete group dialogs (name, optional parent group; no nesting depth limit).
  • Updated website create/edit forms to allow assigning a group.
  • When searching on /websites, the UI falls back to a flat list with a Group column (groupPath).
  • Hierarchical WebsiteSelect (TopNav, boards) uses grouped dropdown; search shows full group path.
  • Admin websites table shows a Group column.
  • Team website settings (/settings/websites) reuse the same tree UI.

Translations

  • Added new strings in en-US and cs-CZ only; other locales unchanged.

Testing

  • Unit tests: src/lib/websiteTree.test.ts, src/permissions/websiteGroup.test.ts — all passing
  • E2E API tests: tests/e2e/api-website-group.spec.ts, extended tests/e2e/api-website.spec.ts
  • pnpm build passes
  • Manual testing with ~15 websites across multiple nesting levels (create/rename/move/delete groups, assign websites, delete non-empty groups, search, team context)

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

@SniperCZE is attempting to deploy a commit to the Umami Software Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds hierarchical website groups across the database, API, and UI. The main changes are:

  • New website_group model and website.group_id field.
  • CRUD and tree endpoints for personal and team website groups.
  • Website create/edit support for assigning groups.
  • Tree and grouped-select UI for browsing websites.
  • Permission checks and tests for website groups.

Confidence Score: 4/5

The grouped selector and bulk user/team cleanup paths need fixes before merging.

  • The grouped dropdown can display websites under the wrong group.
  • Cloud cleanup can leave retained websites pointing at deleted groups.
  • Nested groups can make user or team deletion fail during bulk cleanup.

src/components/input/WebsiteSelect.tsx, src/queries/prisma/user.ts, src/queries/prisma/team.ts

Important Files Changed

Filename Overview
src/components/input/WebsiteSelect.tsx Adds grouped website selection, but flattened group sections can capture sibling websites.
src/queries/prisma/websiteGroup.ts Adds website-group queries, tree loading, and safe single-group reparenting on delete.
src/queries/prisma/team.ts Extends team deletion to remove groups, but bulk deletion skips hierarchy and website-reference cleanup.
src/queries/prisma/user.ts Extends user deletion to remove groups, but bulk deletion skips hierarchy and website-reference cleanup.
src/app/api/website-groups/route.ts Adds list and create endpoints with owner and team validation.
src/app/api/website-groups/[groupId]/route.ts Adds get, update, and delete endpoints with parent validation and single-group reparenting.
src/lib/websiteTree.ts Adds tree building, flattening, path lookup, and cycle-check helpers.
prisma/schema.prisma Adds the WebsiteGroup model and optional Website-to-group relation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[WebsiteGroup rows] --> B[Website tree APIs]
  B --> C[Website tree table]
  B --> D[Grouped WebsiteSelect]
  A --> E[Website groupId assignments]
  F[Single group delete] --> G[Reparent children and websites]
  H[User/team delete] --> I[Bulk delete groups]
  I --> J[Dangling groupId or nested-delete failure]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
  A[WebsiteGroup rows] --> B[Website tree APIs]
  B --> C[Website tree table]
  B --> D[Grouped WebsiteSelect]
  A --> E[Website groupId assignments]
  F[Single group delete] --> G[Reparent children and websites]
  H[User/team delete] --> I[Bulk delete groups]
  I --> J[Dangling groupId or nested-delete failure]
Loading

Reviews (1): Last reviewed commit: "Groups can be deleted even when members ..." | Re-trigger Greptile

Comment thread src/components/input/WebsiteSelect.tsx Outdated
Comment thread src/queries/prisma/team.ts Outdated
Comment thread src/queries/prisma/user.ts Outdated
Comment thread src/queries/prisma/user.ts Outdated
Comment thread src/queries/prisma/team.ts Outdated
@SniperCZE

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants