@@ -5,7 +5,7 @@ import { createStore } from "solid-js/store"
55import { formatDateUTC , formatDateForTable } from "./common"
66import styles from "./member-section.module.css"
77import { and , Database , eq , sql } from "@opencode/console-core/drizzle/index.js"
8- import { UserTable } from "@opencode/console-core/schema/user.sql.js"
8+ import { UserTable , UserRole } from "@opencode/console-core/schema/user.sql.js"
99import { Identifier } from "@opencode/console-core/identifier.js"
1010
1111const removeMember = action ( async ( form : FormData ) => {
@@ -31,10 +31,12 @@ const removeMember = action(async (form: FormData) => {
3131
3232const inviteMember = action ( async ( form : FormData ) => {
3333 "use server"
34- const name = form . get ( "name " ) ?. toString ( ) . trim ( )
35- if ( ! name ) return { error : "Name is required" }
34+ const email = form . get ( "email " ) ?. toString ( ) . trim ( )
35+ if ( ! email ) return { error : "Email is required" }
3636 const workspaceID = form . get ( "workspaceID" ) ?. toString ( )
3737 if ( ! workspaceID ) return { error : "Workspace ID is required" }
38+ const role = form . get ( "role" ) ?. toString ( ) as ( typeof UserRole ) [ number ]
39+ if ( ! role ) return { error : "Role is required" }
3840 return json (
3941 await withActor (
4042 ( ) =>
@@ -44,12 +46,10 @@ const inviteMember = action(async (form: FormData) => {
4446 . values ( {
4547 id : Identifier . create ( "user" ) ,
4648 name : "" ,
47- email : name ,
49+ email,
4850 workspaceID,
49- role : "member" ,
50- timeJoined : sql `now()` ,
51+ role,
5152 } )
52- . onDuplicateKeyUpdate ( { set : { timeJoined : sql `now()` } } )
5353 . then ( ( data ) => ( { error : undefined , data } ) )
5454 . catch ( ( e ) => ( { error : e . message as string } ) ) ,
5555 ) ,
@@ -109,7 +109,23 @@ export function MemberCreateForm() {
109109 >
110110 < form action = { inviteMember } method = "post" data-slot = "create-form" >
111111 < div data-slot = "input-container" >
112- < input ref = { ( r ) => ( input = r ) } data-component = "input" name = "name" type = "text" placeholder = "Enter email" />
112+ < input ref = { ( r ) => ( input = r ) } data-component = "input" name = "email" type = "text" placeholder = "Enter email" />
113+ < div data-slot = "role-selector" >
114+ < label >
115+ < input type = "radio" name = "role" value = "admin" checked />
116+ < div >
117+ < strong > Admin</ strong >
118+ < p > Can manage models, members, and billing</ p >
119+ </ div >
120+ </ label >
121+ < label >
122+ < input type = "radio" name = "role" value = "member" />
123+ < div >
124+ < strong > Member</ strong >
125+ < p > Can only generate API keys for themselves</ p >
126+ </ div >
127+ </ label >
128+ </ div >
113129 < Show when = { submission . result && submission . result . error } >
114130 { ( err ) => < div data-slot = "form-error" > { err ( ) } </ div > }
115131 </ Show >
@@ -160,15 +176,15 @@ export function MemberSection() {
160176 < tbody >
161177 < For each = { members ( ) ! } >
162178 { ( member ) => {
163- const [ copied , setCopied ] = createSignal ( false )
164- // const submission = useSubmission(removeKey, ([fd]) => fd.get("id")?.toString() === key.id)
165179 return (
166180 < tr >
167181 < td data-slot = "member-email" > { member . email } </ td >
168182 < td data-slot = "member-role" > { member . role } </ td >
169- < td data-slot = "member-joined" title = { formatDateUTC ( member . timeJoined ! ) } >
170- { formatDateForTable ( member . timeJoined ! ) }
171- </ td >
183+ < Show when = { member . timeSeen } fallback = { < td data-slot = "member-joined" > invited</ td > } >
184+ < td data-slot = "member-joined" title = { formatDateUTC ( member . timeSeen ! ) } >
185+ { formatDateForTable ( member . timeSeen ! ) }
186+ </ td >
187+ </ Show >
172188 < td data-slot = "member-actions" >
173189 < form action = { removeMember } method = "post" >
174190 < input type = "hidden" name = "id" value = { member . id } />
0 commit comments