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
33 import { addGroup } from "@executor/api";
44 import { getBaseUrl } from "@executor/react/api/base-url";
55 import { CloudAuthApi } from "../auth/api";
6+import { TeamApi } from "../team/api";
67
78 // ---------------------------------------------------------------------------
8−// Cloud API client — core API + cloud auth
9+// Cloud API client — core API + cloud auth + team
910 // ---------------------------------------------------------------------------
1011
11−const CloudApi = addGroup(CloudAuthApi);
12+const CloudApi = addGroup(CloudAuthApi).add(TeamApi);
1213
1314 class CloudApiClient extends AtomHttpApi.Tag<CloudApiClient>()("CloudApiClient", {
1415 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
138138 const isHome = props.pathname === "/";
139139 const isSecrets = props.pathname === "/secrets";
140140 const isBilling = props.pathname === "/billing" || props.pathname.startsWith("/billing/");
141+ const isTeam = props.pathname === "/team";
141142
142143 return (
143144 <>
@@ -152,6 +153,7 @@ function SidebarContent(props: { pathname: string; onNavigate?: () => void; show · 8 unmodified lines
152153 <nav className="flex flex-1 flex-col overflow-y-auto p-2">
153154 <NavItem to="/" label="Sources" active={isHome} onNavigate={props.onNavigate} />
154155 <NavItem to="/secrets" label="Secrets" active={isSecrets} onNavigate={props.onNavigate} />
156+ <NavItem to="/team" label="Team" active={isTeam} onNavigate={props.onNavigate} />
155157 <NavItem to="/billing" label="Billing" active={isBilling} onNavigate={props.onNavigate} />
156158
157159 <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 @@
1+import { CloudApiClient } from "./client";
2+
3+export const teamMembersAtom = CloudApiClient.query("team", "listMembers", {
4+ timeToLive: "30 seconds",
5+});
6+
7+export const teamRolesAtom = CloudApiClient.query("team", "listRoles", {
8+ timeToLive: "5 minutes",
9+});
10+
11+export const inviteMember = CloudApiClient.mutation("team", "invite");
12+
13+export const removeMember = CloudApiClient.mutation("team", "removeMember");
14+
15+export const updateMemberRole = CloudApiClient.mutation("team", "updateMemberRole");