create-fate 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +1 -2
  2. package/bin/create-fate.mjs +14 -50
  3. package/package.json +1 -1
  4. package/templates/fate/README.md +1 -2
  5. package/templates/fate/_shared/vue-app/package.json +4 -3
  6. package/templates/fate/_shared/vue-app/vite.config.ts +18 -11
  7. package/templates/fate/drizzle/client/package.json +6 -6
  8. package/templates/fate/graphql/client/package.json +6 -6
  9. package/templates/fate/graphql/server/.gitignore +5 -0
  10. package/templates/fate/graphql/server/node_modules/.bin/esbuild +22 -0
  11. package/templates/fate/graphql/server/node_modules/.bin/eslint +22 -0
  12. package/templates/fate/graphql/server/node_modules/.bin/fate +22 -0
  13. package/templates/fate/graphql/server/node_modules/.bin/intent +22 -0
  14. package/templates/fate/graphql/server/node_modules/.bin/jiti +22 -0
  15. package/templates/fate/graphql/server/node_modules/.bin/nft +22 -0
  16. package/templates/fate/graphql/server/node_modules/.bin/nodemon +22 -0
  17. package/templates/fate/graphql/server/node_modules/.bin/oxfmt +22 -0
  18. package/templates/fate/graphql/server/node_modules/.bin/oxlint +22 -0
  19. package/templates/fate/graphql/server/node_modules/.bin/prisma +22 -0
  20. package/templates/fate/graphql/server/node_modules/.bin/prisma-json-types-generator +22 -0
  21. package/templates/fate/graphql/server/node_modules/.bin/prisma-pothos-types +22 -0
  22. package/templates/fate/graphql/server/node_modules/.bin/tsc +22 -0
  23. package/templates/fate/graphql/server/node_modules/.bin/tsgolint +22 -0
  24. package/templates/fate/graphql/server/node_modules/.bin/tsx +22 -0
  25. package/templates/fate/graphql/server/node_modules/.bin/vitest +22 -0
  26. package/templates/fate/graphql/server/node_modules/.bin/vp +22 -0
  27. package/templates/fate/graphql/server/node_modules/.bin/vpr +22 -0
  28. package/templates/fate/graphql/server/node_modules/.bin/yaml +22 -0
  29. package/templates/fate/graphql-client/README.md +11 -1
  30. package/templates/fate/graphql-client/env.ts +6 -0
  31. package/templates/fate/graphql-client/package.json +6 -6
  32. package/templates/fate/http/client/package.json +6 -6
  33. package/templates/fate/prisma/client/package.json +6 -6
  34. package/templates/fate/void/README.md +12 -0
  35. package/templates/fate/void/db/migrations/20260918045815_legal_dragon_lord.sql +15 -0
  36. package/templates/fate/{cloudflare/server/db/migrations/meta/20260508073357_snapshot.json → void/db/migrations/meta/20260918045815_snapshot.json} +174 -124
  37. package/templates/fate/void/db/migrations/meta/_journal.json +7 -0
  38. package/templates/fate/void/package.json +6 -7
  39. package/templates/fate/cloudflare/.github/workflows/push.yml +0 -27
  40. package/templates/fate/cloudflare/.vite-hooks/pre-commit +0 -1
  41. package/templates/fate/cloudflare/.vscode/extensions.json +0 -12
  42. package/templates/fate/cloudflare/.vscode/settings.json +0 -25
  43. package/templates/fate/cloudflare/AGENTS.md +0 -55
  44. package/templates/fate/cloudflare/README.md +0 -137
  45. package/templates/fate/cloudflare/_gitignore +0 -20
  46. package/templates/fate/cloudflare/client/node_modules/.bin/browserslist +0 -22
  47. package/templates/fate/cloudflare/client/node_modules/.bin/cf-wrangler +0 -22
  48. package/templates/fate/cloudflare/client/node_modules/.bin/drizzle-kit +0 -22
  49. package/templates/fate/cloudflare/client/node_modules/.bin/esbuild +0 -22
  50. package/templates/fate/cloudflare/client/node_modules/.bin/fate +0 -22
  51. package/templates/fate/cloudflare/client/node_modules/.bin/fbtee +0 -22
  52. package/templates/fate/cloudflare/client/node_modules/.bin/intent +0 -22
  53. package/templates/fate/cloudflare/client/node_modules/.bin/jiti +0 -22
  54. package/templates/fate/cloudflare/client/node_modules/.bin/oxfmt +0 -22
  55. package/templates/fate/cloudflare/client/node_modules/.bin/oxlint +0 -22
  56. package/templates/fate/cloudflare/client/node_modules/.bin/rolldown +0 -22
  57. package/templates/fate/cloudflare/client/node_modules/.bin/tsc +0 -22
  58. package/templates/fate/cloudflare/client/node_modules/.bin/tsx +0 -22
  59. package/templates/fate/cloudflare/client/node_modules/.bin/vitest +0 -22
  60. package/templates/fate/cloudflare/client/node_modules/.bin/void +0 -22
  61. package/templates/fate/cloudflare/client/node_modules/.bin/vp +0 -22
  62. package/templates/fate/cloudflare/client/node_modules/.bin/vpr +0 -22
  63. package/templates/fate/cloudflare/client/node_modules/.bin/workerd +0 -22
  64. package/templates/fate/cloudflare/client/node_modules/.bin/wrangler +0 -22
  65. package/templates/fate/cloudflare/client/node_modules/.bin/wrangler2 +0 -22
  66. package/templates/fate/cloudflare/client/package.json +0 -62
  67. package/templates/fate/cloudflare/client/pages/index.tsx +0 -96
  68. package/templates/fate/cloudflare/client/pages/layout.tsx +0 -68
  69. package/templates/fate/cloudflare/client/pages/login.tsx +0 -107
  70. package/templates/fate/cloudflare/client/pages/post/[id].tsx +0 -22
  71. package/templates/fate/cloudflare/client/pages/search.tsx +0 -99
  72. package/templates/fate/cloudflare/client/public/icon.svg +0 -1
  73. package/templates/fate/cloudflare/client/src/App.css +0 -153
  74. package/templates/fate/cloudflare/client/src/lib/AvailableLanguages.tsx +0 -5
  75. package/templates/fate/cloudflare/client/src/lib/LocaleContext.tsx +0 -61
  76. package/templates/fate/cloudflare/client/src/lib/cx.tsx +0 -6
  77. package/templates/fate/cloudflare/client/src/lib/env.tsx +0 -5
  78. package/templates/fate/cloudflare/client/src/ui/Badge.tsx +0 -31
  79. package/templates/fate/cloudflare/client/src/ui/Button.tsx +0 -108
  80. package/templates/fate/cloudflare/client/src/ui/Card.tsx +0 -32
  81. package/templates/fate/cloudflare/client/src/ui/CommentCard.tsx +0 -75
  82. package/templates/fate/cloudflare/client/src/ui/Error.tsx +0 -19
  83. package/templates/fate/cloudflare/client/src/ui/H2.tsx +0 -15
  84. package/templates/fate/cloudflare/client/src/ui/H3.tsx +0 -20
  85. package/templates/fate/cloudflare/client/src/ui/Header.tsx +0 -98
  86. package/templates/fate/cloudflare/client/src/ui/Input.tsx +0 -17
  87. package/templates/fate/cloudflare/client/src/ui/Link.tsx +0 -11
  88. package/templates/fate/cloudflare/client/src/ui/LocaleSwitcher.tsx +0 -34
  89. package/templates/fate/cloudflare/client/src/ui/PostCard.tsx +0 -303
  90. package/templates/fate/cloudflare/client/src/ui/Section.tsx +0 -23
  91. package/templates/fate/cloudflare/client/src/ui/UserCard.tsx +0 -131
  92. package/templates/fate/cloudflare/client/src/user/AuthClient.tsx +0 -6
  93. package/templates/fate/cloudflare/client/start.mjs +0 -11
  94. package/templates/fate/cloudflare/client/translations/de-DE.json +0 -632
  95. package/templates/fate/cloudflare/client/translations/ja-JP.json +0 -632
  96. package/templates/fate/cloudflare/client/vite.config.ts +0 -41
  97. package/templates/fate/cloudflare/client/void.json +0 -29
  98. package/templates/fate/cloudflare/package.json +0 -42
  99. package/templates/fate/cloudflare/pnpm-workspace.yaml +0 -41
  100. package/templates/fate/cloudflare/server/.env.example +0 -1
  101. package/templates/fate/cloudflare/server/db/db.ts +0 -30
  102. package/templates/fate/cloudflare/server/db/migrations/20260508073357_initial_schema.sql +0 -134
  103. package/templates/fate/cloudflare/server/db/migrations/20260508120500_seed_cloudflare_demo.sql +0 -170
  104. package/templates/fate/cloudflare/server/db/migrations/meta/_journal.json +0 -20
  105. package/templates/fate/cloudflare/server/db/queries.ts +0 -211
  106. package/templates/fate/cloudflare/server/db/schema.ts +0 -331
  107. package/templates/fate/cloudflare/server/node_modules/.bin/cf-wrangler +0 -22
  108. package/templates/fate/cloudflare/server/node_modules/.bin/drizzle-kit +0 -22
  109. package/templates/fate/cloudflare/server/node_modules/.bin/esbuild +0 -22
  110. package/templates/fate/cloudflare/server/node_modules/.bin/fate +0 -22
  111. package/templates/fate/cloudflare/server/node_modules/.bin/intent +0 -22
  112. package/templates/fate/cloudflare/server/node_modules/.bin/jiti +0 -22
  113. package/templates/fate/cloudflare/server/node_modules/.bin/oxfmt +0 -22
  114. package/templates/fate/cloudflare/server/node_modules/.bin/oxlint +0 -22
  115. package/templates/fate/cloudflare/server/node_modules/.bin/tsc +0 -22
  116. package/templates/fate/cloudflare/server/node_modules/.bin/tsx +0 -22
  117. package/templates/fate/cloudflare/server/node_modules/.bin/vitest +0 -22
  118. package/templates/fate/cloudflare/server/node_modules/.bin/vp +0 -22
  119. package/templates/fate/cloudflare/server/node_modules/.bin/vpr +0 -22
  120. package/templates/fate/cloudflare/server/node_modules/.bin/workerd +0 -22
  121. package/templates/fate/cloudflare/server/node_modules/.bin/wrangler +0 -22
  122. package/templates/fate/cloudflare/server/node_modules/.bin/wrangler2 +0 -22
  123. package/templates/fate/cloudflare/server/package.json +0 -34
  124. package/templates/fate/cloudflare/server/src/fate/commentPermissions.ts +0 -17
  125. package/templates/fate/cloudflare/server/src/fate/context.ts +0 -15
  126. package/templates/fate/cloudflare/server/src/fate/live.ts +0 -7
  127. package/templates/fate/cloudflare/server/src/fate/server.ts +0 -414
  128. package/templates/fate/cloudflare/server/src/fate/views.ts +0 -164
  129. package/templates/fate/cloudflare/server/src/index.ts +0 -80
  130. package/templates/fate/cloudflare/server/src/lib/auth.ts +0 -64
  131. package/templates/fate/cloudflare/server/src/lib/origins.ts +0 -17
  132. package/templates/fate/cloudflare/server/src/router.ts +0 -2
  133. package/templates/fate/cloudflare/server/src/user/SessionUser.tsx +0 -17
  134. package/templates/fate/cloudflare/server/tsconfig.json +0 -10
  135. package/templates/fate/cloudflare/server/wrangler.jsonc +0 -29
  136. package/templates/fate/cloudflare/tsconfig.json +0 -24
  137. package/templates/fate/cloudflare/vite.config.ts +0 -75
  138. package/templates/fate/graphql-client/.env.example +0 -3
@@ -1,303 +0,0 @@
1
- import type { Comment as InlineComment, Post } from '@app/server/src/router.ts';
2
- import Stack, { VStack } from '@nkzw/stack';
3
- import { cx } from 'class-variance-authority';
4
- import { fbs } from 'fbtee';
5
- import {
6
- KeyboardEvent,
7
- Suspense,
8
- startTransition,
9
- useActionState,
10
- useCallback,
11
- useEffect,
12
- useState,
13
- } from 'react';
14
- import { ErrorBoundary } from 'react-error-boundary';
15
- import {
16
- defer,
17
- type Deferred,
18
- useListView,
19
- useFateClient,
20
- useView,
21
- view,
22
- ViewRef,
23
- } from 'react-fate';
24
- import { Button } from '../ui/Button.tsx';
25
- import Card from '../ui/Card.tsx';
26
- import AuthClient from '../user/AuthClient.tsx';
27
- import CommentCard, { CommentView } from './CommentCard.tsx';
28
- import H3 from './H3.tsx';
29
- import Link from './Link.tsx';
30
- import { UserView } from './UserCard.tsx';
31
-
32
- const CommentConnectionView = {
33
- args: { first: 3 },
34
- items: {
35
- node: CommentView,
36
- },
37
- };
38
-
39
- export const PostView = view<Post>()({
40
- author: UserView,
41
- commentCount: true,
42
- comments: defer(CommentConnectionView),
43
- content: true,
44
- id: true,
45
- likes: true,
46
- title: true,
47
- });
48
-
49
- const PostComments = ({
50
- comments,
51
- post,
52
- }: {
53
- comments: Deferred<{ items: ReadonlyArray<{ node: ViewRef<'Comment'> }> }>;
54
- post: { commentCount: number; id: string; title: string };
55
- }) => {
56
- const [items, loadNext] = useListView(CommentConnectionView, comments);
57
-
58
- return items.length > 0 ? (
59
- <VStack gap={12}>
60
- {items.map(({ node }) => (
61
- <CommentCard comment={node} key={node.id} post={post} />
62
- ))}
63
- {loadNext ? (
64
- <Button onClick={loadNext} variant="ghost">
65
- <fbt desc="Load more comments button">Load more comments</fbt>
66
- </Button>
67
- ) : null}
68
- </VStack>
69
- ) : null;
70
- };
71
-
72
- const CommentInput = ({
73
- error,
74
- post,
75
- }: {
76
- error?: unknown;
77
- post: { commentCount: number; id: string };
78
- }) => {
79
- const fate = useFateClient();
80
- const { data: session } = AuthClient.useSession();
81
- const user = session?.user;
82
- const [commentText, setCommentText] = useState('');
83
-
84
- const [addCommentResult, handleAddComment, addCommentIsPending] = useActionState(async () => {
85
- const content = commentText.trim();
86
-
87
- if (!content) {
88
- return;
89
- }
90
-
91
- const result = await fate.mutations.comment.add({
92
- input: { content, postId: post.id },
93
- optimistic: {
94
- author: user
95
- ? {
96
- id: user.id,
97
- name: user.name ?? 'Anonymous',
98
- }
99
- : null,
100
- content,
101
- id: `optimistic:${Date.now().toString(36)}`,
102
- post: { commentCount: post.commentCount + 1, id: post.id },
103
- },
104
- view: view<InlineComment>()({
105
- ...CommentView,
106
- post: { commentCount: true },
107
- }),
108
- });
109
-
110
- setCommentText('');
111
-
112
- return result;
113
- }, null);
114
-
115
- const maybeSubmitComment = (event: KeyboardEvent<HTMLTextAreaElement>) => {
116
- if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
117
- startTransition(() => handleAddComment());
118
- }
119
- };
120
-
121
- const commentingIsDisabled = addCommentIsPending || commentText.trim().length === 0;
122
-
123
- const anyServerError = error || addCommentResult?.error;
124
-
125
- return (
126
- <VStack action={handleAddComment} as="form" gap>
127
- <span className="text-sm font-medium text-foreground">
128
- <fbt desc="Comment add label">Add a comment</fbt>
129
- </span>
130
- <textarea
131
- className="squircle min-h-20 w-full border border-gray-200/80 bg-gray-100/50 p-3 text-sm placeholder-gray-500 transition outline-none focus:border-gray-500 focus:ring-2 focus:ring-gray-200 disabled:opacity-50 dark:border-neutral-800 dark:bg-neutral-900/40 dark:placeholder-gray-400"
132
- disabled={addCommentIsPending}
133
- onChange={(event) => setCommentText(event.target.value)}
134
- onKeyDown={maybeSubmitComment}
135
- placeholder={fbs('Share your thoughts...', 'Placeholder text for comment input')}
136
- value={commentText}
137
- />
138
- {anyServerError ? (
139
- <p className="text-sm text-destructive">
140
- {anyServerError instanceof Error ? (
141
- anyServerError.message
142
- ) : (
143
- <fbt desc="Error text">Something went wrong. Please try again.</fbt>
144
- )}
145
- </p>
146
- ) : null}
147
- <Stack end gap>
148
- <Button disabled={commentingIsDisabled} size="sm" type="submit" variant="secondary">
149
- <fbt desc="Post comment button">Post comment</fbt>
150
- </Button>
151
- </Stack>
152
- </VStack>
153
- );
154
- };
155
-
156
- export function PostCard({ detail, post: postRef }: { detail?: boolean; post: ViewRef<'Post'> }) {
157
- const fate = useFateClient();
158
- const post = useView(PostView, postRef);
159
- const author = useView(UserView, post.author);
160
-
161
- const [likeResult, likeAction, likeIsPending] = useActionState(fate.actions.post.like, null);
162
-
163
- const [, unlikeAction, unlikeIsPending] = useActionState(fate.actions.post.unlike, null);
164
-
165
- useEffect(() => {
166
- if (likeResult?.error) {
167
- const timeout = setTimeout(() => startTransition(() => likeAction('reset')), 3000);
168
- return () => clearTimeout(timeout);
169
- }
170
- }, [likeAction, likeResult]);
171
-
172
- const handleLike = useCallback(
173
- async (options?: { error?: 'boundary' | 'callSite'; slow?: boolean }) => {
174
- likeAction({
175
- input: { id: post.id, ...options },
176
- optimistic: { likes: post.likes + 1 },
177
- view: PostView,
178
- });
179
- },
180
- [likeAction, post.id, post.likes],
181
- );
182
-
183
- const handleUnlike = useCallback(async () => {
184
- unlikeAction({
185
- input: { id: post.id },
186
- optimistic: {
187
- likes: Math.max(post.likes - 1, 0),
188
- },
189
- view: PostView,
190
- });
191
- }, [post.id, post.likes, unlikeAction]);
192
-
193
- return (
194
- <Card>
195
- <VStack gap={16}>
196
- <Stack alignStart between gap={16} wrap>
197
- <VStack gap>
198
- <Link to={`/post/${post.id}`}>
199
- <H3>{post.title}</H3>
200
- </Link>
201
- </VStack>
202
- <Stack alignCenter gap={12} wrap>
203
- <Stack
204
- alignCenter
205
- className="squircle bg-slate-100 px-3 py-2 text-sm font-semibold text-slate-800 dark:bg-neutral-800 dark:text-white"
206
- gap
207
- >
208
- <span>👍</span>
209
- <span>
210
- <fbt desc="Like count">
211
- <fbt:plural count={post.likes} many="Likes" showCount="yes">
212
- Like
213
- </fbt:plural>
214
- </fbt>
215
- </span>
216
- </Stack>
217
- <Button action={handleLike} disabled={likeIsPending} size="sm" variant="outline">
218
- <fbt desc="Like button">Like</fbt>
219
- </Button>
220
- {detail && (
221
- <Button
222
- action={() => handleLike({ slow: true })}
223
- disabled={likeIsPending}
224
- size="sm"
225
- variant="outline"
226
- >
227
- <fbt desc="Like button with a slow request">Like (Slow)</fbt>
228
- </Button>
229
- )}
230
- {detail && (
231
- <Button
232
- action={() => handleLike({ error: 'callSite' })}
233
- className={cx(
234
- 'w-32 transition-colors duration-150',
235
- likeResult?.error ? 'border-red-500 text-red-500 hover:text-red-500' : '',
236
- )}
237
- disabled={likeIsPending}
238
- size="sm"
239
- variant="outline"
240
- >
241
- {likeResult?.error ? (
242
- <fbt desc="Very short error messages">Oops, try again!</fbt>
243
- ) : (
244
- <fbt desc="Like button that triggers an error">Like (Error)</fbt>
245
- )}
246
- </Button>
247
- )}
248
- {detail && (
249
- <Button
250
- action={() => handleLike({ error: 'boundary' })}
251
- disabled={likeIsPending}
252
- size="sm"
253
- variant="outline"
254
- >
255
- <fbt desc="Like button that triggers a network error">Like (Network Error)</fbt>
256
- </Button>
257
- )}
258
- {detail && (
259
- <Button
260
- onClick={() =>
261
- fate.mutations.post.like({
262
- input: { id: post.id },
263
- optimistic: { likes: post.likes + 1 },
264
- view: PostView,
265
- })
266
- }
267
- size="sm"
268
- variant="outline"
269
- >
270
- <fbt desc="Like button that can be pressed many times">Like (Many)</fbt>
271
- </Button>
272
- )}
273
- <Button
274
- action={handleUnlike}
275
- disabled={unlikeIsPending || post.likes === 0}
276
- size="sm"
277
- variant="outline"
278
- >
279
- <fbt desc="Unlike button">Unlike</fbt>
280
- </Button>
281
- </Stack>
282
- </Stack>
283
- <p className="text-sm leading-relaxed text-foreground/90 lg:text-base">{post.content}</p>
284
- <p className="text-sm text-muted-foreground">- {author?.name ?? 'Unknown author'}</p>
285
- <VStack gap={16}>
286
- <h4 className="text-base font-semibold text-foreground">
287
- <fbt desc="Comment headline">
288
- <fbt:plural count={post.commentCount} many="Comments" showCount="ifMany">
289
- One Comment
290
- </fbt:plural>
291
- </fbt>
292
- </h4>
293
- <Suspense fallback={null}>
294
- <PostComments comments={post.comments} post={post} />
295
- </Suspense>
296
- <ErrorBoundary fallbackRender={({ error }) => <CommentInput error={error} post={post} />}>
297
- <CommentInput post={post} />
298
- </ErrorBoundary>
299
- </VStack>
300
- </VStack>
301
- </Card>
302
- );
303
- }
@@ -1,23 +0,0 @@
1
- import { Gap, VStack } from '@nkzw/stack';
2
- import { ReactNode } from 'react';
3
- import cx from '../lib/cx.tsx';
4
-
5
- export default function Section({
6
- children,
7
- className,
8
- gap,
9
- }: {
10
- children: ReactNode;
11
- className?: string;
12
- gap?: Gap;
13
- }) {
14
- return (
15
- <VStack
16
- as="section"
17
- className={cx('max-w-8xl container mx-auto px-4 py-6 lg:px-8 lg:py-10', className)}
18
- gap={gap}
19
- >
20
- {children}
21
- </VStack>
22
- );
23
- }
@@ -1,131 +0,0 @@
1
- import { User } from '@app/server/src/router.ts';
2
- import safeParse from '@nkzw/core/safeParse.js';
3
- import Stack, { VStack } from '@nkzw/stack';
4
- import { fbs } from 'fbtee';
5
- import { ChangeEvent, useActionState, useState } from 'react';
6
- import { view } from 'react-fate';
7
- import { useFateClient } from 'react-fate';
8
- import { ViewRef } from 'react-fate';
9
- import { useView } from 'react-fate';
10
- import { Button } from '../ui/Button.tsx';
11
- import Card from '../ui/Card.tsx';
12
- import AuthClient from '../user/AuthClient.tsx';
13
- import H2 from './H2.tsx';
14
- import Input from './Input.tsx';
15
-
16
- export type SessionUser = {
17
- id?: string | null;
18
- name?: string | null;
19
- username?: string | null;
20
- };
21
-
22
- export const UserView = view<User>()({
23
- id: true,
24
- name: true,
25
- username: true,
26
- });
27
-
28
- const UserNameForm = ({ user }: { user: SessionUser }) => {
29
- const fate = useFateClient();
30
- const [name, setName] = useState(user.name ?? '');
31
- const [error, setError] = useState<string | null>(null);
32
-
33
- const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
34
- setError(null);
35
- setName(event.target.value);
36
- };
37
-
38
- const [, submitAction, isPending] = useActionState(async () => {
39
- const id = user?.id;
40
- if (!id) {
41
- return;
42
- }
43
-
44
- const newName = name.trim();
45
- setName(newName);
46
-
47
- if (newName === user.name) {
48
- setError(null);
49
- return;
50
- }
51
-
52
- try {
53
- setError(null);
54
- await fate.mutations.user.update({
55
- input: { name: newName },
56
- optimistic: {
57
- id,
58
- username: newName,
59
- },
60
- view: UserView,
61
- });
62
- await AuthClient.updateUser({ name });
63
- } catch (error) {
64
- setError(
65
- (error instanceof Error &&
66
- error.message &&
67
- safeParse<Array<{ message: string }>>(error.message)?.[0]?.message) ||
68
- 'Failed to update user name.',
69
- );
70
- }
71
- }, null);
72
-
73
- const trimmedName = name.trim();
74
- const originalName = user.name ?? '';
75
- const isSaveDisabled = !user.id || !trimmedName || trimmedName === originalName || isPending;
76
-
77
- return (
78
- <div>
79
- <VStack action={submitAction} as="form" gap={12}>
80
- <h3 className="font-semibold">
81
- <fbt desc="Headline for update name section">Update Name</fbt>
82
- </h3>
83
- <label className="sr-only" htmlFor="header-username">
84
- <fbt desc="Username label">Username</fbt>
85
- </label>
86
- <Input
87
- aria-describedby={error ? 'header-username-error' : undefined}
88
- aria-invalid={error ? 'true' : undefined}
89
- className="w-48"
90
- disabled={isPending}
91
- id="header-username"
92
- name="name"
93
- onChange={handleChange}
94
- placeholder={fbs('name', 'name input field placeholder')}
95
- title={error ?? undefined}
96
- value={name}
97
- />
98
- <div>
99
- <Button disabled={isSaveDisabled} size="sm" type="submit" variant="secondary">
100
- <fbt desc="Button for saving">Save</fbt>
101
- </Button>
102
- </div>
103
- </VStack>
104
- {error ? <span id="header-username-error">{error}</span> : null}
105
- </div>
106
- );
107
- };
108
-
109
- export default function UserCard({ user: userRef }: { user: ViewRef<'User'> }) {
110
- const user = useView(UserView, userRef);
111
-
112
- return (
113
- <Card>
114
- <VStack between className="h-full" gap={16}>
115
- <VStack gap={16}>
116
- <H2>
117
- <fbt desc="Your account headline">Your account</fbt>
118
- </H2>
119
- <Stack alignCenter between gap={16}>
120
- <p className="text-sm text-muted-foreground">
121
- <fbt desc="Signed in text">
122
- Signed in as <fbt:param name="username">{user.name}</fbt:param>.
123
- </fbt>
124
- </p>
125
- </Stack>
126
- </VStack>
127
- <UserNameForm user={user} />
128
- </VStack>
129
- </Card>
130
- );
131
- }
@@ -1,6 +0,0 @@
1
- import { createAuthClient } from 'better-auth/react';
2
- import env from '../lib/env.tsx';
3
-
4
- export default createAuthClient({
5
- baseURL: env('SERVER_URL'),
6
- });
@@ -1,11 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import process from 'node:process';
3
- import { fileURLToPath, URL } from 'node:url';
4
-
5
- const dist = new URL('./dist/', import.meta.url);
6
- // Void's Node renderer reads this manifest to emit client scripts and styles.
7
- globalThis.__VITE_MANIFEST__ = JSON.parse(
8
- readFileSync(new URL('client/.vite/manifest.json', dist), 'utf8'),
9
- );
10
- process.chdir(fileURLToPath(dist));
11
- await import(new URL('ssr/index.js', dist).href);