feat(dashboard): team overview at /manage/<team>/overview - #351
Conversation
Greptile SummaryThe PR adds a membership-authorized team overview API and a dashboard overview driven by the active team stored in client state.
Confidence Score: 3/5The PR is not yet safe to merge because the Create Event and Manage members controls expose visible actions that do nothing. TeamPageHeader renders Create Event without navigation or a handler, and the previously reported Manage members button remains similarly unwired. Files Needing Attention: dashboard/src/components/dashboard/teams/TeamPageHeader.vue, dashboard/src/pages/manage/teams/TeamOverview.vue
|
| Filename | Overview |
|---|---|
| buzz/api/teams/services.py | Adds the membership-gated overview service and enabled-member query without an identified blocking defect. |
| dashboard/src/data/teams.ts | Adds reactive active-team overview loading tied to the stored team selection. |
| dashboard/src/pages/manage/teams/TeamOverview.vue | Builds the overview page, but the previously reported Manage members action remains unwired. |
| dashboard/src/components/dashboard/teams/TeamPageHeader.vue | Adds ambient-team breadcrumbs and an unwired Create Event primary action. |
| dashboard/src/router.ts | Adds fixed team overview routes and explicit placeholder routing for unfinished team sections. |
| dashboard/src/layouts/ManagerLayout.vue | Adds the in-panel page-header target, panel-owned scrolling, and team navigation entries. |
Prompt To Fix All With AI
### Issue 1
dashboard/src/components/dashboard/teams/TeamPageHeader.vue:18
**Create Event action is inert**
When a team member clicks the visible Create Event action, the button has neither a route nor a click handler, so the click has no effect.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (6): Last reviewed commit: "feat(dashboard): team overview page at /..." | Re-trigger Greptile
538c9c7 to
1df6b60
Compare
699cfc5 to
a570feb
Compare
| </Avatar> | ||
| </div> | ||
|
|
||
| <Button variant="ghost" label="Manage members" class="w-fit" /> |
There was a problem hiding this comment.
Manage members action is inert
When a team member clicks Manage members, the button has no route or click handler, so the visible management action does nothing even though the dashboard defines /manage/<team>/members as a team-scoped destination.
| <Button variant="ghost" label="Manage members" class="w-fit" /> | |
| <Button | |
| variant="ghost" | |
| label="Manage members" | |
| class="w-fit" | |
| :route="`/manage/${teamId}/members`" | |
| /> |
Prompt To Fix With AI
This is a comment left during a code review.
Path: dashboard/src/pages/manage/teams/TeamOverview.vue
Line: 113
Comment:
**Manage members action is inert**
When a team member clicks **Manage members**, the button has no route or click handler, so the visible management action does nothing even though the dashboard defines `/manage/<team>/members` as a team-scoped destination.
```suggestion
<Button
variant="ghost"
label="Manage members"
class="w-fit"
:route="`/manage/${teamId}/members`"
/>
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Still inert here, deliberately: /manage/team/members does not exist until the next PR in the stack, which adds both the route and the wiring on this button. The suggested fix cannot apply on this branch — the named route is not registered yet.
Note the path in the suggestion is also out of date. Team scoping moved out of the URL in response to the other review on this PR, following how frappe/central does it, so the destination is a fixed /manage/team/members with no team id in the path.
Flagging rather than silently deferring: if this PR were to merge alone, that button would ship dead.
There was a problem hiding this comment.
Fair point, and the path correction is noted — I missed that team scoping moved out of the URL, so /manage/team/members is right, not /manage/${teamId}/members.
The inert button is reasonable given the stacked PR setup. One small thing worth considering: a brief inline comment on the button would make the intent self-documenting for anyone reviewing this PR in isolation, so it doesn't read as an accidental omission:
| <Button variant="ghost" label="Manage members" class="w-fit" /> | |
| <Button variant="ghost" label="Manage members" class="w-fit" /> | |
| <!-- Route wired in the follow-up PR that adds /manage/team/members --> |
Otherwise, no objection — closing the comment.
1df6b60 to
846f6ed
Compare
a570feb to
50e0c6c
Compare
846f6ed to
2f246bd
Compare
get_team_overview returns one team with its enabled members, for the team dashboard. Membership is the authorization and the query reads past permissions, like get_my_teams: Buzz Team is readable by Event Manager only, while a Frontdesk or Viewer member still works inside the team. An unknown team raises NotATeamMember rather than a not-found, so a non-member cannot probe team ids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Events page files cards under a date heading, so the card only carries a time. A standalone list has no heading to lean on — showDate puts the day label beside the time for those callers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50e0c6c to
34cff5a
Compare
|
✅ UI Demo Check — a screenshot or demo is attached. |
34cff5a to
d870159
Compare
The active team is ambient state, not a path segment: data/teams holds the selection in localStorage and every team-scoped page reads it, so the paths are fixed and a switch re-reads the page in place. This follows frappe/central, where the console keeps the active team in useSession and routes stay flat. get_team_overview moves off createResource onto useCall with the selected team as its params, so it refetches itself on a switch and no page drives it. One watcher covers the first read: get_my_teams settles the selection after the module loads, so there is nothing to ask about until it does. The page shows the team, up to three of its upcoming events (filtered out of the feed the Events page already loads), and the member avatars. Pages declare their header through frappe-ui's PageHeader. The target lives in ManagerLayout rather than DesktopShell's own, so the header sits inside the rounded panel; the registry is a stack and the last target registered wins. The panel now owns its scroll, which is why DesktopShell takes scroll=false. Members has no page yet — a second catch-all sends /manage/team/<section> to WorkInProgress, the way the unscoped sidebar links already work. /manage/team redirects to the overview so the bare path is not a dead end. Trade-off: an overview link is no longer shareable across teams — it opens whichever team the recipient last used. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
d870159 to
afff9d3
Compare
| <template> | ||
| <PageHeader class="border-none pt-2"> | ||
| <Breadcrumbs :items="items" /> | ||
| <Button variant="solid" icon-left="plus" label="Create Event" /> |
There was a problem hiding this comment.
When a team member clicks the visible Create Event action, the button has neither a route nor a click handler, so the click has no effect.
Prompt To Fix With AI
This is a comment left during a code review.
Path: dashboard/src/components/dashboard/teams/TeamPageHeader.vue
Line: 18
Comment:
**Create Event action is inert**
When a team member clicks the visible Create Event action, the button has neither a route nor a click handler, so the click has no effect.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
fixed in later stacked PRs
What changed
First slice of the team section: a real page at
/manage/team/overview, plus the headerand routing the rest of the team pages hang off.
The active team is ambient state, not a path segment —
data/teamsholds the selectionin localStorage and every team-scoped page reads it, so paths are fixed and switching teams
re-reads the open page. This follows
frappe/central, where the console keeps the activeteam in
useSessionand routes stay flat (/servers,/team/members).buzz.api.teams.get_team_overview(team)returns the team and its enabledmembers. Membership is the authorization and the query reads past permissions, same as
get_my_teams: Buzz Team is readable by Event Manager only, but a Frontdesk or Viewermember still works inside the team. An unknown team raises
NotATeamMember, not anot-found, so a non-member can't probe team ids.
get_team_overviewmoves offcreateResourceontouseCallwith theselected team as reactive params.
useTeamOverview()owns the watcher rather than themodule, so only the pages that read one issue a request, and the page owns its first
fetch (so
loadingis true before it paints).a "See all events" link, member avatars alongside. Events are filtered out of the
get_my_eventsfeed viauseMyEvents()in the newdata/events.ts; no second endpoint.MyEvents.vuenow shares that fetcher instead of declaring its own.PageHeader. The target sits inManagerLayoutrather thanDesktopShell's, so it renders inside the rounded panel (theregistry is a stack, last target wins). The panel owns its scroll, hence
scroll=false.Gotchas:
/manage/teamredirects to the overview so the bare path isn't a dead end;team/:sectionsends unbuilt team pages to
WorkInProgress, ordered ahead of the:pathMatch(.*)*404.the check that matters.
the recipient last used. Deliberate, per review.
developafter feat(dashboard): my talk proposals timeline at /manage/proposals #350 merged. PRs feat(dashboard): team members page with add and remove #352–feat(dashboard): a registrations section for an event #357 sit on top and were migrated with it.Demo
Testing
bench --site testbuzz.localhost run-tests --module buzz.api.teams.test_teams— 9/9,covering member-only access, enabled-members filtering and the
NotATeamMemberrefusal.yarn typecheckclean at every branch tip in the stack;yarn test:unit(93) andyarn buildclean.buzz.localhostcan't run tests at all — itsbefore_testsdies on[Event Venue, Test Venue]: team, unrelated to this.🤖 Generated with Claude Code