Skip to content

Commit 2995d96

Browse files
authored
UserButton component and account setting page (stack-auth#10)
* fixed sign in title bug * addec changeset * added popover * added dropdown, improved user button * improved user button * added avatar, adding account setting * added send verification email * added plaintext to emails * fixed select props * added name update * added password update * added email password update * only password update for accounts logged in credential * fixed small errors in endpoints * improved account setting page * fixed types * removed props export * divider -> separator * fixed accidently renamed dividers * added changeset
1 parent 961e6d6 commit 2995d96

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1627
-252
lines changed

.changeset/nice-queens-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@stackframe/stack": patch
3+
---
4+
5+
Fixed signin title bug

.changeset/old-mangos-nail.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@stackframe/stack-server": minor
3+
"@stackframe/stack-shared": minor
4+
"@stackframe/stack": minor
5+
---
6+
7+
Added new UserButton component and Account setting page

apps/demo/src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default function RootLayout({
2525
>
2626
<Provider>
2727
<Header />
28-
<div className="absolute top-12 left-0 right-0 bottom-0 overflow-auto">
28+
<div className="fixed top-12 left-0 right-0 bottom-0 overflow-auto">
2929
{children}
3030
</div>
3131
</Provider>

apps/demo/src/app/not-found.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { MessageCard } from "@stackframe/stack";
2+
3+
export default function NotFound() {
4+
return (
5+
<MessageCard title="404 Page Not Found" fullPage>
6+
<p>The page you are looking for does not exist.</p>
7+
</MessageCard>
8+
);
9+
}

apps/demo/src/components/header.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import Link from "next/link";
4-
import { useDesign } from "@stackframe/stack";
4+
import { useDesign, UserButton } from "@stackframe/stack";
55
import ColorMode from "./color-mode";
66
import Select from "./select";
77
import { useCurrentUI } from "./provider";
@@ -11,7 +11,7 @@ export default function Header() {
1111
const [currentUI, setCurrentUI] = useCurrentUI();
1212
return (
1313
<div
14-
className={"absolute w-full top-0 z-50 p-4 h-12 flex items-center py-4 border-b justify-between"}
14+
className={"fixed w-full top-0 z-50 p-4 h-12 flex items-center py-4 border-b justify-between"}
1515
style={{
1616
borderColor: colors.neutralColor
1717
}}
@@ -30,6 +30,7 @@ export default function Header() {
3030
onChange={(e) => setCurrentUI(e.target.value as any)}
3131
/>
3232
<ColorMode />
33+
<UserButton />
3334
</div>
3435
</div>
3536
);

apps/demo/src/components/select.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ const SelectWrapper = styled.div`
88
display: inline-block;
99
`;
1010

11-
const StyledSelect = styled.select<{bgColor: string, borderColor: string}>`
11+
const StyledSelect = styled.select<{$bgColor: string, $borderColor: string}>`
1212
padding: 3px 5px;
1313
padding-right: 30px;
1414
display: inline-block;
15-
border: 1px solid ${props => props.borderColor};
15+
border: 1px solid ${props => props.$borderColor};
1616
border-radius: 4px;
1717
box-sizing: border-box;
18-
background-color: ${props => props.bgColor};
18+
background-color: ${props => props.$bgColor};
1919
appearance: none;
2020
`;
2121

@@ -34,7 +34,7 @@ const Select = ({ options, ...props }) => {
3434
const { colors } = useDesign();
3535
return (
3636
<SelectWrapper>
37-
<StyledSelect {...props} bgColor={colors.backgroundColor} borderColor={colors.neutralColor}>
37+
<StyledSelect {...props} $bgColor={colors.backgroundColor} $borderColor={colors.neutralColor}>
3838
{options.map(option => (
3939
<StyledOption key={option.value} value={option.value}>
4040
{option.label}

apps/dev/src/app/ui/page-client.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { Button, Container, Divider, Input, Label, Link, Text, useDesign } from '@stackframe/stack';
3+
import { Button, Container, Separator, Input, Label, Link, Text, useDesign } from '@stackframe/stack';
44
import { useCurrentUI } from 'src/components/provider';
55

66
const text = "This is a test sentence. ";
@@ -66,7 +66,7 @@ export default function PageClient() {
6666
</Button>
6767
</div>
6868

69-
<Divider />
69+
<Separator />
7070

7171
<div style={{ display: 'flex', gap: 20}}>
7272
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 5 }}>
@@ -77,7 +77,7 @@ export default function PageClient() {
7777
<Text size='xl'>{text}</Text>
7878
</div>
7979

80-
<Divider orientation='vertical' />
80+
<Separator orientation='vertical' />
8181

8282
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 5 }}>
8383
<Text color="primary">{text}</Text>

docs/docs/02-customization/02-custom-colors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ If you want to spend minimal time on styling but still want to align with your b
1212
There are five variables that you can override:
1313
- `primaryColor`: The primary color used for components like button with primary variant.
1414
- `secondaryColor`: The secondary color used for components like button with secondary variant.
15-
- `neutralColor`: The color used for dividers and borders.
15+
- `neutralColor`: The color used for separators and borders.
1616
- `backgroundColor`: The background color of your the main content area, should be the same the color of your `body` element.
1717

1818
These colors can be different for light and dark mode. You can pass these into the `StackTheme` component (in your `layout.tsx` file if you followed the get started guide) as follows:

docs/docs/02-customization/03-custom-components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Now if you check out your sign-in page, you will see the sign-in button there is
6969
Here is a list of low-level components that you can customize, stared ones are the most used and recommended to customize first:
7070
- Button ⭐
7171
- Container
72-
- Divider
72+
- Separator
7373
- Input ⭐
7474
- Label
7575
- Link
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import * as yup from "yup";
3+
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
4+
import { deprecatedParseRequest, deprecatedSmartRouteHandler } from "@/lib/route-handlers";
5+
import { checkApiKeySet, publishableClientKeyHeaderSchema } from "@/lib/api-keys";
6+
import { decodeAccessToken, authorizationHeaderSchema } from "@/lib/access-token";
7+
import { sendVerificationEmail } from "@/email";
8+
import { getClientUser } from "@/lib/users";
9+
import { KnownErrors } from "@stackframe/stack-shared";
10+
11+
const postSchema = yup.object({
12+
headers: yup.object({
13+
authorization: authorizationHeaderSchema.default(undefined),
14+
"x-stack-publishable-client-key": publishableClientKeyHeaderSchema.default(""),
15+
"x-stack-project-id": yup.string().required(),
16+
}).required(),
17+
body: yup.object({
18+
emailVerificationRedirectUrl: yup.string().required(),
19+
}).required(),
20+
});
21+
22+
const handler = deprecatedSmartRouteHandler(async (req: NextRequest) => {
23+
const {
24+
headers: {
25+
authorization,
26+
"x-stack-project-id": projectId,
27+
"x-stack-publishable-client-key": publishableClientKey,
28+
},
29+
body: {
30+
emailVerificationRedirectUrl
31+
},
32+
} = await deprecatedParseRequest(req, postSchema);
33+
34+
if (!authorization) {
35+
return NextResponse.json(null);
36+
}
37+
38+
const pkValid = await checkApiKeySet(projectId, { publishableClientKey });
39+
if (!pkValid) {
40+
throw new StatusError(StatusError.Forbidden);
41+
}
42+
43+
const decodedAccessToken = await decodeAccessToken(authorization.split(" ")[1]);
44+
const { userId, projectId: accessTokenProjectId } = decodedAccessToken;
45+
46+
if (accessTokenProjectId !== projectId) {
47+
throw new StatusError(StatusError.Forbidden);
48+
}
49+
50+
const user = await getClientUser(projectId, userId);
51+
if (!user) {
52+
throw new StatusError(StatusError.NotFound);
53+
}
54+
if (user.primaryEmailVerified) {
55+
throw new KnownErrors.EmailAlreadyVerified();
56+
}
57+
try {
58+
await sendVerificationEmail(projectId, userId, emailVerificationRedirectUrl);
59+
} catch (e) {
60+
console.error(e);
61+
throw e;
62+
}
63+
return NextResponse.json({});
64+
});
65+
export const POST = handler;

0 commit comments

Comments
 (0)