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/dist/assets/{DealList-BE2GAdk9.js → DealList-C_0DbLKA.js} +2 -2
- package/dist/assets/{DealList-BE2GAdk9.js.map → DealList-C_0DbLKA.js.map} +1 -1
- package/dist/assets/index-C0zU8xwx.css +1 -0
- package/dist/assets/{index-B4PspXuO.js → index-CdC59W53.js} +62 -61
- package/dist/assets/{index-B4PspXuO.js.map → index-CdC59W53.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist/stats.html +1 -1
- package/package.json +1 -1
- package/src/components/atomic-crm/dashboard/DashboardStepper.tsx +1 -1
- package/src/components/atomic-crm/root/CRM.tsx +11 -8
- package/src/components/atomic-crm/root/DatabaseHealthCheck.tsx +68 -0
- package/src/components/atomic-crm/setup/DatabaseSetupGuide.tsx +247 -0
- package/src/lib/database-health-check.ts +60 -0
- package/dist/assets/index-DmJ6Q1Y7.css +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtimex-crm",
|
|
3
|
-
"version": "0.
|
|
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
|
|
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
|
-
<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
}
|