History

feat(cloud): add team management page (#157)

* feat(cloud): add team management page with WorkOS org members Custom team page using WorkOS API for listing members, inviting, removing, and changing roles. Admin-gated write operations with server-side role checks. Uses Effect API layer pattern with typed endpoints and effect-atom for client-side state. Also fixes Sonner toaster to use system theme detection instead of next-themes, removes next-themes dependency. * style: format * chore: remove unused widget token endpoint and getWidgetToken * fix: remove any types from team handler tests * style: format * fix: add @types/node to storage-postgres for tsgo compat * fix: resolve merge conflict in test file * style: format * fix: resolve tsgo typecheck errors in billing and test files * fix: add explicit Plan types for tsgo compatibility * style: format * fix: cast empty array for tsgo type inference

Rhys Sullivan committed 7 months ago

Commit ec8d8861 parent 261c7b219 files · +1079 −16Browse files

Showing the changes to apps/cloud/src/web (3 of 19 files). Show all

Unified · Split

3 files
  1. apps/cloud/src/web/client.tsx +3 −2
  2. apps/cloud/src/web/shell.tsx +2 −0
  3. apps/cloud/src/web/team-atoms.ts +15 −0
apps/cloud/src/web/client.tsx+3−2
@@ -3,12 +3,13 @@ import { FetchHttpClient } from "@effect/platform"; · 2 unmodified lines
3import { addGroup } from "@executor/api";3import { addGroup } from "@executor/api";
4import { getBaseUrl } from "@executor/react/api/base-url";4import { getBaseUrl } from "@executor/react/api/base-url";
5import { CloudAuthApi } from "../auth/api";5import { CloudAuthApi } from "../auth/api";
6import { TeamApi } from "../team/api";
6 7
7// ---------------------------------------------------------------------------8// ---------------------------------------------------------------------------
8// Cloud API client — core API + cloud auth9// Cloud API client — core API + cloud auth + team
9// ---------------------------------------------------------------------------10// ---------------------------------------------------------------------------
10 11
11const CloudApi = addGroup(CloudAuthApi);12const CloudApi = addGroup(CloudAuthApi).add(TeamApi);
12 13
13class CloudApiClient extends AtomHttpApi.Tag<CloudApiClient>()("CloudApiClient", {14class CloudApiClient extends AtomHttpApi.Tag<CloudApiClient>()("CloudApiClient", {
14 api: CloudApi,15 api: CloudApi,
apps/cloud/src/web/shell.tsx+2−0
@@ -138,6 +138,7 @@ function SidebarContent(props: { pathname: string; onNavigate?: () => void; show · 137 unmodified lines
138 const isHome = props.pathname === "/";138 const isHome = props.pathname === "/";
139 const isSecrets = props.pathname === "/secrets";139 const isSecrets = props.pathname === "/secrets";
140 const isBilling = props.pathname === "/billing" || props.pathname.startsWith("/billing/");140 const isBilling = props.pathname === "/billing" || props.pathname.startsWith("/billing/");
141 const isTeam = props.pathname === "/team";
141 142
142 return (143 return (
143 <>144 <>
@@ -152,6 +153,7 @@ function SidebarContent(props: { pathname: string; onNavigate?: () => void; show · 8 unmodified lines
152 <nav className="flex flex-1 flex-col overflow-y-auto p-2">153 <nav className="flex flex-1 flex-col overflow-y-auto p-2">
153 <NavItem to="/" label="Sources" active={isHome} onNavigate={props.onNavigate} />154 <NavItem to="/" label="Sources" active={isHome} onNavigate={props.onNavigate} />
154 <NavItem to="/secrets" label="Secrets" active={isSecrets} onNavigate={props.onNavigate} />155 <NavItem to="/secrets" label="Secrets" active={isSecrets} onNavigate={props.onNavigate} />
156 <NavItem to="/team" label="Team" active={isTeam} onNavigate={props.onNavigate} />
155 <NavItem to="/billing" label="Billing" active={isBilling} onNavigate={props.onNavigate} />157 <NavItem to="/billing" label="Billing" active={isBilling} onNavigate={props.onNavigate} />
156 158
157 <div className="mt-5 mb-1 px-2.5 text-[10px] font-medium uppercase tracking-widest text-muted-foreground/50">159 <div className="mt-5 mb-1 px-2.5 text-[10px] font-medium uppercase tracking-widest text-muted-foreground/50">
apps/cloud/src/web/team-atoms.ts+15−0
@@ -0,0 +1,15 @@
1import { CloudApiClient } from "./client";
2
3export const teamMembersAtom = CloudApiClient.query("team", "listMembers", {
4 timeToLive: "30 seconds",
5});
6
7export const teamRolesAtom = CloudApiClient.query("team", "listRoles", {
8 timeToLive: "5 minutes",
9});
10
11export const inviteMember = CloudApiClient.mutation("team", "invite");
12
13export const removeMember = CloudApiClient.mutation("team", "removeMember");
14
15export const updateMemberRole = CloudApiClient.mutation("team", "updateMemberRole");