realtimex-crm 0.2.1 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "realtimex-crm",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "RealTimeX CRM - A full-featured CRM built with React, shadcn-admin-kit, and Supabase. Fork of Atomic CRM with RealTimeX App SDK integration.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,7 +36,7 @@ export const DashboardStepper = ({
36
36
  <div className="flex flex-col gap-12">
37
37
  <div className="flex gap-8 items-center">
38
38
  <CheckCircle className="text-green-600 w-5 h-5" />
39
- <h4 className="font-bold">Install Atomic CRM</h4>
39
+ <h4 className="font-bold">Install RealTimeX CRM</h4>
40
40
  </div>
41
41
  <div className="flex gap-8 items-start">
42
42
  {step > 1 ? (
@@ -40,6 +40,7 @@ import {
40
40
  } from "./defaultConfiguration";
41
41
  import { i18nProvider } from "./i18nProvider";
42
42
  import { StartPage } from "../login/StartPage.tsx";
43
+ import { DatabaseHealthCheck } from "./DatabaseHealthCheck";
43
44
 
44
45
  export type CRMProps = {
45
46
  dataProvider?: DataProvider;
@@ -155,14 +156,16 @@ export const CRM = ({
155
156
  <Route path={SettingsPage.path} element={<SettingsPage />} />
156
157
  <Route path={DatabasePage.path} element={<DatabasePage />} />
157
158
  </CustomRoutes>
158
- <Resource name="deals" {...deals} />
159
- <Resource name="contacts" {...contacts} />
160
- <Resource name="companies" {...companies} />
161
- <Resource name="contactNotes" />
162
- <Resource name="dealNotes" />
163
- <Resource name="tasks" />
164
- <Resource name="sales" {...sales} />
165
- <Resource name="tags" />
159
+ <DatabaseHealthCheck>
160
+ <Resource name="deals" {...deals} />
161
+ <Resource name="contacts" {...contacts} />
162
+ <Resource name="companies" {...companies} />
163
+ <Resource name="contactNotes" />
164
+ <Resource name="dealNotes" />
165
+ <Resource name="tasks" />
166
+ <Resource name="sales" {...sales} />
167
+ <Resource name="tags" />
168
+ </DatabaseHealthCheck>
166
169
  </Admin>
167
170
  </ConfigurationProvider>
168
171
  );
@@ -0,0 +1,68 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useDataProvider } from "ra-core";
3
+ import { checkDatabaseHealth, DatabaseHealthStatus } from "@/lib/database-health-check";
4
+ import { getSupabaseConfig } from "@/lib/supabase-config";
5
+ import { DatabaseSetupGuide } from "../setup/DatabaseSetupGuide";
6
+
7
+ interface DatabaseHealthCheckProps {
8
+ children: React.ReactNode;
9
+ }
10
+
11
+ export function DatabaseHealthCheck({ children }: DatabaseHealthCheckProps) {
12
+ const dataProvider = useDataProvider();
13
+ const [healthStatus, setHealthStatus] = useState<DatabaseHealthStatus | null>(null);
14
+ const [isChecking, setIsChecking] = useState(true);
15
+
16
+ useEffect(() => {
17
+ let cancelled = false;
18
+
19
+ async function checkHealth() {
20
+ try {
21
+ const status = await checkDatabaseHealth(dataProvider);
22
+ if (!cancelled) {
23
+ setHealthStatus(status);
24
+ setIsChecking(false);
25
+ }
26
+ } catch (error) {
27
+ console.error("Failed to check database health:", error);
28
+ if (!cancelled) {
29
+ setIsChecking(false);
30
+ }
31
+ }
32
+ }
33
+
34
+ checkHealth();
35
+
36
+ return () => {
37
+ cancelled = true;
38
+ };
39
+ }, [dataProvider]);
40
+
41
+ // Show loading state
42
+ if (isChecking) {
43
+ return (
44
+ <div className="flex items-center justify-center min-h-screen">
45
+ <div className="text-center space-y-4">
46
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto"></div>
47
+ <p className="text-muted-foreground">Checking database connection...</p>
48
+ </div>
49
+ </div>
50
+ );
51
+ }
52
+
53
+ // Show setup guide if database is not healthy
54
+ if (healthStatus && !healthStatus.isHealthy) {
55
+ const config = getSupabaseConfig();
56
+ if (config) {
57
+ return (
58
+ <DatabaseSetupGuide
59
+ missingTables={healthStatus.missingTables}
60
+ supabaseUrl={config.url}
61
+ />
62
+ );
63
+ }
64
+ }
65
+
66
+ // Database is healthy, render children
67
+ return <>{children}</>;
68
+ }
@@ -0,0 +1,247 @@
1
+ import {
2
+ Alert,
3
+ AlertDescription,
4
+ AlertTitle,
5
+ } from "@/components/ui/alert";
6
+ import { Button } from "@/components/ui/button";
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardHeader,
12
+ CardTitle,
13
+ } from "@/components/ui/card";
14
+ import { AlertCircle, CheckCircle, Database, ExternalLink } from "lucide-react";
15
+
16
+ interface DatabaseSetupGuideProps {
17
+ missingTables: string[];
18
+ supabaseUrl: string;
19
+ }
20
+
21
+ export function DatabaseSetupGuide({
22
+ missingTables,
23
+ supabaseUrl,
24
+ }: DatabaseSetupGuideProps) {
25
+ const projectRef = supabaseUrl.match(/https:\/\/([^.]+)\.supabase\.co/)?.[1];
26
+
27
+ return (
28
+ <div className="max-w-2xl mx-auto mt-8 space-y-4">
29
+ <Alert variant="destructive">
30
+ <AlertCircle className="h-4 w-4" />
31
+ <AlertTitle>Database Schema Not Configured</AlertTitle>
32
+ <AlertDescription>
33
+ Your Supabase database is connected, but the CRM schema hasn't been
34
+ set up yet.
35
+ </AlertDescription>
36
+ </Alert>
37
+
38
+ <Card>
39
+ <CardHeader>
40
+ <div className="flex items-center gap-2">
41
+ <Database className="h-5 w-5" />
42
+ <CardTitle>Setup Instructions</CardTitle>
43
+ </div>
44
+ <CardDescription>
45
+ Follow these steps to set up your database schema
46
+ </CardDescription>
47
+ </CardHeader>
48
+ <CardContent className="space-y-6">
49
+ {/* Missing Tables */}
50
+ <div>
51
+ <h3 className="font-semibold mb-2">Missing Tables:</h3>
52
+ <div className="flex flex-wrap gap-2">
53
+ {missingTables.map((table) => (
54
+ <span
55
+ key={table}
56
+ className="px-2 py-1 bg-destructive/10 text-destructive text-sm rounded"
57
+ >
58
+ {table}
59
+ </span>
60
+ ))}
61
+ </div>
62
+ </div>
63
+
64
+ {/* Option 1: Supabase CLI */}
65
+ <div className="space-y-3">
66
+ <div className="flex items-center gap-2">
67
+ <div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-primary-foreground text-sm font-bold">
68
+ 1
69
+ </div>
70
+ <h3 className="font-semibold">Using Supabase CLI (Recommended)</h3>
71
+ </div>
72
+ <div className="ml-8 space-y-3">
73
+ <div>
74
+ <p className="text-sm text-muted-foreground mb-2">
75
+ Install Supabase CLI:
76
+ </p>
77
+ <pre className="bg-muted p-3 rounded text-sm overflow-x-auto">
78
+ npm install -g supabase
79
+ </pre>
80
+ </div>
81
+
82
+ <div>
83
+ <p className="text-sm text-muted-foreground mb-2">
84
+ Clone the RealTimeX CRM repository to get migrations:
85
+ </p>
86
+ <pre className="bg-muted p-3 rounded text-sm overflow-x-auto">
87
+ git clone https://github.com/therealtimex/realtimex-crm.git
88
+ {"\n"}cd realtimex-crm
89
+ </pre>
90
+ </div>
91
+
92
+ <div>
93
+ <p className="text-sm text-muted-foreground mb-2">
94
+ Link to your Supabase project:
95
+ </p>
96
+ <pre className="bg-muted p-3 rounded text-sm overflow-x-auto">
97
+ supabase link --project-ref {projectRef || "YOUR_PROJECT_REF"}
98
+ </pre>
99
+ </div>
100
+
101
+ <div>
102
+ <p className="text-sm text-muted-foreground mb-2">
103
+ Push the migrations:
104
+ </p>
105
+ <pre className="bg-muted p-3 rounded text-sm overflow-x-auto">
106
+ supabase db push
107
+ </pre>
108
+ </div>
109
+
110
+ <div>
111
+ <p className="text-sm text-muted-foreground mb-2">
112
+ Reload this page after migrations are complete.
113
+ </p>
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ {/* Option 2: SQL Editor */}
119
+ <div className="space-y-3">
120
+ <div className="flex items-center gap-2">
121
+ <div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-primary-foreground text-sm font-bold">
122
+ 2
123
+ </div>
124
+ <h3 className="font-semibold">Using Supabase SQL Editor</h3>
125
+ </div>
126
+ <div className="ml-8 space-y-3">
127
+ <p className="text-sm text-muted-foreground">
128
+ Manually run the SQL migrations in your Supabase SQL Editor:
129
+ </p>
130
+
131
+ <ol className="list-decimal list-inside space-y-2 text-sm text-muted-foreground">
132
+ <li>
133
+ Download migrations from{" "}
134
+ <a
135
+ href="https://github.com/therealtimex/realtimex-crm/tree/main/supabase/migrations"
136
+ target="_blank"
137
+ rel="noopener noreferrer"
138
+ className="text-primary hover:underline inline-flex items-center gap-1"
139
+ >
140
+ GitHub
141
+ <ExternalLink className="h-3 w-3" />
142
+ </a>
143
+ </li>
144
+ <li>
145
+ Open your{" "}
146
+ <a
147
+ href={`https://supabase.com/dashboard/project/${projectRef}/sql/new`}
148
+ target="_blank"
149
+ rel="noopener noreferrer"
150
+ className="text-primary hover:underline inline-flex items-center gap-1"
151
+ >
152
+ Supabase SQL Editor
153
+ <ExternalLink className="h-3 w-3" />
154
+ </a>
155
+ </li>
156
+ <li>
157
+ Run each migration file in order (by date in filename)
158
+ </li>
159
+ <li>
160
+ Reload this page after all migrations are complete
161
+ </li>
162
+ </ol>
163
+
164
+ {projectRef && (
165
+ <Button asChild variant="outline" className="w-full">
166
+ <a
167
+ href={`https://supabase.com/dashboard/project/${projectRef}/sql/new`}
168
+ target="_blank"
169
+ rel="noopener noreferrer"
170
+ >
171
+ <ExternalLink className="h-4 w-4 mr-2" />
172
+ Open SQL Editor
173
+ </a>
174
+ </Button>
175
+ )}
176
+ </div>
177
+ </div>
178
+
179
+ {/* Verification */}
180
+ <div className="space-y-3 pt-4 border-t">
181
+ <h3 className="font-semibold">After Setup</h3>
182
+ <div className="space-y-2 text-sm text-muted-foreground">
183
+ <div className="flex items-start gap-2">
184
+ <CheckCircle className="h-4 w-4 mt-0.5 text-green-500" />
185
+ <span>
186
+ All tables will be created with proper schemas and
187
+ relationships
188
+ </span>
189
+ </div>
190
+ <div className="flex items-start gap-2">
191
+ <CheckCircle className="h-4 w-4 mt-0.5 text-green-500" />
192
+ <span>Row Level Security (RLS) policies will be configured</span>
193
+ </div>
194
+ <div className="flex items-start gap-2">
195
+ <CheckCircle className="h-4 w-4 mt-0.5 text-green-500" />
196
+ <span>Database triggers and functions will be set up</span>
197
+ </div>
198
+ </div>
199
+
200
+ <Button
201
+ onClick={() => window.location.reload()}
202
+ className="w-full mt-4"
203
+ >
204
+ Reload Page
205
+ </Button>
206
+ </div>
207
+ </CardContent>
208
+ </Card>
209
+
210
+ {/* Additional Resources */}
211
+ <Card>
212
+ <CardHeader>
213
+ <CardTitle className="text-base">Need Help?</CardTitle>
214
+ </CardHeader>
215
+ <CardContent className="space-y-2 text-sm">
216
+ <a
217
+ href="https://github.com/therealtimex/realtimex-crm#installation"
218
+ target="_blank"
219
+ rel="noopener noreferrer"
220
+ className="flex items-center gap-2 text-primary hover:underline"
221
+ >
222
+ <ExternalLink className="h-4 w-4" />
223
+ View Full Setup Guide
224
+ </a>
225
+ <a
226
+ href="https://github.com/therealtimex/realtimex-crm/issues"
227
+ target="_blank"
228
+ rel="noopener noreferrer"
229
+ className="flex items-center gap-2 text-primary hover:underline"
230
+ >
231
+ <ExternalLink className="h-4 w-4" />
232
+ Report an Issue
233
+ </a>
234
+ <a
235
+ href="https://supabase.com/docs/guides/cli"
236
+ target="_blank"
237
+ rel="noopener noreferrer"
238
+ className="flex items-center gap-2 text-primary hover:underline"
239
+ >
240
+ <ExternalLink className="h-4 w-4" />
241
+ Supabase CLI Documentation
242
+ </a>
243
+ </CardContent>
244
+ </Card>
245
+ </div>
246
+ );
247
+ }
@@ -0,0 +1,60 @@
1
+ import { DataProvider } from "ra-core";
2
+
3
+ export interface DatabaseHealthStatus {
4
+ isHealthy: boolean;
5
+ missingTables: string[];
6
+ error?: string;
7
+ }
8
+
9
+ /**
10
+ * Check if the database has the required schema
11
+ */
12
+ export async function checkDatabaseHealth(
13
+ dataProvider: DataProvider,
14
+ ): Promise<DatabaseHealthStatus> {
15
+ const requiredTables = [
16
+ "contacts",
17
+ "companies",
18
+ "deals",
19
+ "contactNotes",
20
+ "dealNotes",
21
+ "tasks",
22
+ "sales",
23
+ "tags",
24
+ ];
25
+
26
+ const missingTables: string[] = [];
27
+
28
+ try {
29
+ // Try to query each table
30
+ for (const table of requiredTables) {
31
+ try {
32
+ await dataProvider.getList(table, {
33
+ pagination: { page: 1, perPage: 1 },
34
+ sort: { field: "id", order: "ASC" },
35
+ filter: {},
36
+ });
37
+ } catch (error: any) {
38
+ // Check if it's a "table not found" error
39
+ if (
40
+ error?.message?.includes("Could not find the table") ||
41
+ error?.message?.includes("relation") ||
42
+ error?.message?.includes("does not exist")
43
+ ) {
44
+ missingTables.push(table);
45
+ }
46
+ }
47
+ }
48
+
49
+ return {
50
+ isHealthy: missingTables.length === 0,
51
+ missingTables,
52
+ };
53
+ } catch (error: any) {
54
+ return {
55
+ isHealthy: false,
56
+ missingTables: requiredTables,
57
+ error: error?.message || "Unknown error",
58
+ };
59
+ }
60
+ }