Skip to content

Commit fc9826a

Browse files
fomalhautbN2D4
authored andcommitted
added create user button (stack-auth#173)
1 parent 2ca5150 commit fc9826a

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/users/page-client.tsx

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,62 @@
11
"use client";
22

33
import { UserTable } from "@/components/data-table/user-table";
4+
import { FormDialog } from "@/components/form-dialog";
5+
import { InputField, SwitchField } from "@/components/form-fields";
46
import { StyledLink } from "@/components/link";
5-
import { Alert } from "@stackframe/stack-ui";
7+
import { Alert, Button } from "@stackframe/stack-ui";
8+
import * as yup from "yup";
69
import { PageLayout } from "../page-layout";
710
import { useAdminApp } from "../use-admin-app";
811

12+
function CreateDialog(props: {
13+
open?: boolean,
14+
onOpenChange?: (open: boolean) => void,
15+
trigger?: React.ReactNode,
16+
}) {
17+
const adminApp = useAdminApp();
18+
const formSchema = yup.object({
19+
displayName: yup.string().optional(),
20+
primaryEmail: yup.string().email().required(),
21+
primaryEmailVerified: yup.boolean().optional(),
22+
password: yup.string().required(),
23+
});
24+
25+
return <FormDialog
26+
trigger={props.trigger}
27+
title={"Create User"}
28+
formSchema={formSchema}
29+
okButton={{ label: "Create" }}
30+
onSubmit={async (values) => {
31+
await adminApp.createUser(values);
32+
}}
33+
cancelButton
34+
render={(form) => (
35+
<>
36+
<InputField control={form.control} label="Display Name" name="displayName" />
37+
38+
<div className="flex gap-4 items-end">
39+
<div className="flex-1">
40+
<InputField control={form.control} label="Primary Email" name="primaryEmail" required />
41+
</div>
42+
<div className="mb-2">
43+
<SwitchField control={form.control} label="Verified" name="primaryEmailVerified" />
44+
</div>
45+
</div>
46+
47+
<InputField control={form.control} label="Password" name="password" type="password" required />
48+
</>
49+
)}
50+
/>;
51+
}
52+
953

1054
export default function PageClient() {
1155
const stackAdminApp = useAdminApp();
1256
const allUsers = stackAdminApp.useUsers();
1357

1458
return (
15-
<PageLayout title="Users">
59+
<PageLayout title="Users" actions={<CreateDialog trigger={<Button>Create User</Button>} />}>
1660
{allUsers.length > 0 ? null : (
1761
<Alert variant='success'>
1862
Congratulations on starting your project! Check the <StyledLink href="https://docs.stack-auth.com">documentation</StyledLink> to add your first users.

packages/stack-shared/src/interface/serverInterface.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ export class StackServerInterface extends StackClientInterface {
7272
}
7373
}
7474

75+
async createServerUser(data: UsersCrud['Server']['Create']): Promise<UsersCrud['Server']['Read']> {
76+
const response = await this.sendServerRequest(
77+
"/users",
78+
{
79+
method: "POST",
80+
headers: {
81+
"content-type": "application/json",
82+
},
83+
body: JSON.stringify(data),
84+
},
85+
null,
86+
);
87+
return await response.json();
88+
}
89+
7590
async getServerUserByToken(session: InternalSession): Promise<CurrentUserCrud['Server']['Read'] | null> {
7691
const responseOrError = await this.sendServerRequestAndCatchKnownError(
7792
"/users/me",

packages/stack/src/lib/stack-app.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,12 @@ class _StackServerAppImpl<HasTokenStore extends boolean, ProjectId extends strin
15681568
};
15691569
}
15701570

1571+
async createUser(options: ServerUserCreateOptions): Promise<ServerUser> {
1572+
const crud = await this._interface.createServerUser(serverUserCreateOptionsToCrud(options));
1573+
await this._refreshUsers();
1574+
return this._serverUserFromCrud(crud);
1575+
}
1576+
15711577

15721578
async getUser(options: GetUserOptions<HasTokenStore> & { or: 'redirect' }): Promise<ProjectCurrentServerUser<ProjectId>>;
15731579
async getUser(options: GetUserOptions<HasTokenStore> & { or: 'throw' }): Promise<ProjectCurrentServerUser<ProjectId>>;
@@ -2236,6 +2242,23 @@ function serverUserUpdateOptionsToCrud(options: ServerUserUpdateOptions): Curren
22362242
}
22372243

22382244

2245+
type ServerUserCreateOptions = {
2246+
primaryEmail: string,
2247+
password: string,
2248+
displayName?: string,
2249+
primaryEmailVerified?: boolean,
2250+
}
2251+
function serverUserCreateOptionsToCrud(options: ServerUserCreateOptions): UsersCrud["Server"]["Create"] {
2252+
return {
2253+
primary_email: options.primaryEmail,
2254+
password: options.password,
2255+
primary_email_auth_enabled: true,
2256+
display_name: options.displayName,
2257+
primary_email_verified: options.primaryEmailVerified,
2258+
};
2259+
}
2260+
2261+
22392262
type _______________PROJECT_______________ = never; // this is a marker for VSCode's outline view
22402263

22412264
export type Project = {
@@ -2616,6 +2639,8 @@ export type StackServerApp<HasTokenStore extends boolean = boolean, ProjectId ex
26162639
*/
26172640
getServerUser(): Promise<ProjectCurrentServerUser<ProjectId> | null>,
26182641

2642+
createUser(options: ServerUserCreateOptions): Promise<ServerUser>,
2643+
26192644
useUser(options: GetUserOptions<HasTokenStore> & { or: 'redirect' }): ProjectCurrentServerUser<ProjectId>,
26202645
useUser(options: GetUserOptions<HasTokenStore> & { or: 'throw' }): ProjectCurrentServerUser<ProjectId>,
26212646
useUser(options?: GetUserOptions<HasTokenStore>): ProjectCurrentServerUser<ProjectId> | null,

0 commit comments

Comments
 (0)