create-chaaskit 0.1.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 (122) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +25 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/add-infra.d.ts +6 -0
  6. package/dist/commands/add-infra.d.ts.map +1 -0
  7. package/dist/commands/add-infra.js +160 -0
  8. package/dist/commands/add-infra.js.map +1 -0
  9. package/dist/commands/build.d.ts +2 -0
  10. package/dist/commands/build.d.ts.map +1 -0
  11. package/dist/commands/build.js +63 -0
  12. package/dist/commands/build.js.map +1 -0
  13. package/dist/commands/db-sync.d.ts +13 -0
  14. package/dist/commands/db-sync.d.ts.map +1 -0
  15. package/dist/commands/db-sync.js +108 -0
  16. package/dist/commands/db-sync.js.map +1 -0
  17. package/dist/commands/dev.d.ts +7 -0
  18. package/dist/commands/dev.d.ts.map +1 -0
  19. package/dist/commands/dev.js +61 -0
  20. package/dist/commands/dev.js.map +1 -0
  21. package/dist/commands/init.d.ts +9 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +214 -0
  24. package/dist/commands/init.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +57 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/templates/.env.example +24 -0
  30. package/dist/templates/README.md +81 -0
  31. package/dist/templates/app/components/AcceptInviteClient.tsx +10 -0
  32. package/dist/templates/app/components/AdminDashboardClient.tsx +10 -0
  33. package/dist/templates/app/components/AdminTeamClient.tsx +10 -0
  34. package/dist/templates/app/components/AdminTeamsClient.tsx +10 -0
  35. package/dist/templates/app/components/AdminUsersClient.tsx +10 -0
  36. package/dist/templates/app/components/ApiKeysClient.tsx +10 -0
  37. package/dist/templates/app/components/AutomationsClient.tsx +10 -0
  38. package/dist/templates/app/components/ChatClient.tsx +13 -0
  39. package/dist/templates/app/components/ClientOnly.tsx +6 -0
  40. package/dist/templates/app/components/DocumentsClient.tsx +10 -0
  41. package/dist/templates/app/components/OAuthConsentClient.tsx +10 -0
  42. package/dist/templates/app/components/PricingClient.tsx +10 -0
  43. package/dist/templates/app/components/TeamSettingsClient.tsx +10 -0
  44. package/dist/templates/app/components/VerifyEmailClient.tsx +10 -0
  45. package/dist/templates/app/entry.client.tsx +12 -0
  46. package/dist/templates/app/entry.server.tsx +67 -0
  47. package/dist/templates/app/root.tsx +91 -0
  48. package/dist/templates/app/routes/_index.tsx +82 -0
  49. package/dist/templates/app/routes/admin._index.tsx +57 -0
  50. package/dist/templates/app/routes/admin.teams.$teamId.tsx +57 -0
  51. package/dist/templates/app/routes/admin.teams._index.tsx +57 -0
  52. package/dist/templates/app/routes/admin.users.tsx +57 -0
  53. package/dist/templates/app/routes/api-keys.tsx +57 -0
  54. package/dist/templates/app/routes/automations.tsx +57 -0
  55. package/dist/templates/app/routes/chat._index.tsx +11 -0
  56. package/dist/templates/app/routes/chat.admin._index.tsx +10 -0
  57. package/dist/templates/app/routes/chat.admin.teams.$teamId.tsx +10 -0
  58. package/dist/templates/app/routes/chat.admin.teams._index.tsx +10 -0
  59. package/dist/templates/app/routes/chat.admin.users.tsx +10 -0
  60. package/dist/templates/app/routes/chat.api-keys.tsx +10 -0
  61. package/dist/templates/app/routes/chat.automations.tsx +10 -0
  62. package/dist/templates/app/routes/chat.documents.tsx +10 -0
  63. package/dist/templates/app/routes/chat.team.$teamId.settings.tsx +10 -0
  64. package/dist/templates/app/routes/chat.thread.$threadId.tsx +11 -0
  65. package/dist/templates/app/routes/chat.tsx +39 -0
  66. package/dist/templates/app/routes/documents.tsx +57 -0
  67. package/dist/templates/app/routes/invite.$token.tsx +10 -0
  68. package/dist/templates/app/routes/login.tsx +334 -0
  69. package/dist/templates/app/routes/oauth.consent.tsx +10 -0
  70. package/dist/templates/app/routes/pricing.tsx +10 -0
  71. package/dist/templates/app/routes/privacy.tsx +197 -0
  72. package/dist/templates/app/routes/register.tsx +398 -0
  73. package/dist/templates/app/routes/shared.$shareId.tsx +226 -0
  74. package/dist/templates/app/routes/team.$teamId.settings.tsx +57 -0
  75. package/dist/templates/app/routes/terms.tsx +173 -0
  76. package/dist/templates/app/routes/thread.$threadId.tsx +102 -0
  77. package/dist/templates/app/routes/verify-email.tsx +10 -0
  78. package/dist/templates/app/routes.ts +47 -0
  79. package/dist/templates/config/app.config.ts +216 -0
  80. package/dist/templates/docs/admin.md +257 -0
  81. package/dist/templates/docs/api-keys.md +403 -0
  82. package/dist/templates/docs/authentication.md +247 -0
  83. package/dist/templates/docs/configuration.md +1212 -0
  84. package/dist/templates/docs/custom-pages.md +466 -0
  85. package/dist/templates/docs/deployment.md +362 -0
  86. package/dist/templates/docs/development.md +411 -0
  87. package/dist/templates/docs/documents.md +293 -0
  88. package/dist/templates/docs/extensions.md +639 -0
  89. package/dist/templates/docs/index.md +139 -0
  90. package/dist/templates/docs/installation.md +286 -0
  91. package/dist/templates/docs/mcp.md +952 -0
  92. package/dist/templates/docs/native-tools.md +688 -0
  93. package/dist/templates/docs/queue.md +514 -0
  94. package/dist/templates/docs/scheduled-prompts.md +279 -0
  95. package/dist/templates/docs/settings.md +415 -0
  96. package/dist/templates/docs/slack.md +318 -0
  97. package/dist/templates/docs/styling.md +288 -0
  98. package/dist/templates/extensions/agents/.gitkeep +0 -0
  99. package/dist/templates/extensions/pages/.gitkeep +0 -0
  100. package/dist/templates/extensions/payment-plans/.gitkeep +0 -0
  101. package/dist/templates/index.html +16 -0
  102. package/dist/templates/infra-aws/.github/workflows/deploy.yml +95 -0
  103. package/dist/templates/infra-aws/README.md +207 -0
  104. package/dist/templates/infra-aws/bin/cdk.ts +18 -0
  105. package/dist/templates/infra-aws/cdk.json +43 -0
  106. package/dist/templates/infra-aws/config/deployment.ts +156 -0
  107. package/dist/templates/infra-aws/lib/chaaskit-stack.ts +419 -0
  108. package/dist/templates/infra-aws/package.json +27 -0
  109. package/dist/templates/infra-aws/scripts/build-app.sh +63 -0
  110. package/dist/templates/infra-aws/tsconfig.json +25 -0
  111. package/dist/templates/package.json +46 -0
  112. package/dist/templates/prisma/schema/base.prisma +584 -0
  113. package/dist/templates/prisma/schema/custom.prisma +24 -0
  114. package/dist/templates/prisma/schema.prisma +271 -0
  115. package/dist/templates/public/favicon.svg +4 -0
  116. package/dist/templates/public/logo.svg +4 -0
  117. package/dist/templates/react-router.config.ts +11 -0
  118. package/dist/templates/server.js +52 -0
  119. package/dist/templates/src/main.tsx +8 -0
  120. package/dist/templates/tsconfig.json +26 -0
  121. package/dist/templates/vite.config.ts +26 -0
  122. package/package.json +46 -0
@@ -0,0 +1,398 @@
1
+ import { Form, redirect, useNavigation } from 'react-router';
2
+ import type { Route } from './+types/register';
3
+ import { config } from '../../config/app.config';
4
+
5
+ export async function loader({}: Route.LoaderArgs) {
6
+ return { config };
7
+ }
8
+
9
+ export async function action({ request }: Route.ActionArgs) {
10
+ const formData = await request.formData();
11
+ const name = formData.get('name') as string;
12
+ const email = formData.get('email') as string;
13
+ const password = formData.get('password') as string;
14
+
15
+ const apiUrl = process.env.API_URL || 'http://localhost:3000';
16
+ const res = await fetch(`${apiUrl}/api/auth/register`, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({ name, email, password }),
20
+ });
21
+
22
+ if (!res.ok) {
23
+ // Try to parse JSON, fallback to text for rate limit errors etc.
24
+ let errorMessage = 'Registration failed';
25
+ const contentType = res.headers.get('content-type');
26
+ if (contentType?.includes('application/json')) {
27
+ try {
28
+ const data = await res.json();
29
+ // Handle both {error: "string"} and {error: {message: "string"}} formats
30
+ errorMessage = typeof data.error === 'string'
31
+ ? data.error
32
+ : data.error?.message || data.message || 'Registration failed';
33
+ } catch {
34
+ errorMessage = await res.text() || 'Registration failed';
35
+ }
36
+ } else {
37
+ errorMessage = await res.text() || 'Registration failed';
38
+ }
39
+ return { error: errorMessage };
40
+ }
41
+
42
+ const data = await res.json();
43
+
44
+ // Handle email verification flow
45
+ if (data.requiresVerification) {
46
+ return redirect('/verify-email');
47
+ }
48
+
49
+ // Forward the session cookie and redirect to chat
50
+ const setCookie = res.headers.get('set-cookie');
51
+ return redirect('/chat', {
52
+ headers: setCookie ? { 'Set-Cookie': setCookie } : {},
53
+ });
54
+ }
55
+
56
+ export function meta({}: Route.MetaArgs) {
57
+ return [{ title: `Create Account - ${config.app.name}` }];
58
+ }
59
+
60
+ export default function Register({
61
+ loaderData,
62
+ actionData,
63
+ }: Route.ComponentProps) {
64
+ const { config } = loaderData;
65
+ const navigation = useNavigation();
66
+ const isSubmitting = navigation.state === 'submitting';
67
+
68
+ const hasGoogle = config.auth?.methods?.includes('google') ?? false;
69
+ const hasGitHub = config.auth?.methods?.includes('github') ?? false;
70
+ const hasEmailPassword = config.auth?.methods?.includes('email-password') ?? true;
71
+
72
+ return (
73
+ <div
74
+ style={{
75
+ display: 'flex',
76
+ minHeight: '100vh',
77
+ alignItems: 'center',
78
+ justifyContent: 'center',
79
+ backgroundColor: 'rgb(var(--color-background))',
80
+ padding: '1rem',
81
+ }}
82
+ >
83
+ <div style={{ width: '100%', maxWidth: '28rem' }}>
84
+ <div style={{ marginBottom: '2rem', textAlign: 'center' }}>
85
+ {config.ui?.logo && (
86
+ <img
87
+ src={
88
+ typeof config.ui.logo === 'string'
89
+ ? config.ui.logo
90
+ : config.ui.logo.light
91
+ }
92
+ alt={config.app.name}
93
+ style={{
94
+ height: '4rem',
95
+ width: '4rem',
96
+ marginLeft: 'auto',
97
+ marginRight: 'auto',
98
+ marginBottom: '1rem',
99
+ borderRadius: '0.5rem',
100
+ objectFit: 'contain',
101
+ }}
102
+ />
103
+ )}
104
+ <h1
105
+ style={{
106
+ fontSize: '1.875rem',
107
+ fontWeight: 'bold',
108
+ color: 'rgb(var(--color-text-primary))',
109
+ margin: 0,
110
+ }}
111
+ >
112
+ Create an account
113
+ </h1>
114
+ <p
115
+ style={{
116
+ marginTop: '0.5rem',
117
+ color: 'rgb(var(--color-text-secondary))',
118
+ }}
119
+ >
120
+ Get started with {config.app.name}
121
+ </p>
122
+ </div>
123
+
124
+ {/* OAuth buttons */}
125
+ {(hasGoogle || hasGitHub) && (
126
+ <div
127
+ style={{
128
+ marginBottom: '1.5rem',
129
+ display: 'flex',
130
+ flexDirection: 'column',
131
+ gap: '0.75rem',
132
+ }}
133
+ >
134
+ {hasGoogle && (
135
+ <a
136
+ href="/api/auth/oauth/google"
137
+ style={{
138
+ display: 'flex',
139
+ width: '100%',
140
+ alignItems: 'center',
141
+ justifyContent: 'center',
142
+ gap: '0.5rem',
143
+ borderRadius: '0.5rem',
144
+ border: '1px solid rgb(var(--color-border))',
145
+ backgroundColor: 'rgb(var(--color-background))',
146
+ padding: '0.5rem 1rem',
147
+ color: 'rgb(var(--color-text-primary))',
148
+ textDecoration: 'none',
149
+ }}
150
+ >
151
+ Sign up with Google
152
+ </a>
153
+ )}
154
+ {hasGitHub && (
155
+ <a
156
+ href="/api/auth/oauth/github"
157
+ style={{
158
+ display: 'flex',
159
+ width: '100%',
160
+ alignItems: 'center',
161
+ justifyContent: 'center',
162
+ gap: '0.5rem',
163
+ borderRadius: '0.5rem',
164
+ border: '1px solid rgb(var(--color-border))',
165
+ backgroundColor: 'rgb(var(--color-background))',
166
+ padding: '0.5rem 1rem',
167
+ color: 'rgb(var(--color-text-primary))',
168
+ textDecoration: 'none',
169
+ }}
170
+ >
171
+ Sign up with GitHub
172
+ </a>
173
+ )}
174
+ </div>
175
+ )}
176
+
177
+ {(hasGoogle || hasGitHub) && hasEmailPassword && (
178
+ <div style={{ position: 'relative', marginBottom: '1.5rem' }}>
179
+ <div
180
+ style={{
181
+ position: 'absolute',
182
+ inset: 0,
183
+ display: 'flex',
184
+ alignItems: 'center',
185
+ }}
186
+ >
187
+ <div
188
+ style={{
189
+ width: '100%',
190
+ borderTop: '1px solid rgb(var(--color-border))',
191
+ }}
192
+ />
193
+ </div>
194
+ <div
195
+ style={{
196
+ position: 'relative',
197
+ display: 'flex',
198
+ justifyContent: 'center',
199
+ fontSize: '0.875rem',
200
+ }}
201
+ >
202
+ <span
203
+ style={{
204
+ backgroundColor: 'rgb(var(--color-background))',
205
+ padding: '0 0.5rem',
206
+ color: 'rgb(var(--color-text-muted))',
207
+ }}
208
+ >
209
+ Or continue with email
210
+ </span>
211
+ </div>
212
+ </div>
213
+ )}
214
+
215
+ {hasEmailPassword && (
216
+ <Form
217
+ method="post"
218
+ style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}
219
+ >
220
+ <div>
221
+ <label
222
+ htmlFor="name"
223
+ style={{
224
+ display: 'block',
225
+ fontSize: '0.875rem',
226
+ fontWeight: 500,
227
+ color: 'rgb(var(--color-text-primary))',
228
+ }}
229
+ >
230
+ Name
231
+ </label>
232
+ <input
233
+ type="text"
234
+ id="name"
235
+ name="name"
236
+ required
237
+ style={{
238
+ marginTop: '0.25rem',
239
+ width: '100%',
240
+ borderRadius: '0.5rem',
241
+ border: '1px solid rgb(var(--color-input-border))',
242
+ backgroundColor: 'rgb(var(--color-input-background))',
243
+ padding: '0.5rem 1rem',
244
+ color: 'rgb(var(--color-text-primary))',
245
+ boxSizing: 'border-box',
246
+ }}
247
+ />
248
+ </div>
249
+
250
+ <div>
251
+ <label
252
+ htmlFor="email"
253
+ style={{
254
+ display: 'block',
255
+ fontSize: '0.875rem',
256
+ fontWeight: 500,
257
+ color: 'rgb(var(--color-text-primary))',
258
+ }}
259
+ >
260
+ Email
261
+ </label>
262
+ <input
263
+ type="email"
264
+ id="email"
265
+ name="email"
266
+ required
267
+ style={{
268
+ marginTop: '0.25rem',
269
+ width: '100%',
270
+ borderRadius: '0.5rem',
271
+ border: '1px solid rgb(var(--color-input-border))',
272
+ backgroundColor: 'rgb(var(--color-input-background))',
273
+ padding: '0.5rem 1rem',
274
+ color: 'rgb(var(--color-text-primary))',
275
+ boxSizing: 'border-box',
276
+ }}
277
+ />
278
+ </div>
279
+
280
+ <div>
281
+ <label
282
+ htmlFor="password"
283
+ style={{
284
+ display: 'block',
285
+ fontSize: '0.875rem',
286
+ fontWeight: 500,
287
+ color: 'rgb(var(--color-text-primary))',
288
+ }}
289
+ >
290
+ Password
291
+ </label>
292
+ <input
293
+ type="password"
294
+ id="password"
295
+ name="password"
296
+ required
297
+ minLength={8}
298
+ style={{
299
+ marginTop: '0.25rem',
300
+ width: '100%',
301
+ borderRadius: '0.5rem',
302
+ border: '1px solid rgb(var(--color-input-border))',
303
+ backgroundColor: 'rgb(var(--color-input-background))',
304
+ padding: '0.5rem 1rem',
305
+ color: 'rgb(var(--color-text-primary))',
306
+ boxSizing: 'border-box',
307
+ }}
308
+ />
309
+ </div>
310
+
311
+ {actionData?.error && (
312
+ <div
313
+ style={{
314
+ padding: '0.75rem',
315
+ backgroundColor: 'rgba(239, 68, 68, 0.1)',
316
+ borderRadius: '0.5rem',
317
+ color: 'rgb(239, 68, 68)',
318
+ fontSize: '0.875rem',
319
+ textAlign: 'center',
320
+ }}
321
+ >
322
+ {actionData.error}
323
+ </div>
324
+ )}
325
+
326
+ <button
327
+ type="submit"
328
+ disabled={isSubmitting}
329
+ style={{
330
+ width: '100%',
331
+ borderRadius: '0.5rem',
332
+ backgroundColor: 'rgb(var(--color-primary))',
333
+ padding: '0.5rem 1rem',
334
+ fontWeight: 500,
335
+ color: 'white',
336
+ border: 'none',
337
+ cursor: isSubmitting ? 'not-allowed' : 'pointer',
338
+ opacity: isSubmitting ? 0.7 : 1,
339
+ }}
340
+ >
341
+ {isSubmitting ? 'Creating account...' : 'Create account'}
342
+ </button>
343
+ </Form>
344
+ )}
345
+
346
+ <p
347
+ style={{
348
+ marginTop: '1rem',
349
+ textAlign: 'center',
350
+ fontSize: '0.75rem',
351
+ color: 'rgb(var(--color-text-muted))',
352
+ }}
353
+ >
354
+ By signing up, you agree to our{' '}
355
+ <a
356
+ href={config.legal?.termsOfServiceUrl || '/terms'}
357
+ style={{
358
+ color: 'rgb(var(--color-primary))',
359
+ textDecoration: 'none',
360
+ }}
361
+ >
362
+ Terms of Service
363
+ </a>{' '}
364
+ and{' '}
365
+ <a
366
+ href={config.legal?.privacyPolicyUrl || '/privacy'}
367
+ style={{
368
+ color: 'rgb(var(--color-primary))',
369
+ textDecoration: 'none',
370
+ }}
371
+ >
372
+ Privacy Policy
373
+ </a>
374
+ </p>
375
+
376
+ <p
377
+ style={{
378
+ marginTop: '1.5rem',
379
+ textAlign: 'center',
380
+ fontSize: '0.875rem',
381
+ color: 'rgb(var(--color-text-secondary))',
382
+ }}
383
+ >
384
+ Already have an account?{' '}
385
+ <a
386
+ href="/login"
387
+ style={{
388
+ color: 'rgb(var(--color-primary))',
389
+ textDecoration: 'none',
390
+ }}
391
+ >
392
+ Sign in
393
+ </a>
394
+ </p>
395
+ </div>
396
+ </div>
397
+ );
398
+ }
@@ -0,0 +1,226 @@
1
+ import type { Route } from './+types/shared.$shareId';
2
+ import { loadSharedThread } from '@chaaskit/server/loaders';
3
+ import { config } from '../../config/app.config';
4
+
5
+ // Simple SSR-safe message list component
6
+ // This renders messages as plain text for SEO, markdown hydration happens client-side if needed
7
+ function SimpleMessageList({ messages }: { messages: Array<{ id: string; role: string; content: string }> }) {
8
+ return (
9
+ <div style={{ maxWidth: '48rem', marginLeft: 'auto', marginRight: 'auto', padding: '1rem' }}>
10
+ {messages.map((message) => (
11
+ <div
12
+ key={message.id}
13
+ style={{
14
+ marginBottom: '1.5rem',
15
+ padding: '1rem',
16
+ borderRadius: '0.5rem',
17
+ backgroundColor:
18
+ message.role === 'user'
19
+ ? 'rgb(var(--color-user-message-bg))'
20
+ : 'rgb(var(--color-assistant-message-bg))',
21
+ color:
22
+ message.role === 'user'
23
+ ? 'rgb(var(--color-user-message-text))'
24
+ : 'rgb(var(--color-assistant-message-text))',
25
+ }}
26
+ >
27
+ <div
28
+ style={{
29
+ fontSize: '0.75rem',
30
+ fontWeight: 600,
31
+ marginBottom: '0.5rem',
32
+ textTransform: 'uppercase',
33
+ opacity: 0.7,
34
+ }}
35
+ >
36
+ {message.role === 'user' ? 'You' : 'Assistant'}
37
+ </div>
38
+ <div style={{ whiteSpace: 'pre-wrap' }}>{message.content}</div>
39
+ </div>
40
+ ))}
41
+ </div>
42
+ );
43
+ }
44
+
45
+ export async function loader({ params }: Route.LoaderArgs) {
46
+ try {
47
+ const data = await loadSharedThread(params.shareId!);
48
+ return { ...data, config, error: null };
49
+ } catch (error: any) {
50
+ return {
51
+ thread: null,
52
+ config,
53
+ error: {
54
+ message: error.message || 'Failed to load thread',
55
+ status: error.status || 500,
56
+ },
57
+ };
58
+ }
59
+ }
60
+
61
+ export function meta({ data }: Route.MetaArgs) {
62
+ if (data?.error || !data?.thread) {
63
+ return [{ title: 'Thread Not Found' }];
64
+ }
65
+ return [
66
+ { title: `${data.thread.title} | ${data.config.app.name}` },
67
+ {
68
+ name: 'description',
69
+ content: `A shared conversation with ${data.thread.messages.length} messages`,
70
+ },
71
+ { property: 'og:title', content: data.thread.title },
72
+ {
73
+ property: 'og:description',
74
+ content: `A shared conversation with ${data.thread.messages.length} messages`,
75
+ },
76
+ { property: 'og:type', content: 'article' },
77
+ ];
78
+ }
79
+
80
+ export default function SharedThread({ loaderData }: Route.ComponentProps) {
81
+ const { thread, config, error } = loaderData;
82
+
83
+ if (error || !thread) {
84
+ return (
85
+ <div
86
+ style={{
87
+ display: 'flex',
88
+ flexDirection: 'column',
89
+ alignItems: 'center',
90
+ justifyContent: 'center',
91
+ minHeight: '100vh',
92
+ padding: '1rem',
93
+ }}
94
+ >
95
+ <h1
96
+ style={{
97
+ fontSize: '1.5rem',
98
+ fontWeight: 'bold',
99
+ marginBottom: '0.5rem',
100
+ color: 'rgb(var(--color-text-primary))',
101
+ }}
102
+ >
103
+ {error?.status === 410
104
+ ? 'Link Expired'
105
+ : 'Conversation not available'}
106
+ </h1>
107
+ <p
108
+ style={{
109
+ color: 'rgb(var(--color-text-secondary))',
110
+ marginBottom: '1.5rem',
111
+ }}
112
+ >
113
+ {error?.status === 410
114
+ ? 'This share link has expired.'
115
+ : 'This shared conversation was not found or has been removed.'}
116
+ </p>
117
+ <a
118
+ href="/"
119
+ style={{
120
+ backgroundColor: 'rgb(var(--color-primary))',
121
+ color: 'white',
122
+ padding: '0.5rem 1rem',
123
+ borderRadius: '0.5rem',
124
+ textDecoration: 'none',
125
+ }}
126
+ >
127
+ Go to Home
128
+ </a>
129
+ </div>
130
+ );
131
+ }
132
+
133
+ return (
134
+ <div
135
+ style={{
136
+ minHeight: '100vh',
137
+ backgroundColor: 'rgb(var(--color-background))',
138
+ }}
139
+ >
140
+ {/* Header */}
141
+ <header
142
+ style={{
143
+ borderBottom: '1px solid rgb(var(--color-border))',
144
+ backgroundColor: 'rgb(var(--color-background-secondary))',
145
+ }}
146
+ >
147
+ <div
148
+ style={{
149
+ maxWidth: '48rem',
150
+ marginLeft: 'auto',
151
+ marginRight: 'auto',
152
+ padding: '1rem',
153
+ }}
154
+ >
155
+ <div
156
+ style={{
157
+ display: 'flex',
158
+ alignItems: 'center',
159
+ justifyContent: 'space-between',
160
+ }}
161
+ >
162
+ <div>
163
+ <h1
164
+ style={{
165
+ fontSize: '1.125rem',
166
+ fontWeight: 600,
167
+ color: 'rgb(var(--color-text-primary))',
168
+ margin: 0,
169
+ }}
170
+ >
171
+ {thread.title}
172
+ </h1>
173
+ <p
174
+ style={{
175
+ fontSize: '0.875rem',
176
+ color: 'rgb(var(--color-text-muted))',
177
+ margin: '0.25rem 0 0 0',
178
+ }}
179
+ >
180
+ Shared conversation from {config.app.name}
181
+ </p>
182
+ </div>
183
+ <a
184
+ href="/"
185
+ style={{
186
+ backgroundColor: 'rgb(var(--color-primary))',
187
+ color: 'white',
188
+ padding: '0.5rem 1rem',
189
+ borderRadius: '0.5rem',
190
+ fontSize: '0.875rem',
191
+ textDecoration: 'none',
192
+ }}
193
+ >
194
+ Try {config.app.name}
195
+ </a>
196
+ </div>
197
+ </div>
198
+ </header>
199
+
200
+ {/* Messages */}
201
+ <main style={{ paddingBottom: '2rem' }}>
202
+ <SimpleMessageList messages={thread.messages} />
203
+ </main>
204
+
205
+ {/* Footer */}
206
+ <footer
207
+ style={{
208
+ borderTop: '1px solid rgb(var(--color-border))',
209
+ backgroundColor: 'rgb(var(--color-background-secondary))',
210
+ padding: '1rem',
211
+ }}
212
+ >
213
+ <p
214
+ style={{
215
+ textAlign: 'center',
216
+ fontSize: '0.875rem',
217
+ color: 'rgb(var(--color-text-muted))',
218
+ margin: 0,
219
+ }}
220
+ >
221
+ Powered by {config.app.name}
222
+ </p>
223
+ </footer>
224
+ </div>
225
+ );
226
+ }
@@ -0,0 +1,57 @@
1
+ import type { Route } from './+types/team.$teamId.settings';
2
+ import { lazy, Suspense } from 'react';
3
+ import { ClientOnly } from '../components/ClientOnly';
4
+
5
+ const TeamSettingsClient = lazy(() => import('../components/TeamSettingsClient'));
6
+
7
+ export function meta({}: Route.MetaArgs) {
8
+ return [{ title: 'Team Settings' }];
9
+ }
10
+
11
+ export function links() {
12
+ return [{ rel: 'stylesheet', href: '/node_modules/@chaaskit/client/dist/lib/styles.css' }];
13
+ }
14
+
15
+ export default function TeamSettings() {
16
+ return (
17
+ <ClientOnly fallback={<LoadingSkeleton />}>
18
+ {() => (
19
+ <Suspense fallback={<LoadingSkeleton />}>
20
+ <TeamSettingsClient />
21
+ </Suspense>
22
+ )}
23
+ </ClientOnly>
24
+ );
25
+ }
26
+
27
+ function LoadingSkeleton() {
28
+ return (
29
+ <div
30
+ style={{
31
+ display: 'flex',
32
+ height: '100vh',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ backgroundColor: 'rgb(var(--color-background))',
36
+ }}
37
+ >
38
+ <div
39
+ style={{
40
+ width: '2rem',
41
+ height: '2rem',
42
+ border: '2px solid rgb(var(--color-primary))',
43
+ borderTopColor: 'transparent',
44
+ borderRadius: '50%',
45
+ animation: 'spin 1s linear infinite',
46
+ }}
47
+ />
48
+ <style>
49
+ {`
50
+ @keyframes spin {
51
+ to { transform: rotate(360deg); }
52
+ }
53
+ `}
54
+ </style>
55
+ </div>
56
+ );
57
+ }