realtimex-crm 0.2.0 → 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/bin/create-realtimex-crm.js +7 -2
- package/dist/assets/{DealList-D2733gLg.js → DealList-C_0DbLKA.js} +2 -2
- package/dist/assets/{DealList-D2733gLg.js.map → DealList-C_0DbLKA.js.map} +1 -1
- package/dist/assets/index-C0zU8xwx.css +1 -0
- package/dist/assets/{index-NL4AEbnn.js → index-CdC59W53.js} +62 -61
- package/dist/assets/{index-NL4AEbnn.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/dashboard/Welcome.tsx +2 -1
- package/src/components/atomic-crm/layout/Header.tsx +16 -2
- package/src/components/atomic-crm/root/CRM.tsx +13 -8
- package/src/components/atomic-crm/root/DatabaseHealthCheck.tsx +68 -0
- package/src/components/atomic-crm/settings/DatabasePage.tsx +11 -0
- package/src/components/atomic-crm/settings/DatabaseSettings.tsx +3 -11
- package/src/components/atomic-crm/settings/SettingsPage.tsx +0 -3
- package/src/components/atomic-crm/setup/DatabaseSetupGuide.tsx +247 -0
- package/src/lib/database-health-check.ts +60 -0
- package/dist/assets/index-DY3zstf2.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 ? (
|
|
@@ -13,7 +13,8 @@ export const Welcome = () => (
|
|
|
13
13
|
>
|
|
14
14
|
RealTimeX CRM
|
|
15
15
|
</a>{" "}
|
|
16
|
-
is a full-featured CRM designed to help you manage contacts, deals, and
|
|
16
|
+
is a full-featured CRM designed to help you manage contacts, deals, and
|
|
17
|
+
tasks.
|
|
17
18
|
</p>
|
|
18
19
|
<p className="text-sm mb-4">
|
|
19
20
|
This demo runs on a mock API, so you can explore and modify the data. It
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
|
2
|
-
import {
|
|
2
|
+
import { Database, User } from "lucide-react";
|
|
3
3
|
import { CanAccess } from "ra-core";
|
|
4
4
|
import { Link, matchPath, useLocation } from "react-router";
|
|
5
5
|
import { RefreshButton } from "@/components/admin/refresh-button";
|
|
@@ -76,6 +76,7 @@ const Header = () => {
|
|
|
76
76
|
<RefreshButton />
|
|
77
77
|
<UserMenu>
|
|
78
78
|
<ConfigurationMenu />
|
|
79
|
+
<DatabaseMenu />
|
|
79
80
|
<CanAccess resource="sales" action="list">
|
|
80
81
|
<UsersMenu />
|
|
81
82
|
</CanAccess>
|
|
@@ -125,10 +126,23 @@ const ConfigurationMenu = () => {
|
|
|
125
126
|
return (
|
|
126
127
|
<DropdownMenuItem asChild onClick={onClose}>
|
|
127
128
|
<Link to="/settings" className="flex items-center gap-2">
|
|
128
|
-
<
|
|
129
|
+
<User />
|
|
129
130
|
My info
|
|
130
131
|
</Link>
|
|
131
132
|
</DropdownMenuItem>
|
|
132
133
|
);
|
|
133
134
|
};
|
|
135
|
+
|
|
136
|
+
const DatabaseMenu = () => {
|
|
137
|
+
const { onClose } = useUserMenu() ?? {};
|
|
138
|
+
return (
|
|
139
|
+
<DropdownMenuItem asChild onClick={onClose}>
|
|
140
|
+
<Link to="/database" className="flex items-center gap-2">
|
|
141
|
+
<Database />
|
|
142
|
+
Database
|
|
143
|
+
</Link>
|
|
144
|
+
</DropdownMenuItem>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
134
148
|
export default Header;
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
dataProvider as defaultDataProvider,
|
|
23
23
|
} from "../providers/supabase";
|
|
24
24
|
import sales from "../sales";
|
|
25
|
+
import { DatabasePage } from "../settings/DatabasePage";
|
|
25
26
|
import { SettingsPage } from "../settings/SettingsPage";
|
|
26
27
|
import type { ConfigurationContextValue } from "./ConfigurationContext";
|
|
27
28
|
import { ConfigurationProvider } from "./ConfigurationContext";
|
|
@@ -39,6 +40,7 @@ import {
|
|
|
39
40
|
} from "./defaultConfiguration";
|
|
40
41
|
import { i18nProvider } from "./i18nProvider";
|
|
41
42
|
import { StartPage } from "../login/StartPage.tsx";
|
|
43
|
+
import { DatabaseHealthCheck } from "./DatabaseHealthCheck";
|
|
42
44
|
|
|
43
45
|
export type CRMProps = {
|
|
44
46
|
dataProvider?: DataProvider;
|
|
@@ -152,15 +154,18 @@ export const CRM = ({
|
|
|
152
154
|
|
|
153
155
|
<CustomRoutes>
|
|
154
156
|
<Route path={SettingsPage.path} element={<SettingsPage />} />
|
|
157
|
+
<Route path={DatabasePage.path} element={<DatabasePage />} />
|
|
155
158
|
</CustomRoutes>
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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>
|
|
164
169
|
</Admin>
|
|
165
170
|
</ConfigurationProvider>
|
|
166
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
|
+
}
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
CardTitle,
|
|
8
8
|
} from "@/components/ui/card";
|
|
9
9
|
import { Button } from "@/components/ui/button";
|
|
10
|
-
import { Badge } from "@/components/ui/badge";
|
|
11
10
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
12
11
|
import { Database, CheckCircle, XCircle, Settings, Trash2 } from "lucide-react";
|
|
13
12
|
import {
|
|
@@ -37,16 +36,9 @@ export function DatabaseSettings() {
|
|
|
37
36
|
<>
|
|
38
37
|
<Card>
|
|
39
38
|
<CardHeader>
|
|
40
|
-
<div className="flex items-center
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
<CardTitle>Database Connection</CardTitle>
|
|
44
|
-
</div>
|
|
45
|
-
{config && (
|
|
46
|
-
<Badge variant={source === "ui" ? "default" : "secondary"}>
|
|
47
|
-
{source === "ui" ? "UI Configured" : "Environment Variables"}
|
|
48
|
-
</Badge>
|
|
49
|
-
)}
|
|
39
|
+
<div className="flex items-center gap-2">
|
|
40
|
+
<Database className="h-5 w-5" />
|
|
41
|
+
<CardTitle>Database Connection</CardTitle>
|
|
50
42
|
</div>
|
|
51
43
|
<CardDescription>
|
|
52
44
|
Manage your Supabase database connection settings
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
import ImageEditorField from "../misc/ImageEditorField";
|
|
25
25
|
import type { CrmDataProvider } from "../providers/types";
|
|
26
26
|
import type { Sale, SalesFormData } from "../types";
|
|
27
|
-
import { DatabaseSettings } from "./DatabaseSettings";
|
|
28
27
|
|
|
29
28
|
export const SettingsPage = () => {
|
|
30
29
|
const [isEditMode, setEditMode] = useState(false);
|
|
@@ -130,8 +129,6 @@ const SettingsForm = ({
|
|
|
130
129
|
|
|
131
130
|
return (
|
|
132
131
|
<div className="space-y-4">
|
|
133
|
-
<DatabaseSettings />
|
|
134
|
-
|
|
135
132
|
<Card>
|
|
136
133
|
<CardContent>
|
|
137
134
|
<div className="mb-4 flex flex-row justify-between">
|
|
@@ -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
|
+
}
|