create-chaaskit 0.1.1 → 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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/templates/app/routes/chat.admin.promo-codes.tsx +10 -0
  3. package/dist/templates/app/routes/chat.admin.waitlist.tsx +10 -0
  4. package/dist/templates/app/routes.ts +2 -0
  5. package/dist/templates/config/app.config.ts +24 -0
  6. package/dist/templates/server.js +40 -10
  7. package/package.json +19 -10
  8. package/dist/cli.d.ts +0 -3
  9. package/dist/cli.d.ts.map +0 -1
  10. package/dist/cli.js +0 -25
  11. package/dist/cli.js.map +0 -1
  12. package/dist/templates/app/components/AcceptInviteClient.tsx +0 -10
  13. package/dist/templates/app/components/AdminDashboardClient.tsx +0 -10
  14. package/dist/templates/app/components/AdminTeamClient.tsx +0 -10
  15. package/dist/templates/app/components/AdminTeamsClient.tsx +0 -10
  16. package/dist/templates/app/components/AdminUsersClient.tsx +0 -10
  17. package/dist/templates/app/components/ApiKeysClient.tsx +0 -10
  18. package/dist/templates/app/components/AutomationsClient.tsx +0 -10
  19. package/dist/templates/app/components/ChatClient.tsx +0 -13
  20. package/dist/templates/app/components/DocumentsClient.tsx +0 -10
  21. package/dist/templates/app/components/OAuthConsentClient.tsx +0 -10
  22. package/dist/templates/app/components/PricingClient.tsx +0 -10
  23. package/dist/templates/app/components/TeamSettingsClient.tsx +0 -10
  24. package/dist/templates/app/components/VerifyEmailClient.tsx +0 -10
  25. package/dist/templates/app/routes/admin._index.tsx +0 -57
  26. package/dist/templates/app/routes/admin.teams.$teamId.tsx +0 -57
  27. package/dist/templates/app/routes/admin.teams._index.tsx +0 -57
  28. package/dist/templates/app/routes/admin.users.tsx +0 -57
  29. package/dist/templates/app/routes/api-keys.tsx +0 -57
  30. package/dist/templates/app/routes/automations.tsx +0 -57
  31. package/dist/templates/app/routes/documents.tsx +0 -57
  32. package/dist/templates/app/routes/team.$teamId.settings.tsx +0 -57
  33. package/dist/templates/app/routes/thread.$threadId.tsx +0 -102
  34. package/dist/templates/docs/admin.md +0 -257
  35. package/dist/templates/docs/api-keys.md +0 -403
  36. package/dist/templates/docs/authentication.md +0 -247
  37. package/dist/templates/docs/configuration.md +0 -1212
  38. package/dist/templates/docs/custom-pages.md +0 -466
  39. package/dist/templates/docs/deployment.md +0 -362
  40. package/dist/templates/docs/development.md +0 -411
  41. package/dist/templates/docs/documents.md +0 -293
  42. package/dist/templates/docs/extensions.md +0 -639
  43. package/dist/templates/docs/index.md +0 -139
  44. package/dist/templates/docs/installation.md +0 -286
  45. package/dist/templates/docs/mcp.md +0 -952
  46. package/dist/templates/docs/native-tools.md +0 -688
  47. package/dist/templates/docs/queue.md +0 -514
  48. package/dist/templates/docs/scheduled-prompts.md +0 -279
  49. package/dist/templates/docs/settings.md +0 -415
  50. package/dist/templates/docs/slack.md +0 -318
  51. package/dist/templates/docs/styling.md +0 -288
  52. package/dist/templates/index.html +0 -16
  53. package/dist/templates/prisma/schema.prisma +0 -271
  54. package/dist/templates/src/main.tsx +0 -8
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Matt Ferrante
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ import { createRoute } from '@chaaskit/client/ssr-utils';
2
+
3
+ const route = createRoute({
4
+ title: 'Admin Promo Codes',
5
+ load: () => import('@chaaskit/client/routes/AdminPromoCodesRoute'),
6
+ });
7
+
8
+ export const meta = route.meta;
9
+ export const links = route.links;
10
+ export default route.default;
@@ -0,0 +1,10 @@
1
+ import { createRoute } from '@chaaskit/client/ssr-utils';
2
+
3
+ const route = createRoute({
4
+ title: 'Admin Waitlist',
5
+ load: () => import('@chaaskit/client/routes/AdminWaitlistRoute'),
6
+ });
7
+
8
+ export const meta = route.meta;
9
+ export const links = route.links;
10
+ export default route.default;
@@ -41,6 +41,8 @@ export default [
41
41
  // Admin pages
42
42
  route(`${base}/admin`, 'routes/chat.admin._index.tsx'),
43
43
  route(`${base}/admin/users`, 'routes/chat.admin.users.tsx'),
44
+ route(`${base}/admin/waitlist`, 'routes/chat.admin.waitlist.tsx'),
45
+ route(`${base}/admin/promo-codes`, 'routes/chat.admin.promo-codes.tsx'),
44
46
  route(`${base}/admin/teams`, 'routes/chat.admin.teams._index.tsx'),
45
47
  route(`${base}/admin/teams/:teamId`, 'routes/chat.admin.teams.$teamId.tsx'),
46
48
  ]),
@@ -29,6 +29,11 @@ export const config: AppConfig = {
29
29
  enabled: true,
30
30
  expiresInMinutes: 15,
31
31
  },
32
+ gating: {
33
+ mode: 'open',
34
+ inviteExpiryDays: 7,
35
+ waitlistEnabled: true,
36
+ },
32
37
  },
33
38
 
34
39
  // Configure your AI agent(s) here
@@ -138,4 +143,23 @@ export const config: AppConfig = {
138
143
  'application/json',
139
144
  ],
140
145
  },
146
+
147
+ credits: {
148
+ enabled: true,
149
+ expiryEnabled: true,
150
+ defaultExpiryDays: 365,
151
+ tokensPerCredit: 1000,
152
+ referralRewardCredits: 10,
153
+ referralTriggers: {
154
+ signup: true,
155
+ firstMessage: true,
156
+ paying: true,
157
+ },
158
+ promoEnabled: true,
159
+ },
160
+
161
+ metering: {
162
+ enabled: true,
163
+ recordPromptCompletion: true,
164
+ },
141
165
  };
@@ -4,21 +4,43 @@
4
4
  * This is a single server that handles:
5
5
  * - API routes at /api/*, /mcp/*, /v1/*
6
6
  * - SSR for all other routes via React Router
7
+ *
8
+ * ARCHITECTURE: We create a wrapper Express app that handles React Router routes
9
+ * BEFORE body parsing, then delegates API routes to the chaaskit app (which has
10
+ * body parsing). This prevents the body stream from being consumed before React
11
+ * Router can read it with request.formData().
12
+ *
13
+ * SECRETS: Configure secret loading via environment variables:
14
+ * SECRETS_PROVIDER=env (default) - secrets already in env vars
15
+ * SECRETS_PROVIDER=aws-secrets-manager - load from AWS Secrets Manager
16
+ * AWS_SECRET_ARN - ARN of the secret to load
17
+ * AWS_REGION - Region (optional, defaults to 'us-west-2')
7
18
  */
8
- import { createApp } from '@chaaskit/server';
9
- import { createRequestHandler } from '@react-router/express';
10
- import express from 'express';
11
- import path from 'path';
12
- import { fileURLToPath } from 'url';
19
+
20
+ // Load secrets BEFORE importing anything that uses them (Prisma, etc.)
21
+ const { loadSecrets } = await import('@chaaskit/server');
22
+ await loadSecrets();
23
+
24
+ // Now safe to import modules that depend on DATABASE_URL, OPENAI_API_KEY, etc.
25
+ const { createApp } = await import('@chaaskit/server');
26
+ const { createRequestHandler } = await import('@react-router/express');
27
+ const express = (await import('express')).default;
28
+ const path = (await import('path')).default;
29
+ const { fileURLToPath } = await import('url');
13
30
 
14
31
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
32
 
16
33
  async function startServer() {
17
- // Create the chaaskit Express app with all API routes
18
- // serveSpa: false disables the built-in SPA fallback since React Router handles SSR
19
- const app = await createApp({ serveSpa: false });
34
+ // Create the chaaskit Express app with all API routes (has body parsing)
35
+ const chaaskitApp = await createApp({ serveSpa: false });
20
36
 
21
- // Serve static assets from the React Router build
37
+ // Create wrapper Express app (NO body parsing here)
38
+ const app = express();
39
+
40
+ // Trust proxy headers when behind load balancer (ALB, nginx, CloudFlare, etc.)
41
+ app.set('trust proxy', true);
42
+
43
+ // Serve static assets FIRST (before any other processing)
22
44
  app.use(
23
45
  '/assets',
24
46
  express.static(path.join(__dirname, 'build/client/assets'), {
@@ -28,8 +50,16 @@ async function startServer() {
28
50
  );
29
51
  app.use(express.static(path.join(__dirname, 'build/client'), { maxAge: '1h' }));
30
52
 
53
+ // Delegate API routes to chaaskit app (which has body parsing)
54
+ app.use((req, res, next) => {
55
+ if (req.path.startsWith('/api/') || req.path.startsWith('/v1/') || req.path.startsWith('/mcp/')) {
56
+ return chaaskitApp(req, res, next);
57
+ }
58
+ next();
59
+ });
60
+
31
61
  // Handle all other routes with React Router SSR
32
- // This must come after API routes (which are already registered in createApp)
62
+ // No body parsing has occurred, so request.formData() works
33
63
  app.all(
34
64
  '*',
35
65
  createRequestHandler({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-chaaskit",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "CLI to create and manage ChaasKit AI chat SaaS applications",
5
5
  "license": "MIT",
6
6
  "author": "Matt Ferrante <@ferrants>",
@@ -13,7 +13,16 @@
13
13
  "bugs": {
14
14
  "url": "https://github.com/ferrants/chaaskit/issues"
15
15
  },
16
- "keywords": ["chaaskit", "ai", "chat", "saas", "cli", "create", "scaffold", "template"],
16
+ "keywords": [
17
+ "chaaskit",
18
+ "ai",
19
+ "chat",
20
+ "saas",
21
+ "cli",
22
+ "create",
23
+ "scaffold",
24
+ "template"
25
+ ],
17
26
  "type": "module",
18
27
  "main": "./dist/index.js",
19
28
  "bin": {
@@ -23,13 +32,6 @@
23
32
  "dist",
24
33
  "templates"
25
34
  ],
26
- "scripts": {
27
- "build": "tsc && npm run copy-templates",
28
- "copy-templates": "cp -r src/templates dist/",
29
- "dev": "tsc --watch",
30
- "typecheck": "tsc --noEmit",
31
- "clean": "rm -rf dist"
32
- },
33
35
  "dependencies": {
34
36
  "commander": "^12.0.0",
35
37
  "fs-extra": "^11.2.0",
@@ -42,5 +44,12 @@
42
44
  "@types/node": "^20.11.0",
43
45
  "@types/prompts": "^2.4.9",
44
46
  "typescript": "^5.3.3"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc && npm run copy-templates",
50
+ "copy-templates": "cp -r src/templates dist/",
51
+ "dev": "tsc --watch",
52
+ "typecheck": "tsc --noEmit",
53
+ "clean": "rm -rf dist"
45
54
  }
46
- }
55
+ }
package/dist/cli.d.ts DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js DELETED
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import { dev } from './commands/dev.js';
4
- import { build } from './commands/build.js';
5
- const program = new Command();
6
- program
7
- .name('chat-saas')
8
- .description('CLI for managing Chat SaaS applications')
9
- .version('0.1.0');
10
- program
11
- .command('dev')
12
- .description('Start development servers')
13
- .option('-p, --port <port>', 'Server port', '3000')
14
- .option('--client-port <port>', 'Client dev server port', '5173')
15
- .action(async (options) => {
16
- await dev(options);
17
- });
18
- program
19
- .command('build')
20
- .description('Build for production')
21
- .action(async () => {
22
- await build();
23
- });
24
- program.parse();
25
- //# sourceMappingURL=cli.js.map
package/dist/cli.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,yCAAyC,CAAC;KACtD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,2BAA2B,CAAC;KACxC,MAAM,CAAC,mBAAmB,EAAE,aAAa,EAAE,MAAM,CAAC;KAClD,MAAM,CAAC,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,CAAC;KAChE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,sBAAsB,CAAC;KACnC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,KAAK,EAAE,CAAC;AAChB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -1,10 +0,0 @@
1
- // Client-only component for Accept Invite page
2
- import { ChatProviders, AcceptInvitePage } from '@chaaskit/client';
3
-
4
- export default function AcceptInviteClient() {
5
- return (
6
- <ChatProviders>
7
- <AcceptInvitePage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Admin Dashboard page
2
- import { ChatProviders, AdminDashboardPage } from '@chaaskit/client';
3
-
4
- export default function AdminDashboardClient() {
5
- return (
6
- <ChatProviders>
7
- <AdminDashboardPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Admin Team detail page
2
- import { ChatProviders, AdminTeamPage } from '@chaaskit/client';
3
-
4
- export default function AdminTeamClient() {
5
- return (
6
- <ChatProviders>
7
- <AdminTeamPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Admin Teams page
2
- import { ChatProviders, AdminTeamsPage } from '@chaaskit/client';
3
-
4
- export default function AdminTeamsClient() {
5
- return (
6
- <ChatProviders>
7
- <AdminTeamsPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Admin Users page
2
- import { ChatProviders, AdminUsersPage } from '@chaaskit/client';
3
-
4
- export default function AdminUsersClient() {
5
- return (
6
- <ChatProviders>
7
- <AdminUsersPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for API Keys page
2
- import { ChatProviders, ApiKeysPage } from '@chaaskit/client';
3
-
4
- export default function ApiKeysClient() {
5
- return (
6
- <ChatProviders>
7
- <ApiKeysPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Automations (Scheduled Prompts) page
2
- import { ChatProviders, ScheduledPromptsPage } from '@chaaskit/client';
3
-
4
- export default function AutomationsClient() {
5
- return (
6
- <ChatProviders>
7
- <ScheduledPromptsPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,13 +0,0 @@
1
- // This file is only imported on the client side via dynamic import
2
- // It uses @chaaskit/client components directly
3
- import { ChatProviders, MainLayout, ChatPage } from '@chaaskit/client';
4
-
5
- export default function ChatClient() {
6
- return (
7
- <ChatProviders>
8
- <MainLayout>
9
- <ChatPage />
10
- </MainLayout>
11
- </ChatProviders>
12
- );
13
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Documents page
2
- import { ChatProviders, DocumentsPage } from '@chaaskit/client';
3
-
4
- export default function DocumentsClient() {
5
- return (
6
- <ChatProviders>
7
- <DocumentsPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for OAuth Consent page
2
- import { ChatProviders, OAuthConsentPage } from '@chaaskit/client';
3
-
4
- export default function OAuthConsentClient() {
5
- return (
6
- <ChatProviders>
7
- <OAuthConsentPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Pricing page
2
- import { ChatProviders, PricingPage } from '@chaaskit/client';
3
-
4
- export default function PricingClient() {
5
- return (
6
- <ChatProviders>
7
- <PricingPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Team Settings page
2
- import { ChatProviders, TeamSettingsPage } from '@chaaskit/client';
3
-
4
- export default function TeamSettingsClient() {
5
- return (
6
- <ChatProviders>
7
- <TeamSettingsPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,10 +0,0 @@
1
- // Client-only component for Email Verification page
2
- import { ChatProviders, VerifyEmailPage } from '@chaaskit/client';
3
-
4
- export default function VerifyEmailClient() {
5
- return (
6
- <ChatProviders>
7
- <VerifyEmailPage />
8
- </ChatProviders>
9
- );
10
- }
@@ -1,57 +0,0 @@
1
- import type { Route } from './+types/admin._index';
2
- import { lazy, Suspense } from 'react';
3
- import { ClientOnly } from '../components/ClientOnly';
4
-
5
- const AdminDashboardClient = lazy(() => import('../components/AdminDashboardClient'));
6
-
7
- export function meta({}: Route.MetaArgs) {
8
- return [{ title: 'Admin Dashboard' }];
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 AdminDashboard() {
16
- return (
17
- <ClientOnly fallback={<LoadingSkeleton />}>
18
- {() => (
19
- <Suspense fallback={<LoadingSkeleton />}>
20
- <AdminDashboardClient />
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
- }
@@ -1,57 +0,0 @@
1
- import type { Route } from './+types/admin.teams.$teamId';
2
- import { lazy, Suspense } from 'react';
3
- import { ClientOnly } from '../components/ClientOnly';
4
-
5
- const AdminTeamClient = lazy(() => import('../components/AdminTeamClient'));
6
-
7
- export function meta({}: Route.MetaArgs) {
8
- return [{ title: 'Admin - Team Details' }];
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 AdminTeamDetails() {
16
- return (
17
- <ClientOnly fallback={<LoadingSkeleton />}>
18
- {() => (
19
- <Suspense fallback={<LoadingSkeleton />}>
20
- <AdminTeamClient />
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
- }
@@ -1,57 +0,0 @@
1
- import type { Route } from './+types/admin.teams._index';
2
- import { lazy, Suspense } from 'react';
3
- import { ClientOnly } from '../components/ClientOnly';
4
-
5
- const AdminTeamsClient = lazy(() => import('../components/AdminTeamsClient'));
6
-
7
- export function meta({}: Route.MetaArgs) {
8
- return [{ title: 'Admin - Teams' }];
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 AdminTeams() {
16
- return (
17
- <ClientOnly fallback={<LoadingSkeleton />}>
18
- {() => (
19
- <Suspense fallback={<LoadingSkeleton />}>
20
- <AdminTeamsClient />
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
- }
@@ -1,57 +0,0 @@
1
- import type { Route } from './+types/admin.users';
2
- import { lazy, Suspense } from 'react';
3
- import { ClientOnly } from '../components/ClientOnly';
4
-
5
- const AdminUsersClient = lazy(() => import('../components/AdminUsersClient'));
6
-
7
- export function meta({}: Route.MetaArgs) {
8
- return [{ title: 'Admin - Users' }];
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 AdminUsers() {
16
- return (
17
- <ClientOnly fallback={<LoadingSkeleton />}>
18
- {() => (
19
- <Suspense fallback={<LoadingSkeleton />}>
20
- <AdminUsersClient />
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
- }