@chaaskit/client 0.1.0 → 0.1.2
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.
- package/LICENSE +21 -0
- package/dist/lib/index.js +1023 -160
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/routes/AcceptInviteRoute.js +1 -1
- package/dist/lib/routes/AcceptInviteRoute.js.map +1 -1
- package/dist/lib/routes/AdminDashboardRoute.js +1 -1
- package/dist/lib/routes/AdminDashboardRoute.js.map +1 -1
- package/dist/lib/routes/AdminPromoCodesRoute.js +19 -0
- package/dist/lib/routes/AdminPromoCodesRoute.js.map +1 -0
- package/dist/lib/routes/AdminTeamRoute.js +1 -1
- package/dist/lib/routes/AdminTeamRoute.js.map +1 -1
- package/dist/lib/routes/AdminTeamsRoute.js +1 -1
- package/dist/lib/routes/AdminTeamsRoute.js.map +1 -1
- package/dist/lib/routes/AdminUsersRoute.js +1 -1
- package/dist/lib/routes/AdminUsersRoute.js.map +1 -1
- package/dist/lib/routes/AdminWaitlistRoute.js +19 -0
- package/dist/lib/routes/AdminWaitlistRoute.js.map +1 -0
- package/dist/lib/routes/ApiKeysRoute.js +1 -1
- package/dist/lib/routes/ApiKeysRoute.js.map +1 -1
- package/dist/lib/routes/AutomationsRoute.js +1 -1
- package/dist/lib/routes/AutomationsRoute.js.map +1 -1
- package/dist/lib/routes/ChatRoute.js +1 -1
- package/dist/lib/routes/ChatRoute.js.map +1 -1
- package/dist/lib/routes/DocumentsRoute.js +1 -1
- package/dist/lib/routes/DocumentsRoute.js.map +1 -1
- package/dist/lib/routes/OAuthConsentRoute.js +1 -1
- package/dist/lib/routes/OAuthConsentRoute.js.map +1 -1
- package/dist/lib/routes/PricingRoute.js +1 -1
- package/dist/lib/routes/PricingRoute.js.map +1 -1
- package/dist/lib/routes/PrivacyRoute.js +1 -1
- package/dist/lib/routes/PrivacyRoute.js.map +1 -1
- package/dist/lib/routes/TeamSettingsRoute.js +1 -1
- package/dist/lib/routes/TeamSettingsRoute.js.map +1 -1
- package/dist/lib/routes/TermsRoute.js +1 -1
- package/dist/lib/routes/TermsRoute.js.map +1 -1
- package/dist/lib/routes/VerifyEmailRoute.js +1 -1
- package/dist/lib/routes/VerifyEmailRoute.js.map +1 -1
- package/dist/lib/routes.js +47 -37
- package/dist/lib/routes.js.map +1 -1
- package/dist/lib/ssr-utils.js +64 -1
- package/dist/lib/ssr-utils.js.map +1 -1
- package/dist/lib/ssr.js +23 -0
- package/dist/lib/ssr.js.map +1 -1
- package/dist/lib/styles.css +58 -62
- package/dist/lib/useExtensions-B5nX_8XD.js.map +1 -1
- package/package.json +25 -12
- package/src/components/MessageItem.tsx +35 -4
- package/src/components/MessageList.tsx +51 -5
- package/src/components/OAuthAppsSection.tsx +1 -1
- package/src/components/Sidebar.tsx +1 -3
- package/src/components/ToolCallDisplay.tsx +102 -11
- package/src/components/tool-renderers/DocumentListRenderer.tsx +44 -0
- package/src/components/tool-renderers/DocumentReadRenderer.tsx +33 -0
- package/src/components/tool-renderers/DocumentSaveRenderer.tsx +32 -0
- package/src/components/tool-renderers/DocumentSearchRenderer.tsx +33 -0
- package/src/components/tool-renderers/index.ts +36 -0
- package/src/components/tool-renderers/utils.ts +7 -0
- package/src/contexts/AuthContext.tsx +16 -6
- package/src/contexts/ConfigContext.tsx +60 -28
- package/src/contexts/ThemeContext.tsx +39 -68
- package/src/extensions/registry.ts +2 -1
- package/src/hooks/__tests__/basePath.test.ts +42 -0
- package/src/index.tsx +11 -2
- package/src/pages/AdminDashboardPage.tsx +15 -1
- package/src/pages/AdminPromoCodesPage.tsx +378 -0
- package/src/pages/AdminTeamPage.tsx +29 -1
- package/src/pages/AdminTeamsPage.tsx +15 -1
- package/src/pages/AdminUsersPage.tsx +15 -1
- package/src/pages/AdminWaitlistPage.tsx +156 -0
- package/src/pages/RegisterPage.tsx +91 -9
- package/src/routes/AcceptInviteRoute.tsx +1 -1
- package/src/routes/AdminDashboardRoute.tsx +1 -1
- package/src/routes/AdminPromoCodesRoute.tsx +24 -0
- package/src/routes/AdminTeamRoute.tsx +1 -1
- package/src/routes/AdminTeamsRoute.tsx +1 -1
- package/src/routes/AdminUsersRoute.tsx +1 -1
- package/src/routes/AdminWaitlistRoute.tsx +24 -0
- package/src/routes/ApiKeysRoute.tsx +1 -1
- package/src/routes/AutomationsRoute.tsx +1 -1
- package/src/routes/ChatRoute.tsx +2 -1
- package/src/routes/DocumentsRoute.tsx +1 -1
- package/src/routes/OAuthConsentRoute.tsx +1 -1
- package/src/routes/PricingRoute.tsx +1 -1
- package/src/routes/PrivacyRoute.tsx +1 -1
- package/src/routes/TeamSettingsRoute.tsx +1 -1
- package/src/routes/TermsRoute.tsx +1 -1
- package/src/routes/VerifyEmailRoute.tsx +1 -1
- package/src/routes/index.ts +2 -0
- package/src/ssr-utils.tsx +100 -1
- package/src/ssr.ts +59 -0
- package/src/stores/chatStore.ts +5 -0
- package/src/styles/index.css +16 -63
- package/src/tailwind-preset.js +360 -0
- package/dist/favicon.svg +0 -11
- package/dist/index.html +0 -17
- package/dist/logo.svg +0 -12
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect } from 'react';
|
|
2
2
|
import { Link, useParams } from 'react-router';
|
|
3
|
-
import { LayoutDashboard, X, Users, Building2 } from 'lucide-react';
|
|
3
|
+
import { LayoutDashboard, X, Users, Building2, Mail, Tag } from 'lucide-react';
|
|
4
4
|
import { useAppPath } from '../hooks/useAppPath';
|
|
5
5
|
import { api } from '../utils/api';
|
|
6
6
|
import type { AdminTeamDetails } from '@chaaskit/shared';
|
|
@@ -86,6 +86,20 @@ export default function AdminTeamPage() {
|
|
|
86
86
|
<Building2 size={16} />
|
|
87
87
|
Teams
|
|
88
88
|
</Link>
|
|
89
|
+
<Link
|
|
90
|
+
to={appPath('/admin/waitlist')}
|
|
91
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
92
|
+
>
|
|
93
|
+
<Mail size={16} />
|
|
94
|
+
Waitlist
|
|
95
|
+
</Link>
|
|
96
|
+
<Link
|
|
97
|
+
to={appPath('/admin/promo-codes')}
|
|
98
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
99
|
+
>
|
|
100
|
+
<Tag size={16} />
|
|
101
|
+
Promo Codes
|
|
102
|
+
</Link>
|
|
89
103
|
</div>
|
|
90
104
|
|
|
91
105
|
<div className="rounded-lg bg-error/10 p-4 text-sm text-error">
|
|
@@ -142,6 +156,20 @@ export default function AdminTeamPage() {
|
|
|
142
156
|
<Building2 size={16} />
|
|
143
157
|
Teams
|
|
144
158
|
</Link>
|
|
159
|
+
<Link
|
|
160
|
+
to={appPath('/admin/waitlist')}
|
|
161
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
162
|
+
>
|
|
163
|
+
<Mail size={16} />
|
|
164
|
+
Waitlist
|
|
165
|
+
</Link>
|
|
166
|
+
<Link
|
|
167
|
+
to={appPath('/admin/promo-codes')}
|
|
168
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
169
|
+
>
|
|
170
|
+
<Tag size={16} />
|
|
171
|
+
Promo Codes
|
|
172
|
+
</Link>
|
|
145
173
|
</div>
|
|
146
174
|
|
|
147
175
|
{/* Team Header */}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { Link } from 'react-router';
|
|
3
|
-
import { LayoutDashboard, X, Users, Building2 } from 'lucide-react';
|
|
3
|
+
import { LayoutDashboard, X, Users, Building2, Mail, Tag } from 'lucide-react';
|
|
4
4
|
import { useAppPath } from '../hooks/useAppPath';
|
|
5
5
|
import { api } from '../utils/api';
|
|
6
6
|
import type { AdminTeam, AdminTeamsResponse } from '@chaaskit/shared';
|
|
@@ -91,6 +91,20 @@ export default function AdminTeamsPage() {
|
|
|
91
91
|
<Building2 size={16} />
|
|
92
92
|
Teams
|
|
93
93
|
</Link>
|
|
94
|
+
<Link
|
|
95
|
+
to={appPath('/admin/waitlist')}
|
|
96
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
97
|
+
>
|
|
98
|
+
<Mail size={16} />
|
|
99
|
+
Waitlist
|
|
100
|
+
</Link>
|
|
101
|
+
<Link
|
|
102
|
+
to={appPath('/admin/promo-codes')}
|
|
103
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
104
|
+
>
|
|
105
|
+
<Tag size={16} />
|
|
106
|
+
Promo Codes
|
|
107
|
+
</Link>
|
|
94
108
|
</div>
|
|
95
109
|
|
|
96
110
|
{error && (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { Link } from 'react-router';
|
|
3
|
-
import { LayoutDashboard, X, Users, Building2 } from 'lucide-react';
|
|
3
|
+
import { LayoutDashboard, X, Users, Building2, Mail, Tag } from 'lucide-react';
|
|
4
4
|
import { useConfig } from '../contexts/ConfigContext';
|
|
5
5
|
import { useAppPath } from '../hooks/useAppPath';
|
|
6
6
|
import { api } from '../utils/api';
|
|
@@ -134,6 +134,20 @@ export default function AdminUsersPage() {
|
|
|
134
134
|
Teams
|
|
135
135
|
</Link>
|
|
136
136
|
)}
|
|
137
|
+
<Link
|
|
138
|
+
to={appPath('/admin/waitlist')}
|
|
139
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
140
|
+
>
|
|
141
|
+
<Mail size={16} />
|
|
142
|
+
Waitlist
|
|
143
|
+
</Link>
|
|
144
|
+
<Link
|
|
145
|
+
to={appPath('/admin/promo-codes')}
|
|
146
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
147
|
+
>
|
|
148
|
+
<Tag size={16} />
|
|
149
|
+
Promo Codes
|
|
150
|
+
</Link>
|
|
137
151
|
</div>
|
|
138
152
|
|
|
139
153
|
{error && (
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { Link } from 'react-router';
|
|
3
|
+
import { LayoutDashboard, Users, Building2, X, Mail, Tag } from 'lucide-react';
|
|
4
|
+
import { useConfig } from '../contexts/ConfigContext';
|
|
5
|
+
import { useAppPath } from '../hooks/useAppPath';
|
|
6
|
+
import { api } from '../utils/api';
|
|
7
|
+
import type { AdminWaitlistEntry, AdminWaitlistResponse } from '@chaaskit/shared';
|
|
8
|
+
|
|
9
|
+
export default function AdminWaitlistPage() {
|
|
10
|
+
const config = useConfig();
|
|
11
|
+
const appPath = useAppPath();
|
|
12
|
+
const [entries, setEntries] = useState<AdminWaitlistEntry[]>([]);
|
|
13
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
14
|
+
const [error, setError] = useState('');
|
|
15
|
+
const [success, setSuccess] = useState('');
|
|
16
|
+
|
|
17
|
+
async function loadWaitlist() {
|
|
18
|
+
setIsLoading(true);
|
|
19
|
+
setError('');
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const response = await api.get<AdminWaitlistResponse>('/api/admin/waitlist');
|
|
23
|
+
setEntries(response.entries);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
setError(err instanceof Error ? err.message : 'Failed to load waitlist');
|
|
26
|
+
} finally {
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
loadWaitlist();
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
async function handleInvite(entryId: string, email: string) {
|
|
36
|
+
setError('');
|
|
37
|
+
setSuccess('');
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await api.post(`/api/admin/waitlist/${entryId}/invite`, {});
|
|
41
|
+
setSuccess(`Invite sent to ${email}`);
|
|
42
|
+
loadWaitlist();
|
|
43
|
+
} catch (err) {
|
|
44
|
+
setError(err instanceof Error ? err.message : 'Failed to send invite');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="min-h-screen bg-background p-4 sm:p-8">
|
|
50
|
+
<div className="mx-auto max-w-6xl">
|
|
51
|
+
<div className="flex items-center justify-between mb-4">
|
|
52
|
+
<h1 className="text-xl sm:text-2xl font-bold text-text-primary">Admin</h1>
|
|
53
|
+
<Link
|
|
54
|
+
to={appPath('/')}
|
|
55
|
+
className="flex items-center justify-center rounded-lg p-2 text-text-muted hover:text-text-primary hover:bg-background-secondary"
|
|
56
|
+
aria-label="Close"
|
|
57
|
+
>
|
|
58
|
+
<X size={20} />
|
|
59
|
+
</Link>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div className="flex items-center gap-2 mb-6 sm:mb-8">
|
|
63
|
+
<Link
|
|
64
|
+
to={appPath('/admin')}
|
|
65
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
66
|
+
>
|
|
67
|
+
<LayoutDashboard size={16} />
|
|
68
|
+
Overview
|
|
69
|
+
</Link>
|
|
70
|
+
<Link
|
|
71
|
+
to={appPath('/admin/users')}
|
|
72
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
73
|
+
>
|
|
74
|
+
<Users size={16} />
|
|
75
|
+
Users
|
|
76
|
+
</Link>
|
|
77
|
+
{config.teams?.enabled && (
|
|
78
|
+
<Link
|
|
79
|
+
to={appPath('/admin/teams')}
|
|
80
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
81
|
+
>
|
|
82
|
+
<Building2 size={16} />
|
|
83
|
+
Teams
|
|
84
|
+
</Link>
|
|
85
|
+
)}
|
|
86
|
+
<Link
|
|
87
|
+
to={appPath('/admin/promo-codes')}
|
|
88
|
+
className="flex items-center gap-1.5 rounded-full bg-background-secondary px-4 py-2 text-sm font-medium text-text-secondary hover:bg-background-secondary/80"
|
|
89
|
+
>
|
|
90
|
+
<Tag size={16} />
|
|
91
|
+
Promo Codes
|
|
92
|
+
</Link>
|
|
93
|
+
<span className="flex items-center gap-1.5 rounded-full bg-primary px-4 py-2 text-sm font-medium text-white">
|
|
94
|
+
<Mail size={16} />
|
|
95
|
+
Waitlist
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
{error && (
|
|
100
|
+
<div className="mb-6 rounded-lg bg-error/10 p-4 text-sm text-error">
|
|
101
|
+
{error}
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{success && (
|
|
106
|
+
<div className="mb-6 rounded-lg bg-success/10 p-4 text-sm text-success">
|
|
107
|
+
{success}
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
<div className="rounded-lg bg-background-secondary overflow-hidden">
|
|
112
|
+
<div className="hidden md:block px-4 py-3 bg-background">
|
|
113
|
+
<div className="grid grid-cols-12 gap-4 text-sm font-medium text-text-muted">
|
|
114
|
+
<div className="col-span-4">Email</div>
|
|
115
|
+
<div className="col-span-2">Name</div>
|
|
116
|
+
<div className="col-span-2">Status</div>
|
|
117
|
+
<div className="col-span-2">Joined</div>
|
|
118
|
+
<div className="col-span-2">Action</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div className="divide-y divide-background">
|
|
123
|
+
{isLoading ? (
|
|
124
|
+
<div className="px-4 py-8 text-center text-text-muted">Loading...</div>
|
|
125
|
+
) : entries.length === 0 ? (
|
|
126
|
+
<div className="px-4 py-8 text-center text-text-muted">No waitlist entries</div>
|
|
127
|
+
) : (
|
|
128
|
+
entries.map((entry) => (
|
|
129
|
+
<div key={entry.id} className="px-4 py-3 hover:bg-background/50">
|
|
130
|
+
<div className="grid grid-cols-1 md:grid-cols-12 gap-2 md:gap-4 text-sm">
|
|
131
|
+
<div className="md:col-span-4 text-text-primary">{entry.email}</div>
|
|
132
|
+
<div className="md:col-span-2 text-text-secondary">{entry.name || '-'}</div>
|
|
133
|
+
<div className="md:col-span-2 capitalize text-text-secondary">{entry.status.replace('_', ' ')}</div>
|
|
134
|
+
<div className="md:col-span-2 text-text-muted">
|
|
135
|
+
{new Date(entry.createdAt).toLocaleDateString()}
|
|
136
|
+
</div>
|
|
137
|
+
<div className="md:col-span-2">
|
|
138
|
+
<button
|
|
139
|
+
type="button"
|
|
140
|
+
onClick={() => handleInvite(entry.id, entry.email)}
|
|
141
|
+
disabled={entry.status === 'invited'}
|
|
142
|
+
className="rounded-lg bg-primary px-3 py-1.5 text-xs font-medium text-white hover:bg-primary-hover disabled:opacity-50"
|
|
143
|
+
>
|
|
144
|
+
{entry.status === 'invited' ? 'Invited' : 'Invite'}
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
))
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import { Link, useNavigate } from 'react-router';
|
|
2
|
+
import { Link, useNavigate, useSearchParams } from 'react-router';
|
|
3
3
|
import { useAuth } from '../contexts/AuthContext';
|
|
4
4
|
import { useConfig } from '../contexts/ConfigContext';
|
|
5
5
|
import { useTheme } from '../contexts/ThemeContext';
|
|
6
6
|
import { useAppPath } from '../hooks/useAppPath';
|
|
7
|
+
import { api } from '../utils/api';
|
|
7
8
|
|
|
8
9
|
export default function RegisterPage() {
|
|
9
10
|
const navigate = useNavigate();
|
|
@@ -11,12 +12,22 @@ export default function RegisterPage() {
|
|
|
11
12
|
const { register } = useAuth();
|
|
12
13
|
const config = useConfig();
|
|
13
14
|
const { theme } = useTheme();
|
|
15
|
+
const [searchParams] = useSearchParams();
|
|
16
|
+
|
|
17
|
+
const inviteToken = searchParams.get('invite') || undefined;
|
|
18
|
+
const referralCode = searchParams.get('ref') || undefined;
|
|
19
|
+
const gating = config.auth.gating;
|
|
20
|
+
const signupsRestricted = gating?.mode && gating.mode !== 'open' && !inviteToken;
|
|
21
|
+
const waitlistEnabled = gating?.waitlistEnabled ?? false;
|
|
22
|
+
const showWaitlist = !!signupsRestricted && waitlistEnabled;
|
|
23
|
+
const showRestrictedMessage = !!signupsRestricted && !waitlistEnabled;
|
|
14
24
|
|
|
15
25
|
const [name, setName] = useState('');
|
|
16
26
|
const [email, setEmail] = useState('');
|
|
17
27
|
const [password, setPassword] = useState('');
|
|
18
28
|
const [error, setError] = useState('');
|
|
19
29
|
const [isLoading, setIsLoading] = useState(false);
|
|
30
|
+
const [waitlistStatus, setWaitlistStatus] = useState<'idle' | 'submitted'>('idle');
|
|
20
31
|
|
|
21
32
|
async function handleSubmit(e: React.FormEvent) {
|
|
22
33
|
e.preventDefault();
|
|
@@ -30,7 +41,10 @@ export default function RegisterPage() {
|
|
|
30
41
|
setIsLoading(true);
|
|
31
42
|
|
|
32
43
|
try {
|
|
33
|
-
const { requiresVerification } = await register(email, password, name || undefined
|
|
44
|
+
const { requiresVerification } = await register(email, password, name || undefined, {
|
|
45
|
+
inviteToken,
|
|
46
|
+
referralCode,
|
|
47
|
+
});
|
|
34
48
|
if (requiresVerification) {
|
|
35
49
|
navigate('/verify-email');
|
|
36
50
|
} else {
|
|
@@ -43,6 +57,18 @@ export default function RegisterPage() {
|
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
|
|
60
|
+
async function handleWaitlistSubmit(e: React.FormEvent) {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
setError('');
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
await api.post('/api/auth/waitlist', { email, name: name || undefined });
|
|
66
|
+
setWaitlistStatus('submitted');
|
|
67
|
+
} catch (err) {
|
|
68
|
+
setError(err instanceof Error ? err.message : 'Failed to join waitlist');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
return (
|
|
47
73
|
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
|
48
74
|
<div className="w-full max-w-md">
|
|
@@ -66,7 +92,60 @@ export default function RegisterPage() {
|
|
|
66
92
|
</div>
|
|
67
93
|
)}
|
|
68
94
|
|
|
69
|
-
|
|
95
|
+
{showRestrictedMessage ? (
|
|
96
|
+
<div className="rounded-lg border border-border bg-background-secondary p-4 text-sm text-text-secondary">
|
|
97
|
+
Signups are currently closed. Please check back later.
|
|
98
|
+
</div>
|
|
99
|
+
) : showWaitlist ? (
|
|
100
|
+
<div className="rounded-lg border border-border bg-background-secondary p-4">
|
|
101
|
+
<p className="text-sm text-text-secondary">
|
|
102
|
+
Signups are currently restricted. Join the waitlist to get an invite.
|
|
103
|
+
</p>
|
|
104
|
+
|
|
105
|
+
{waitlistStatus === 'submitted' ? (
|
|
106
|
+
<div className="mt-4 rounded-lg bg-success/10 p-3 text-sm text-success">
|
|
107
|
+
Thanks! You’re on the waitlist.
|
|
108
|
+
</div>
|
|
109
|
+
) : (
|
|
110
|
+
<form onSubmit={handleWaitlistSubmit} className="mt-4 space-y-3">
|
|
111
|
+
<div>
|
|
112
|
+
<label htmlFor="waitlist-name" className="block text-sm font-medium text-text-primary">
|
|
113
|
+
Name (optional)
|
|
114
|
+
</label>
|
|
115
|
+
<input
|
|
116
|
+
type="text"
|
|
117
|
+
id="waitlist-name"
|
|
118
|
+
value={name}
|
|
119
|
+
onChange={(e) => setName(e.target.value)}
|
|
120
|
+
className="mt-1 w-full rounded-lg border border-input-border bg-input-background px-4 py-2 text-text-primary focus:border-primary focus:outline-none"
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div>
|
|
125
|
+
<label htmlFor="waitlist-email" className="block text-sm font-medium text-text-primary">
|
|
126
|
+
Email
|
|
127
|
+
</label>
|
|
128
|
+
<input
|
|
129
|
+
type="email"
|
|
130
|
+
id="waitlist-email"
|
|
131
|
+
value={email}
|
|
132
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
133
|
+
required
|
|
134
|
+
className="mt-1 w-full rounded-lg border border-input-border bg-input-background px-4 py-2 text-text-primary focus:border-primary focus:outline-none"
|
|
135
|
+
/>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<button
|
|
139
|
+
type="submit"
|
|
140
|
+
className="w-full rounded-lg bg-primary px-4 py-2 font-medium text-white hover:bg-primary-hover"
|
|
141
|
+
>
|
|
142
|
+
Join waitlist
|
|
143
|
+
</button>
|
|
144
|
+
</form>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
147
|
+
) : (
|
|
148
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
70
149
|
<div>
|
|
71
150
|
<label
|
|
72
151
|
htmlFor="name"
|
|
@@ -129,13 +208,16 @@ export default function RegisterPage() {
|
|
|
129
208
|
{isLoading ? 'Creating account...' : 'Create account'}
|
|
130
209
|
</button>
|
|
131
210
|
</form>
|
|
211
|
+
)}
|
|
132
212
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
213
|
+
{!signupsRestricted && (
|
|
214
|
+
<p className="mt-6 text-center text-sm text-text-secondary">
|
|
215
|
+
Already have an account?{' '}
|
|
216
|
+
<Link to="/login" className="text-primary hover:underline">
|
|
217
|
+
Sign in
|
|
218
|
+
</Link>
|
|
219
|
+
</p>
|
|
220
|
+
)}
|
|
139
221
|
|
|
140
222
|
<p className="mt-4 text-center text-xs text-text-muted">
|
|
141
223
|
By creating an account, you agree to our{' '}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Promo Codes Route - Promo code management
|
|
3
|
+
*/
|
|
4
|
+
import { ChatProviders } from '../index';
|
|
5
|
+
import AdminPromoCodesPage from '../pages/AdminPromoCodesPage';
|
|
6
|
+
import { SimpleLoadingSkeleton } from '../components/LoadingSkeletons';
|
|
7
|
+
|
|
8
|
+
export function meta() {
|
|
9
|
+
return [{ title: 'Admin - Promo Codes' }];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function links() {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function AdminPromoCodesRoute() {
|
|
17
|
+
return (
|
|
18
|
+
<ChatProviders>
|
|
19
|
+
<AdminPromoCodesPage />
|
|
20
|
+
</ChatProviders>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { SimpleLoadingSkeleton as LoadingSkeleton };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Waitlist Route - Waitlist management
|
|
3
|
+
*/
|
|
4
|
+
import { ChatProviders } from '../index';
|
|
5
|
+
import AdminWaitlistPage from '../pages/AdminWaitlistPage';
|
|
6
|
+
import { SimpleLoadingSkeleton } from '../components/LoadingSkeletons';
|
|
7
|
+
|
|
8
|
+
export function meta() {
|
|
9
|
+
return [{ title: 'Admin - Waitlist' }];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function links() {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function AdminWaitlistRoute() {
|
|
17
|
+
return (
|
|
18
|
+
<ChatProviders>
|
|
19
|
+
<AdminWaitlistPage />
|
|
20
|
+
</ChatProviders>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { SimpleLoadingSkeleton as LoadingSkeleton };
|
package/src/routes/ChatRoute.tsx
CHANGED
|
@@ -12,7 +12,8 @@ export function meta() {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function links() {
|
|
15
|
-
|
|
15
|
+
// CSS is bundled via app's Tailwind preset - no separate stylesheet needed
|
|
16
|
+
return [];
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export default function ChatRoute() {
|
package/src/routes/index.ts
CHANGED
|
@@ -45,6 +45,8 @@ export { default as AdminDashboardRoute, meta as adminDashboardMeta, links as ad
|
|
|
45
45
|
export { default as AdminUsersRoute, meta as adminUsersMeta, links as adminUsersLinks, LoadingSkeleton as AdminUsersLoadingSkeleton } from './AdminUsersRoute';
|
|
46
46
|
export { default as AdminTeamsRoute, meta as adminTeamsMeta, links as adminTeamsLinks, LoadingSkeleton as AdminTeamsLoadingSkeleton } from './AdminTeamsRoute';
|
|
47
47
|
export { default as AdminTeamRoute, meta as adminTeamMeta, links as adminTeamLinks, LoadingSkeleton as AdminTeamLoadingSkeleton } from './AdminTeamRoute';
|
|
48
|
+
export { default as AdminWaitlistRoute, meta as adminWaitlistMeta, links as adminWaitlistLinks, LoadingSkeleton as AdminWaitlistLoadingSkeleton } from './AdminWaitlistRoute';
|
|
49
|
+
export { default as AdminPromoCodesRoute, meta as adminPromoCodesMeta, links as adminPromoCodesLinks, LoadingSkeleton as AdminPromoCodesLoadingSkeleton } from './AdminPromoCodesRoute';
|
|
48
50
|
|
|
49
51
|
// Auth-related routes
|
|
50
52
|
export { default as VerifyEmailRoute, meta as verifyEmailMeta, links as verifyEmailLinks, LoadingSkeleton as VerifyEmailLoadingSkeleton } from './VerifyEmailRoute';
|