Unified · Split
| @@ -1,10 +1,5 @@ | ||
| 1 | −import { AppShell } from "@/components/app-shell"; | |
| 2 | 1 | import { ApprovalsView } from "@/components/approvals-view"; |
| 3 | 2 | |
| 4 | 3 | export default function ApprovalsPage() { |
| 5 | − return ( | |
| 6 | − <AppShell> | |
| 7 | − <ApprovalsView /> | |
| 8 | − </AppShell> | |
| 9 | − ); | |
| 4 | + return <ApprovalsView />; | |
| 10 | 5 | } |
| @@ -1,10 +1,5 @@ | ||
| 1 | −import { AppShell } from "@/components/app-shell"; | |
| 2 | 1 | import { BillingView } from "@/components/billing-view"; |
| 3 | 2 | |
| 4 | 3 | export default function BillingPage() { |
| 5 | − return ( | |
| 6 | − <AppShell> | |
| 7 | − <BillingView /> | |
| 8 | − </AppShell> | |
| 9 | − ); | |
| 4 | + return <BillingView />; | |
| 10 | 5 | } |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import type { Metadata } from "next"; |
| 2 | 2 | import { JetBrains_Mono, Inter } from "next/font/google"; |
| 3 | 3 | import { Toaster } from "sonner"; |
| 4 | +import { AppShell } from "@/components/app-shell"; | |
| 4 | 5 | import { AppConvexProvider } from "@/lib/convex-provider"; |
| 5 | 6 | import { QueryProvider } from "@/lib/query-provider"; |
| 6 | 7 | import { SessionProvider } from "@/lib/session-context"; |
| @@ -34,7 +35,7 @@ export default function RootLayout({ · 27 unmodified lines | ||
| 34 | 35 | <AppConvexProvider> |
| 35 | 36 | <QueryProvider> |
| 36 | 37 | <SessionProvider> |
| 37 | − {children} | |
| 38 | + <AppShell>{children}</AppShell> | |
| 38 | 39 | </SessionProvider> |
| 39 | 40 | </QueryProvider> |
| 40 | 41 | </AppConvexProvider> |
| @@ -1,10 +1,5 @@ | ||
| 1 | −import { AppShell } from "@/components/app-shell"; | |
| 2 | 1 | import { MembersView } from "@/components/members-view"; |
| 3 | 2 | |
| 4 | 3 | export default function MembersPage() { |
| 5 | − return ( | |
| 6 | − <AppShell> | |
| 7 | − <MembersView /> | |
| 8 | − </AppShell> | |
| 9 | − ); | |
| 4 | + return <MembersView />; | |
| 10 | 5 | } |
| @@ -1,10 +1,5 @@ | ||
| 1 | −import { AppShell } from "@/components/app-shell"; | |
| 2 | 1 | import { DashboardView } from "@/components/dashboard-view"; |
| 3 | 2 | |
| 4 | 3 | export default function DashboardPage() { |
| 5 | − return ( | |
| 6 | − <AppShell> | |
| 7 | − <DashboardView /> | |
| 8 | − </AppShell> | |
| 9 | − ); | |
| 4 | + return <DashboardView />; | |
| 10 | 5 | } |
| @@ -1,13 +1,10 @@ | ||
| 1 | 1 | import { Suspense } from "react"; |
| 2 | −import { AppShell } from "@/components/app-shell"; | |
| 3 | 2 | import { TasksView } from "@/components/tasks-view"; |
| 4 | 3 | |
| 5 | 4 | export default function TasksPage() { |
| 6 | 5 | return ( |
| 7 | − <AppShell> | |
| 8 | − <Suspense> | |
| 9 | − <TasksView /> | |
| 10 | − </Suspense> | |
| 11 | − </AppShell> | |
| 6 | + <Suspense> | |
| 7 | + <TasksView /> | |
| 8 | + </Suspense> | |
| 12 | 9 | ); |
| 13 | 10 | } |
| @@ -1,10 +1,5 @@ | ||
| 1 | −import { AppShell } from "@/components/app-shell"; | |
| 2 | 1 | import { ToolsView } from "@/components/tools-view"; |
| 3 | 2 | |
| 4 | 3 | export default function ToolsPage() { |
| 5 | − return ( | |
| 6 | − <AppShell> | |
| 7 | − <ToolsView /> | |
| 8 | − </AppShell> | |
| 9 | − ); | |
| 4 | + return <ToolsView />; | |
| 10 | 5 | } |
| @@ -84,8 +84,6 @@ function WorkspaceSelector({ inHeader = false }: { inHeader?: boolean }) { · 83 unmodified lines | ||
| 84 | 84 | context, |
| 85 | 85 | mode, |
| 86 | 86 | clientConfig, |
| 87 | − organizations, | |
| 88 | − selectedOrganizationId, | |
| 89 | 87 | workspaces, |
| 90 | 88 | switchWorkspace, |
| 91 | 89 | creatingWorkspace, |
| @@ -99,25 +97,9 @@ function WorkspaceSelector({ inHeader = false }: { inHeader?: boolean }) { · 7 unmodified lines | ||
| 99 | 97 | const activeWorkspace = context |
| 100 | 98 | ? workspaces.find((workspace) => workspace.id === context.workspaceId) |
| 101 | 99 | : null; |
| 102 | − const currentOrganizationId = activeWorkspace?.organizationId ?? selectedOrganizationId; | |
| 103 | − const activeOrganization = | |
| 104 | − currentOrganizationId | |
| 105 | − ? organizations.find((organization) => organization.id === currentOrganizationId) ?? null | |
| 106 | − : null; | |
| 107 | 100 | const activeWorkspaceLabel = activeWorkspace?.name ?? (mode === "guest" ? "Guest Workspace" : "Select workspace"); |
| 108 | − const activeOrganizationLabel = activeOrganization?.name | |
| 109 | − ?? (activeWorkspace?.kind === "organization" ? "Organization" : mode === "guest" ? "Guest" : "Personal"); | |
| 110 | 101 | const activeWorkspaceInitial = (activeWorkspaceLabel[0] ?? "W").toUpperCase(); |
| 111 | 102 | |
| 112 | − const organizationGroups = organizations | |
| 113 | − .map((organization) => ({ | |
| 114 | − organization, | |
| 115 | − workspaces: workspaces.filter((workspace) => workspace.organizationId === organization.id), | |
| 116 | − })) | |
| 117 | − .filter((group) => group.workspaces.length > 0); | |
| 118 | − const personalWorkspaces = workspaces.filter((workspace) => workspace.organizationId === null); | |
| 119 | − const showOrganizationHeaders = organizationGroups.length > 1; | |
| 120 | − | |
| 121 | 103 | const openCreateWorkspace = () => { |
| 122 | 104 | setCreateError(null); |
| 123 | 105 | setNewWorkspaceName(""); |
| @@ -172,9 +154,6 @@ function WorkspaceSelector({ inHeader = false }: { inHeader?: boolean }) { · 48 unmodified lines | ||
| 172 | 154 | )} |
| 173 | 155 | <span className="min-w-0"> |
| 174 | 156 | <span className="truncate block">{activeWorkspaceLabel}</span> |
| 175 | − {mode === "workos" ? ( | |
| 176 | − <span className="truncate block text-[10px] text-muted-foreground">{activeOrganizationLabel}</span> | |
| 177 | − ) : null} | |
| 178 | 157 | </span> |
| 179 | 158 | </span> |
| 180 | 159 | <ChevronsUpDown className="h-3.5 w-3.5 text-muted-foreground" /> |
| @@ -184,75 +163,32 @@ function WorkspaceSelector({ inHeader = false }: { inHeader?: boolean }) { · 3 unmodified lines | ||
| 184 | 163 | {mode === "workos" |
| 185 | 164 | ? ( |
| 186 | 165 | <> |
| 187 | − {organizationGroups.map((group, index) => ( | |
| 188 | − <div key={group.organization.id}> | |
| 189 | − {showOrganizationHeaders ? ( | |
| 190 | − <DropdownMenuLabel className="text-[10px] uppercase tracking-wide text-muted-foreground"> | |
| 191 | − {group.organization.name} | |
| 192 | − </DropdownMenuLabel> | |
| 193 | − ) : null} | |
| 194 | − {group.workspaces.map((workspace) => { | |
| 195 | − const isActive = workspace.id === context?.workspaceId; | |
| 196 | − return ( | |
| 197 | − <DropdownMenuItem | |
| 198 | − key={workspace.id} | |
| 199 | − onSelect={() => switchWorkspace(workspace.id)} | |
| 200 | − className="text-xs" | |
| 201 | − > | |
| 202 | − <Check className={cn("mr-2 h-3.5 w-3.5", isActive ? "opacity-100" : "opacity-0")} /> | |
| 203 | − {workspace.iconUrl ? ( | |
| 204 | − <img | |
| 205 | − src={workspace.iconUrl} | |
| 206 | − alt={workspace.name} | |
| 207 | − className="mr-2 h-4 w-4 rounded-sm border border-border object-cover" | |
| 208 | − /> | |
| 209 | − ) : ( | |
| 210 | − <span className="mr-2 h-4 w-4 rounded-sm border border-border bg-muted text-[9px] font-semibold flex items-center justify-center text-muted-foreground"> | |
| 211 | − {(workspace.name[0] ?? "W").toUpperCase()} | |
| 212 | − </span> | |
| 213 | − )} | |
| 214 | − <span className="truncate">{workspace.name}</span> | |
| 215 | − </DropdownMenuItem> | |
| 216 | − ); | |
| 217 | − })} | |
| 218 | − {showOrganizationHeaders && index < organizationGroups.length - 1 ? <DropdownMenuSeparator /> : null} | |
| 219 | − </div> | |
| 220 | − ))} | |
| 221 | − | |
| 222 | − {personalWorkspaces.length > 0 ? ( | |
| 223 | − <> | |
| 224 | − {organizationGroups.length > 0 ? <DropdownMenuSeparator /> : null} | |
| 225 | − <DropdownMenuLabel className="text-[10px] uppercase tracking-wide text-muted-foreground"> | |
| 226 | − Personal | |
| 227 | − </DropdownMenuLabel> | |
| 228 | − {personalWorkspaces.map((workspace) => { | |
| 229 | − const isActive = workspace.id === context?.workspaceId; | |
| 230 | − return ( | |
| 231 | − <DropdownMenuItem | |
| 232 | − key={workspace.id} | |
| 233 | − onSelect={() => switchWorkspace(workspace.id)} | |
| 234 | − className="text-xs" | |
| 235 | − > | |
| 236 | − <Check className={cn("mr-2 h-3.5 w-3.5", isActive ? "opacity-100" : "opacity-0")} /> | |
| 237 | − {workspace.iconUrl ? ( | |
| 238 | − <img | |
| 239 | − src={workspace.iconUrl} | |
| 240 | − alt={workspace.name} | |
| 241 | − className="mr-2 h-4 w-4 rounded-sm border border-border object-cover" | |
| 242 | − /> | |
| 243 | − ) : ( | |
| 244 | − <span className="mr-2 h-4 w-4 rounded-sm border border-border bg-muted text-[9px] font-semibold flex items-center justify-center text-muted-foreground"> | |
| 245 | − {(workspace.name[0] ?? "W").toUpperCase()} | |
| 246 | − </span> | |
| 247 | − )} | |
| 248 | − <span className="truncate">{workspace.name}</span> | |
| 249 | − </DropdownMenuItem> | |
| 250 | − ); | |
| 251 | − })} | |
| 252 | − </> | |
| 253 | − ) : null} | |
| 166 | + {workspaces.map((workspace) => { | |
| 167 | + const isActive = workspace.id === context?.workspaceId; | |
| 168 | + return ( | |
| 169 | + <DropdownMenuItem | |
| 170 | + key={workspace.id} | |
| 171 | + onSelect={() => switchWorkspace(workspace.id)} | |
| 172 | + className="text-xs" | |
| 173 | + > | |
| 174 | + <Check className={cn("mr-2 h-3.5 w-3.5", isActive ? "opacity-100" : "opacity-0")} /> | |
| 175 | + {workspace.iconUrl ? ( | |
| 176 | + <img | |
| 177 | + src={workspace.iconUrl} | |
| 178 | + alt={workspace.name} | |
| 179 | + className="mr-2 h-4 w-4 rounded-sm border border-border object-cover" | |
| 180 | + /> | |
| 181 | + ) : ( | |
| 182 | + <span className="mr-2 h-4 w-4 rounded-sm border border-border bg-muted text-[9px] font-semibold flex items-center justify-center text-muted-foreground"> | |
| 183 | + {(workspace.name[0] ?? "W").toUpperCase()} | |
| 184 | + </span> | |
| 185 | + )} | |
| 186 | + <span className="truncate">{workspace.name}</span> | |
| 187 | + </DropdownMenuItem> | |
| 188 | + ); | |
| 189 | + })} | |
| 254 | 190 | |
| 255 | − {organizationGroups.length === 0 && personalWorkspaces.length === 0 ? ( | |
| 191 | + {workspaces.length === 0 ? ( | |
| 256 | 192 | <DropdownMenuItem disabled className="text-xs"> |
| 257 | 193 | No workspaces |
| 258 | 194 | </DropdownMenuItem> |
| @@ -8,7 +8,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; · 7 unmodified lines | ||
| 8 | 8 | import { useSession } from "@/lib/session-context"; |
| 9 | 9 | |
| 10 | 10 | export function BillingView() { |
| 11 | − const { context, organizations, selectedOrganizationId, workspaces } = useSession(); | |
| 11 | + const { context, organizations, workspaces } = useSession(); | |
| 12 | 12 | const [actionState, setActionState] = useState<"idle" | "running" | "error">("idle"); |
| 13 | 13 | const [actionMessage, setActionMessage] = useState<string | null>(null); |
| 14 | 14 | |
| @@ -18,8 +18,8 @@ export function BillingView() { · 3 unmodified lines | ||
| 18 | 18 | const activeWorkspace = context |
| 19 | 19 | ? workspaces.find((workspace) => workspace.id === context.workspaceId) ?? null |
| 20 | 20 | : null; |
| 21 | − const effectiveOrganizationId = derivedOrganizationId ?? selectedOrganizationId; | |
| 22 | − const hasLegacyOrganizationWorkspace = Boolean(activeWorkspace) && !effectiveOrganizationId; | |
| 21 | + const effectiveOrganizationId = derivedOrganizationId; | |
| 22 | + const hasLegacyOrganizationWorkspace = activeWorkspace?.kind === "organization" && !effectiveOrganizationId; | |
| 23 | 23 | |
| 24 | 24 | const activeOrganization = useMemo( |
| 25 | 25 | () => organizations.find((organization) => organization.id === effectiveOrganizationId) ?? null, |
| @@ -40,7 +40,7 @@ export function BillingView() { · 14 unmodified lines | ||
| 40 | 40 | <PageHeader title="Billing" description="Plan, seats, and subscription management" /> |
| 41 | 41 | <Card> |
| 42 | 42 | <CardContent className="p-6 text-sm text-muted-foreground"> |
| 43 | − Select an organization from the switcher to manage billing. | |
| 43 | + Select a workspace to manage billing. | |
| 44 | 44 | </CardContent> |
| 45 | 45 | </Card> |
| 46 | 46 | </div> |
| @@ -53,8 +53,8 @@ export function BillingView() { · 6 unmodified lines | ||
| 53 | 53 | <PageHeader title="Billing" description="Plan, seats, and subscription management" /> |
| 54 | 54 | <Card> |
| 55 | 55 | <CardContent className="p-6 text-sm text-muted-foreground"> |
| 56 | − Billing is waiting for an organization record for this workspace. The active workspace | |
| 57 | − does not have a linked `organizationId` yet. | |
| 56 | + Billing is waiting for workspace provisioning to finish. This workspace is missing its | |
| 57 | + internal organization link. | |
| 58 | 58 | </CardContent> |
| 59 | 59 | </Card> |
| 60 | 60 | </div> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | "use client"; |
| 2 | 2 | |
| 3 | −import { useMemo, useState } from "react"; | |
| 3 | +import { useState } from "react"; | |
| 4 | 4 | import { useMutation, useQuery } from "convex/react"; |
| 5 | 5 | import { Mail, UserMinus, Users } from "lucide-react"; |
| 6 | 6 | import { PageHeader } from "@/components/page-header"; |
| @@ -18,8 +18,6 @@ export function MembersView() { · 11 unmodified lines | ||
| 18 | 18 | const { |
| 19 | 19 | context, |
| 20 | 20 | clientConfig, |
| 21 | − organizations, | |
| 22 | − selectedOrganizationId, | |
| 23 | 21 | workspaces, |
| 24 | 22 | } = useSession(); |
| 25 | 23 | |
| @@ -30,8 +28,7 @@ export function MembersView() { · 4 unmodified lines | ||
| 30 | 28 | ? workspaces.find((workspace) => workspace.id === context.workspaceId) ?? null |
| 31 | 29 | : null; |
| 32 | 30 | const typedOrganizationId = derivedOrganizationId; |
| 33 | − const effectiveOrganizationId = typedOrganizationId ?? selectedOrganizationId; | |
| 34 | − const hasLegacyOrganizationWorkspace = Boolean(activeWorkspace) && !typedOrganizationId; | |
| 31 | + const hasLegacyOrganizationWorkspace = activeWorkspace?.kind === "organization" && !typedOrganizationId; | |
| 35 | 32 | |
| 36 | 33 | const members = useQuery( |
| 37 | 34 | convexApi.organizationMembers.list, |
| @@ -43,6 +40,13 @@ export function MembersView() { · 5 unmodified lines | ||
| 43 | 40 | const updateRole = useMutation(convexApi.organizationMembers.updateRole); |
| 44 | 41 | const updateBillable = useMutation(convexApi.organizationMembers.updateBillable); |
| 45 | 42 | const removeMember = useMutation(convexApi.organizationMembers.remove); |
| 43 | + const listInvites = useQuery( | |
| 44 | + convexApi.invites.list, | |
| 45 | + typedOrganizationId | |
| 46 | + ? { organizationId: typedOrganizationId, sessionId: context?.sessionId ?? undefined } | |
| 47 | + : "skip", | |
| 48 | + ); | |
| 49 | + const createInvite = useMutation(convexApi.invites.create); | |
| 46 | 50 | |
| 47 | 51 | const [inviteEmail, setInviteEmail] = useState(""); |
| 48 | 52 | const [inviteRole, setInviteRole] = useState<Role>("member"); |
| @@ -50,20 +54,14 @@ export function MembersView() { · 1 unmodified line | ||
| 50 | 54 | const [inviteMessage, setInviteMessage] = useState<string | null>(null); |
| 51 | 55 | const [busyMemberAccountId, setBusyMemberAccountId] = useState<string | null>(null); |
| 52 | 56 | |
| 53 | − const activeOrganization = useMemo( | |
| 54 | − () => organizations.find((organization) => organization.id === effectiveOrganizationId) ?? null, | |
| 55 | − [organizations, effectiveOrganizationId], | |
| 56 | − ); | |
| 57 | − | |
| 58 | − const canManageMembers = activeOrganization | |
| 59 | − ? activeOrganization.role === "owner" || activeOrganization.role === "admin" | |
| 60 | − : false; | |
| 61 | − const canManageBilling = activeOrganization | |
| 62 | − ? activeOrganization.role === "owner" || activeOrganization.role === "admin" || activeOrganization.role === "billing_admin" | |
| 63 | − : false; | |
| 64 | − | |
| 65 | 57 | const memberItems = members?.items ?? []; |
| 66 | − const inviteItems: Array<{ id: string; email: string; role: string; status: string }> = []; | |
| 58 | + const inviteItems = listInvites?.items ?? []; | |
| 59 | + const actorMembership = memberItems.find((member) => | |
| 60 | + context?.accountId ? String(member.accountId) === context.accountId : false, | |
| 61 | + ); | |
| 62 | + const actorRole = actorMembership?.role ?? null; | |
| 63 | + const canManageMembers = actorRole === "owner" || actorRole === "admin"; | |
| 64 | + const canManageBilling = actorRole === "owner" || actorRole === "admin" || actorRole === "billing_admin"; | |
| 67 | 65 | |
| 68 | 66 | const submitInvite = async () => { |
| 69 | 67 | if (!typedOrganizationId) { |
| @@ -72,10 +70,17 @@ export function MembersView() { · 2 unmodified lines | ||
| 72 | 70 | setInviteState("sending"); |
| 73 | 71 | setInviteMessage(null); |
| 74 | 72 | try { |
| 75 | − await new Promise((resolve) => setTimeout(resolve, 300)); | |
| 76 | − const deliveryProvider = clientConfig?.invitesProvider ?? "local"; | |
| 77 | − setInviteState("failed"); | |
| 78 | − setInviteMessage(`Invite creation via ${deliveryProvider} is not wired yet on backend.`); | |
| 73 | + const result = await createInvite({ | |
| 74 | + organizationId: typedOrganizationId, | |
| 75 | + email: inviteEmail.trim(), | |
| 76 | + role: inviteRole, | |
| 77 | + workspaceId: activeWorkspace?.docId ?? undefined, | |
| 78 | + sessionId: context?.sessionId ?? undefined, | |
| 79 | + }); | |
| 80 | + setInviteState("sent"); | |
| 81 | + const deliveryProvider = result.delivery.provider; | |
| 82 | + setInviteMessage(`Invite ${result.delivery.state} via ${deliveryProvider}.`); | |
| 83 | + setInviteEmail(""); | |
| 79 | 84 | } catch (error) { |
| 80 | 85 | setInviteState("failed"); |
| 81 | 86 | setInviteMessage(error instanceof Error ? error.message : "Failed to send invite"); |
| @@ -88,7 +93,7 @@ export function MembersView() { · 6 unmodified lines | ||
| 88 | 93 | <PageHeader title="Members" description="Manage organization membership and invites" /> |
| 89 | 94 | <Card> |
| 90 | 95 | <CardContent className="p-6 text-sm text-muted-foreground"> |
| 91 | − Select an organization from the switcher to manage members. | |
| 96 | + Select a workspace to manage members. | |
| 92 | 97 | </CardContent> |
| 93 | 98 | </Card> |
| 94 | 99 | </div> |
| @@ -101,8 +106,8 @@ export function MembersView() { · 6 unmodified lines | ||
| 101 | 106 | <PageHeader title="Members" description="Manage organization membership and invites" /> |
| 102 | 107 | <Card> |
| 103 | 108 | <CardContent className="p-6 text-sm text-muted-foreground"> |
| 104 | − Member management is waiting for an organization record for this workspace. The active | |
| 105 | − workspace does not have a linked `organizationId` yet. | |
| 109 | + Member management is waiting for workspace provisioning to finish. This workspace is | |
| 110 | + missing its internal organization link. | |
| 106 | 111 | </CardContent> |
| 107 | 112 | </Card> |
| 108 | 113 | </div> |
| @@ -166,7 +171,7 @@ export function MembersView() { · 57 unmodified lines | ||
| 166 | 171 | <CardHeader> |
| 167 | 172 | <CardTitle className="text-sm flex items-center gap-2"> |
| 168 | 173 | <Users className="h-4 w-4" /> |
| 169 | − Organization Members | |
| 174 | + Workspace Members | |
| 170 | 175 | </CardTitle> |
| 171 | 176 | </CardHeader> |
| 172 | 177 | <CardContent className="space-y-3"> |
| @@ -189,6 +194,9 @@ export function MembersView() { · 16 unmodified lines | ||
| 189 | 194 | onChange={async (event) => { |
| 190 | 195 | setBusyMemberAccountId(member.accountId); |
| 191 | 196 | try { |
| 197 | + if (!typedOrganizationId) { | |
| 198 | + return; | |
| 199 | + } | |
| 192 | 200 | await updateRole({ |
| 193 | 201 | organizationId: typedOrganizationId, |
| 194 | 202 | accountId: member.accountId, |
| @@ -215,6 +223,9 @@ export function MembersView() { · 20 unmodified lines | ||
| 215 | 223 | onClick={async () => { |
| 216 | 224 | setBusyMemberAccountId(member.accountId); |
| 217 | 225 | try { |
| 226 | + if (!typedOrganizationId) { | |
| 227 | + return; | |
| 228 | + } | |
| 218 | 229 | await updateBillable({ |
| 219 | 230 | organizationId: typedOrganizationId, |
| 220 | 231 | accountId: member.accountId, |
| @@ -237,6 +248,9 @@ export function MembersView() { · 16 unmodified lines | ||
| 237 | 248 | onClick={async () => { |
| 238 | 249 | setBusyMemberAccountId(member.accountId); |
| 239 | 250 | try { |
| 251 | + if (!typedOrganizationId) { | |
| 252 | + return; | |
| 253 | + } | |
| 240 | 254 | await removeMember({ |
| 241 | 255 | organizationId: typedOrganizationId, |
| 242 | 256 | accountId: member.accountId, |
| @@ -20,8 +20,8 @@ interface SessionState { · 19 unmodified lines | ||
| 20 | 20 | loading: boolean; |
| 21 | 21 | error: string | null; |
| 22 | 22 | clientConfig: { |
| 23 | − authProviderMode: "workos" | "local"; | |
| 24 | − invitesProvider: "workos" | "local"; | |
| 23 | + authProviderMode: string; | |
| 24 | + invitesProvider: string; | |
| 25 | 25 | features: { |
| 26 | 26 | organizations: boolean; |
| 27 | 27 | billing: boolean; |
| @@ -40,6 +40,7 @@ interface SessionState { · 12 unmodified lines | ||
| 40 | 40 | switchOrganization: (organizationId: string | null) => void; |
| 41 | 41 | workspaces: Array<{ |
| 42 | 42 | id: string; |
| 43 | + docId: Id<"workspaces"> | null; | |
| 43 | 44 | name: string; |
| 44 | 45 | kind: "organization" | "personal" | "anonymous"; |
| 45 | 46 | organizationId: Id<"organizations"> | null; |
| @@ -236,7 +237,7 @@ export function SessionProvider({ children }: { children: ReactNode }) { · 190 unmodified lines | ||
| 236 | 237 | useEffect(() => { |
| 237 | 238 | if ( |
| 238 | 239 | !workosEnabled |
| 239 | − || account !== null | |
| 240 | + || account === undefined | |
| 240 | 241 | || bootstrappingWorkos |
| 241 | 242 | || workosBootstrapAttempted |
| 242 | 243 | ) { |
| @@ -333,7 +334,7 @@ export function SessionProvider({ children }: { children: ReactNode }) { · 90 unmodified lines | ||
| 333 | 334 | setCreatingWorkspace(true); |
| 334 | 335 | setError(null); |
| 335 | 336 | try { |
| 336 | − let iconStorageId: string | undefined; | |
| 337 | + let iconStorageId: Id<"_storage"> | undefined; | |
| 337 | 338 | |
| 338 | 339 | if (iconFile) { |
| 339 | 340 | const uploadUrl = await generateWorkspaceIconUploadUrl({ |
| @@ -356,7 +357,7 @@ export function SessionProvider({ children }: { children: ReactNode }) { · 16 unmodified lines | ||
| 356 | 357 | if (!json.storageId) { |
| 357 | 358 | throw new Error("Upload did not return a storage id"); |
| 358 | 359 | } |
| 359 | − iconStorageId = json.storageId; | |
| 360 | + iconStorageId = json.storageId as Id<"_storage">; | |
| 360 | 361 | } |
| 361 | 362 | |
| 362 | 363 | const created = await createWorkspaceMutation({ |
| @@ -423,8 +424,9 @@ export function SessionProvider({ children }: { children: ReactNode }) { · 60 unmodified lines | ||
| 423 | 424 | ); |
| 424 | 425 | const workspaceOptions = useMemo(() => { |
| 425 | 426 | if (mode === "workos" && workspaces) { |
| 426 | − return workspaces.map((workspace) => ({ | |
| 427 | + return workspaces.map((workspace): SessionState["workspaces"][number] => ({ | |
| 427 | 428 | id: workspace.runtimeWorkspaceId, |
| 429 | + docId: workspace._id, | |
| 428 | 430 | name: workspace.name, |
| 429 | 431 | kind: |
| 430 | 432 | workspace.kind === "organization" || workspace.kind === "org" |
| @@ -441,6 +443,7 @@ export function SessionProvider({ children }: { children: ReactNode }) { · 10 unmodified lines | ||
| 441 | 443 | return [ |
| 442 | 444 | { |
| 443 | 445 | id: guestContext.workspaceId, |
| 446 | + docId: null, | |
| 444 | 447 | name: "Guest Workspace", |
| 445 | 448 | kind: "anonymous" as const, |
| 446 | 449 | organizationId: null, |