11import { CheckIcon , XMarkIcon } from "@heroicons/react/20/solid" ;
22import { type MetaFunction } from "@remix-run/react" ;
33import { useState } from "react" ;
4- import {
5- type UseDataFunctionReturn ,
6- typedjson ,
7- useTypedLoaderData ,
8- } from "remix-typedjson" ;
4+ import { type UseDataFunctionReturn , typedjson , useTypedLoaderData } from "remix-typedjson" ;
95import { z } from "zod" ;
106import { PageBody , PageContainer } from "~/components/layout/AppLayout" ;
117import { Badge } from "~/components/primitives/Badge" ;
128import { Button } from "~/components/primitives/Buttons" ;
13- import {
14- Dialog ,
15- DialogContent ,
16- DialogHeader ,
17- DialogTrigger ,
18- } from "~/components/primitives/Dialog" ;
9+ import { Dialog , DialogContent , DialogHeader , DialogTrigger } from "~/components/primitives/Dialog" ;
1910import { Header3 } from "~/components/primitives/Headers" ;
2011import { NavBar , PageTitle } from "~/components/primitives/PageHeader" ;
2112import { Paragraph } from "~/components/primitives/Paragraph" ;
@@ -32,10 +23,9 @@ import { cn } from "~/utils/cn";
3223import { $replica } from "~/db.server" ;
3324import { useOrganization } from "~/hooks/useOrganizations" ;
3425import { rbac } from "~/services/rbac.server" ;
35- import {
36- dashboardLoader ,
37- } from "~/services/routeBuilders/dashboardBuilder" ;
26+ import { dashboardLoader } from "~/services/routeBuilders/dashboardBuilder" ;
3827import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route" ;
28+ import { TextLink } from "~/components/primitives/TextLink" ;
3929
4030export const meta : MetaFunction = ( ) => {
4131 return [
@@ -72,13 +62,12 @@ export const loader = dashboardLoader(
7262 throw new Response ( "Not Found" , { status : 404 } ) ;
7363 }
7464
75- const [ roles , assignableRoleIds , allPermissions , systemRoleIds ] =
76- await Promise . all ( [
77- rbac . allRoles ( orgId ) ,
78- rbac . getAssignableRoleIds ( orgId ) ,
79- rbac . allPermissions ( orgId ) ,
80- rbac . systemRoleIds ( ) ,
81- ] ) ;
65+ const [ roles , assignableRoleIds , allPermissions , systemRoleIds ] = await Promise . all ( [
66+ rbac . allRoles ( orgId ) ,
67+ rbac . getAssignableRoleIds ( orgId ) ,
68+ rbac . allPermissions ( orgId ) ,
69+ rbac . systemRoleIds ( ) ,
70+ ] ) ;
8271
8372 return typedjson ( {
8473 roles,
@@ -160,15 +149,14 @@ export default function Page() {
160149 // custom roles in the order rbac.allRoles returned them. systemRoleIds
161150 // is null when no plugin is installed — there are no system roles to
162151 // pin; fall through to whatever order rbac.allRoles returns.
163- const systemRoleOrder : ReadonlyArray < { id : string ; name : string } > =
164- systemRoleIds
165- ? [
166- { id : systemRoleIds . owner , name : "Owner" } ,
167- { id : systemRoleIds . admin , name : "Admin" } ,
168- { id : systemRoleIds . developer , name : "Developer" } ,
169- { id : systemRoleIds . member , name : "Member" } ,
170- ]
171- : [ ] ;
152+ const systemRoleOrder : ReadonlyArray < { id : string ; name : string } > = systemRoleIds
153+ ? [
154+ { id : systemRoleIds . owner , name : "Owner" } ,
155+ { id : systemRoleIds . admin , name : "Admin" } ,
156+ { id : systemRoleIds . developer , name : "Developer" } ,
157+ { id : systemRoleIds . member , name : "Member" } ,
158+ ]
159+ : [ ] ;
172160 const systemRoleIdSet = new Set ( systemRoleOrder . map ( ( r ) => r . id ) ) ;
173161 const systemColumns = systemRoleOrder . flatMap ( ( meta ) => {
174162 const role = rolesById . get ( meta . id ) ;
@@ -190,17 +178,10 @@ export default function Page() {
190178 < PageBody scrollable = { false } >
191179 < div className = "grid max-h-full min-h-full grid-rows-[auto_1fr]" >
192180 < div className = "border-b border-grid-bright px-4 py-6" >
193- < Paragraph >
194- Roles control what each team member can do in{ " " }
195- < strong > { organization . title } </ strong > . Compare what each role
196- grants below; assign a role to a team member from the{ " " }
197- < a
198- className = "text-text-link hover:underline"
199- href = { `/orgs/${ organization . slug } /settings/team` }
200- >
201- Team page
202- </ a >
203- .
181+ < Paragraph variant = "small" >
182+ Roles control what each team member can do in < strong > { organization . title } </ strong > .
183+ Compare what each role grants below; assign a role to a team member from the{ " " }
184+ < TextLink to = { `/orgs/${ organization . slug } /settings/team` } > Team page</ TextLink > .
204185 </ Paragraph >
205186 </ div >
206187 < div className = "overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600" >
@@ -236,10 +217,7 @@ export default function Page() {
236217 ) : (
237218 grouped . flatMap ( ( { group, permissions } ) => [
238219 < TableRow key = { `${ group } -header` } >
239- < TableCell
240- colSpan = { columns . length + 2 }
241- className = "bg-charcoal-800"
242- >
220+ < TableCell colSpan = { columns . length + 2 } className = "bg-charcoal-800" >
243221 < Header3 className = "text-xs uppercase tracking-wide text-text-dimmed" >
244222 { group }
245223 </ Header3 >
@@ -305,10 +283,7 @@ function PlanBadge({
305283 if ( assignable . has ( roleId ) ) return null ;
306284 // System role gating: Owner+Admin always available; Member/Developer
307285 // only on Pro+; custom roles only on Enterprise.
308- if (
309- systemRoleIds &&
310- ( roleId === systemRoleIds . member || roleId === systemRoleIds . developer )
311- ) {
286+ if ( systemRoleIds && ( roleId === systemRoleIds . member || roleId === systemRoleIds . developer ) ) {
312287 return < Badge variant = "extra-small" > Pro</ Badge > ;
313288 }
314289 return < Badge variant = "extra-small" > Enterprise</ Badge > ;
@@ -355,9 +330,7 @@ function RoleCell({
355330 const conditionalDeny = denied . find ( ( p ) => p . conditions ) ;
356331 if ( conditionalDeny ?. conditions ) {
357332 return (
358- < span className = "text-xs text-text-dimmed" >
359- { conditionLabel ( conditionalDeny . conditions ) }
360- </ span >
333+ < span className = "text-xs text-text-dimmed" > { conditionLabel ( conditionalDeny . conditions ) } </ span >
361334 ) ;
362335 }
363336 return (
@@ -405,13 +378,12 @@ function CreateRoleUpsell() {
405378 < DialogHeader > Custom roles are an Enterprise feature</ DialogHeader >
406379 < div className = "flex flex-col gap-3 pt-2" >
407380 < Paragraph >
408- Define your own roles with bespoke permission sets — perfect for
409- "Member, but no production deploys" or a vendor/contractor role.
410- Available on the Enterprise plan.
381+ Define your own roles with bespoke permission sets — perfect for "Member, but no
382+ production deploys" or a vendor/contractor role. Available on the Enterprise plan.
411383 </ Paragraph >
412384 < Paragraph variant = "small" className = "text-text-dimmed" >
413- Get in touch and we'll walk you through the Enterprise plan and how
414- custom roles fit your team.
385+ Get in touch and we'll walk you through the Enterprise plan and how custom roles fit
386+ your team.
415387 </ Paragraph >
416388 </ div >
417389 < div className = "mt-6 flex justify-end gap-2" >
0 commit comments