nl-d365boilerplate-vite 1.0.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/.env.example +22 -0
- package/README.md +75 -0
- package/bin/cli.js +84 -0
- package/components.json +22 -0
- package/eslint.config.js +23 -0
- package/getToken.js +197 -0
- package/index.html +30 -0
- package/package.json +69 -0
- package/src/App.tsx +28 -0
- package/src/assets/images/novalogica-logo.svg +24 -0
- package/src/components/nl-header.tsx +165 -0
- package/src/components/page-layout.tsx +78 -0
- package/src/components/page-render.tsx +16 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/button.tsx +165 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/input.tsx +23 -0
- package/src/components/ui/label.tsx +23 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/theme-toggle.tsx +28 -0
- package/src/config/pages.config.ts +34 -0
- package/src/contexts/dataverse-context.tsx +12 -0
- package/src/contexts/navigation-context.tsx +14 -0
- package/src/hooks/useAccounts.ts +194 -0
- package/src/hooks/useDataverse.ts +11 -0
- package/src/hooks/useNavigation.ts +41 -0
- package/src/index.css +147 -0
- package/src/lib/nav-items.ts +25 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +12 -0
- package/src/pages/Demo.tsx +465 -0
- package/src/pages/Documentation.tsx +850 -0
- package/src/pages/Home.tsx +132 -0
- package/src/pages/index.ts +4 -0
- package/src/providers/dataverse-provider.tsx +81 -0
- package/src/providers/navigation-provider.tsx +33 -0
- package/src/providers/theme-provider.tsx +92 -0
- package/src/public/novalogica-logo.svg +24 -0
- package/tsconfig.app.json +32 -0
- package/tsconfig.json +17 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +26 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { useState, useCallback } from "react";
|
|
2
|
+
import { useDataverse } from "@/hooks/useDataverse";
|
|
3
|
+
|
|
4
|
+
export interface Account {
|
|
5
|
+
accountid?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
emailaddress1?: string;
|
|
8
|
+
telephone1?: string;
|
|
9
|
+
websiteurl?: string;
|
|
10
|
+
address1_city?: string;
|
|
11
|
+
createdon?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const useAccounts = () => {
|
|
15
|
+
const { api, isReady } = useDataverse();
|
|
16
|
+
const [accounts, setAccounts] = useState<Account[]>([]);
|
|
17
|
+
const [loading, setLoading] = useState(false);
|
|
18
|
+
const [error, setError] = useState<string | null>(null);
|
|
19
|
+
|
|
20
|
+
// Retrieve Multiple Accounts
|
|
21
|
+
const fetchAccounts = useCallback(async () => {
|
|
22
|
+
if (!api || !isReady) return;
|
|
23
|
+
|
|
24
|
+
setLoading(true);
|
|
25
|
+
setError(null);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const response = await api.retrieveMultiple({
|
|
29
|
+
collection: "accounts",
|
|
30
|
+
select: [
|
|
31
|
+
"accountid",
|
|
32
|
+
"name",
|
|
33
|
+
"emailaddress1",
|
|
34
|
+
"telephone1",
|
|
35
|
+
"websiteurl",
|
|
36
|
+
"address1_city",
|
|
37
|
+
"createdon",
|
|
38
|
+
],
|
|
39
|
+
filter: "statecode eq 0",
|
|
40
|
+
orderBy: ["name asc"],
|
|
41
|
+
maxPageSize: 100,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
setAccounts(response?.value || []);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
setError(err instanceof Error ? err.message : "Failed to fetch accounts");
|
|
47
|
+
} finally {
|
|
48
|
+
setLoading(false);
|
|
49
|
+
}
|
|
50
|
+
}, [api, isReady]);
|
|
51
|
+
|
|
52
|
+
// Retrieve Single Account
|
|
53
|
+
const getAccount = useCallback(
|
|
54
|
+
async (accountId: string): Promise<Account | null> => {
|
|
55
|
+
if (!api || !isReady || !accountId) return null;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
setError(null);
|
|
59
|
+
const response = await api.retrieve({
|
|
60
|
+
collection: "accounts",
|
|
61
|
+
key: accountId,
|
|
62
|
+
select: [
|
|
63
|
+
"accountid",
|
|
64
|
+
"name",
|
|
65
|
+
"emailaddress1",
|
|
66
|
+
"telephone1",
|
|
67
|
+
"websiteurl",
|
|
68
|
+
"address1_city",
|
|
69
|
+
"createdon",
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return response as Account;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
setError(
|
|
76
|
+
err instanceof Error ? err.message : "Failed to retrieve account",
|
|
77
|
+
);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[api, isReady],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Create Account
|
|
85
|
+
const createAccount = useCallback(
|
|
86
|
+
async (accountData: Partial<Account>): Promise<string | null> => {
|
|
87
|
+
if (!api || !isReady || !accountData.name) return null;
|
|
88
|
+
|
|
89
|
+
setLoading(true);
|
|
90
|
+
setError(null);
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const response = await api.create({
|
|
94
|
+
collection: "accounts",
|
|
95
|
+
data: accountData,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Refresh the accounts list
|
|
99
|
+
await fetchAccounts();
|
|
100
|
+
|
|
101
|
+
return response as string;
|
|
102
|
+
} catch (err) {
|
|
103
|
+
setError(
|
|
104
|
+
err instanceof Error ? err.message : "Failed to create account",
|
|
105
|
+
);
|
|
106
|
+
return null;
|
|
107
|
+
} finally {
|
|
108
|
+
setLoading(false);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
[api, isReady, fetchAccounts],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Update Account
|
|
115
|
+
const updateAccount = useCallback(
|
|
116
|
+
async (accountId: string, updates: Partial<Account>): Promise<boolean> => {
|
|
117
|
+
if (!api || !isReady || !accountId) return false;
|
|
118
|
+
|
|
119
|
+
setLoading(true);
|
|
120
|
+
setError(null);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await api.update({
|
|
124
|
+
collection: "accounts",
|
|
125
|
+
key: accountId,
|
|
126
|
+
data: updates,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Refresh the accounts list
|
|
130
|
+
await fetchAccounts();
|
|
131
|
+
|
|
132
|
+
return true;
|
|
133
|
+
} catch (err) {
|
|
134
|
+
setError(
|
|
135
|
+
err instanceof Error ? err.message : "Failed to update account",
|
|
136
|
+
);
|
|
137
|
+
return false;
|
|
138
|
+
} finally {
|
|
139
|
+
setLoading(false);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
[api, isReady, fetchAccounts],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Delete Account
|
|
146
|
+
const deleteAccount = useCallback(
|
|
147
|
+
async (accountId: string): Promise<boolean> => {
|
|
148
|
+
if (!api || !isReady || !accountId) return false;
|
|
149
|
+
|
|
150
|
+
setLoading(true);
|
|
151
|
+
setError(null);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await api.deleteRecord({
|
|
155
|
+
collection: "accounts",
|
|
156
|
+
key: accountId,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Refresh the accounts list
|
|
160
|
+
await fetchAccounts();
|
|
161
|
+
|
|
162
|
+
return true;
|
|
163
|
+
} catch (err) {
|
|
164
|
+
setError(
|
|
165
|
+
err instanceof Error ? err.message : "Failed to delete account",
|
|
166
|
+
);
|
|
167
|
+
return false;
|
|
168
|
+
} finally {
|
|
169
|
+
setLoading(false);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
[api, isReady, fetchAccounts],
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const clearError = useCallback(() => {
|
|
176
|
+
setError(null);
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
// State
|
|
181
|
+
accounts,
|
|
182
|
+
loading,
|
|
183
|
+
error,
|
|
184
|
+
isReady,
|
|
185
|
+
|
|
186
|
+
// Actions
|
|
187
|
+
fetchAccounts,
|
|
188
|
+
getAccount,
|
|
189
|
+
createAccount,
|
|
190
|
+
updateAccount,
|
|
191
|
+
deleteAccount,
|
|
192
|
+
clearError,
|
|
193
|
+
};
|
|
194
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
|
|
3
|
+
import { DataverseContext } from "@/contexts/dataverse-context";
|
|
4
|
+
|
|
5
|
+
export const useDataverse = () => {
|
|
6
|
+
const context = useContext(DataverseContext);
|
|
7
|
+
if (!context) {
|
|
8
|
+
throw new Error("useDataverse must be used within a DataverseProvider");
|
|
9
|
+
}
|
|
10
|
+
return context;
|
|
11
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useCallback, useContext } from "react";
|
|
2
|
+
|
|
3
|
+
import { type PageConfig, PAGES } from "@/config/pages.config";
|
|
4
|
+
import { NavigationContext } from "@/contexts/navigation-context";
|
|
5
|
+
|
|
6
|
+
export function useNavigation() {
|
|
7
|
+
const context = useContext(NavigationContext);
|
|
8
|
+
|
|
9
|
+
if (!context) {
|
|
10
|
+
throw new Error("useNavigation must be used within a NavigationProvider");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { currentPage, navigate: navigateToPage, canGoBack, goBack } = context;
|
|
14
|
+
|
|
15
|
+
const navigate = useCallback(
|
|
16
|
+
(pageId: string) => {
|
|
17
|
+
const page = PAGES[pageId];
|
|
18
|
+
if (page) {
|
|
19
|
+
navigateToPage(page);
|
|
20
|
+
} else {
|
|
21
|
+
console.error(`Page "${pageId}" not found in page configuration`);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
[navigateToPage],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const navigateTo = useCallback(
|
|
28
|
+
(page: PageConfig) => {
|
|
29
|
+
navigateToPage(page);
|
|
30
|
+
},
|
|
31
|
+
[navigateToPage],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
currentPage,
|
|
36
|
+
navigate,
|
|
37
|
+
navigateTo,
|
|
38
|
+
canGoBack,
|
|
39
|
+
goBack,
|
|
40
|
+
};
|
|
41
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
@theme inline {
|
|
7
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
8
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
9
|
+
--radius-lg: var(--radius);
|
|
10
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
11
|
+
--radius-2xl: calc(var(--radius) + 8px);
|
|
12
|
+
--radius-3xl: calc(var(--radius) + 12px);
|
|
13
|
+
--radius-4xl: calc(var(--radius) + 16px);
|
|
14
|
+
--color-background: var(--background);
|
|
15
|
+
--color-foreground: var(--foreground);
|
|
16
|
+
--color-card: var(--card);
|
|
17
|
+
--color-card-foreground: var(--card-foreground);
|
|
18
|
+
--color-popover: var(--popover);
|
|
19
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
20
|
+
--color-primary: var(--primary);
|
|
21
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
22
|
+
--color-secondary: var(--secondary);
|
|
23
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
24
|
+
--color-muted: var(--muted);
|
|
25
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
26
|
+
--color-accent: var(--accent);
|
|
27
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
28
|
+
--color-destructive: var(--destructive);
|
|
29
|
+
--color-border: var(--border);
|
|
30
|
+
--color-input: var(--input);
|
|
31
|
+
--color-ring: var(--ring);
|
|
32
|
+
--color-chart-1: var(--chart-1);
|
|
33
|
+
--color-chart-2: var(--chart-2);
|
|
34
|
+
--color-chart-3: var(--chart-3);
|
|
35
|
+
--color-chart-4: var(--chart-4);
|
|
36
|
+
--color-chart-5: var(--chart-5);
|
|
37
|
+
--color-sidebar: var(--sidebar);
|
|
38
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
39
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
40
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
41
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
42
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
43
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
44
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
45
|
+
--color-code-highlight: var(--code-highlight);
|
|
46
|
+
--color-code-border: var(--code-border);
|
|
47
|
+
--color-code-muted: var(--code-muted);
|
|
48
|
+
--color-code-foreground: var(--code-foreground);
|
|
49
|
+
--color-code-bg: var(--code-bg);
|
|
50
|
+
--color-success-foreground: var(--success-foreground);
|
|
51
|
+
--color-success: var(--success);
|
|
52
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
:root {
|
|
56
|
+
--radius: 0.8rem;
|
|
57
|
+
--background: oklch(0.985 0.002 265);
|
|
58
|
+
--foreground: oklch(0.145 0.025 265);
|
|
59
|
+
--card: oklch(1 0 0);
|
|
60
|
+
--card-foreground: oklch(0.145 0.025 265);
|
|
61
|
+
--popover: oklch(1 0 0);
|
|
62
|
+
--popover-foreground: oklch(0.145 0.025 265);
|
|
63
|
+
--primary: oklch(0.455 0.188 262);
|
|
64
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
65
|
+
--secondary: oklch(0.965 0.008 265);
|
|
66
|
+
--secondary-foreground: oklch(0.205 0.025 265);
|
|
67
|
+
--muted: oklch(0.965 0.008 265);
|
|
68
|
+
--muted-foreground: oklch(0.485 0.022 265);
|
|
69
|
+
--accent: oklch(0.965 0.008 265);
|
|
70
|
+
--accent-foreground: oklch(0.205 0.025 265);
|
|
71
|
+
--destructive: oklch(0.575 0.195 27);
|
|
72
|
+
--border: oklch(0.915 0.008 265);
|
|
73
|
+
--input: oklch(0.915 0.008 265);
|
|
74
|
+
--ring: oklch(0.455 0.188 262);
|
|
75
|
+
--chart-1: oklch(0.455 0.188 262);
|
|
76
|
+
--chart-2: oklch(0.585 0.155 200);
|
|
77
|
+
--chart-3: oklch(0.585 0.155 340);
|
|
78
|
+
--chart-4: oklch(0.625 0.175 145);
|
|
79
|
+
--chart-5: oklch(0.625 0.175 38);
|
|
80
|
+
--sidebar: oklch(0.975 0.005 265);
|
|
81
|
+
--sidebar-foreground: oklch(0.145 0.025 265);
|
|
82
|
+
--sidebar-primary: oklch(0.455 0.188 262);
|
|
83
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
84
|
+
--sidebar-accent: oklch(0.965 0.008 265);
|
|
85
|
+
--sidebar-accent-foreground: oklch(0.205 0.025 265);
|
|
86
|
+
--sidebar-border: oklch(0.915 0.008 265);
|
|
87
|
+
--sidebar-ring: oklch(0.455 0.188 262);
|
|
88
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
89
|
+
--success: oklch(0.625 0.175 145);
|
|
90
|
+
--success-foreground: oklch(0.985 0 0);
|
|
91
|
+
--code-bg: oklch(0.97 0 0);
|
|
92
|
+
--code-foreground: oklch(0.205 0.025 265);
|
|
93
|
+
--code-muted: oklch(0.552 0.016 285.938);
|
|
94
|
+
--code-border: oklch(0.92 0 0);
|
|
95
|
+
--code-highlight: oklch(0.95 0 0);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.dark {
|
|
99
|
+
--background: oklch(0.115 0.018 265);
|
|
100
|
+
--foreground: oklch(0.965 0.008 265);
|
|
101
|
+
--card: oklch(0.145 0.022 265);
|
|
102
|
+
--card-foreground: oklch(0.965 0.008 265);
|
|
103
|
+
--popover: oklch(0.145 0.022 265);
|
|
104
|
+
--popover-foreground: oklch(0.965 0.008 265);
|
|
105
|
+
--primary: oklch(0.625 0.215 262);
|
|
106
|
+
--primary-foreground: oklch(0.115 0.018 265);
|
|
107
|
+
--secondary: oklch(0.205 0.028 265);
|
|
108
|
+
--secondary-foreground: oklch(0.965 0.008 265);
|
|
109
|
+
--muted: oklch(0.205 0.028 265);
|
|
110
|
+
--muted-foreground: oklch(0.625 0.018 265);
|
|
111
|
+
--accent: oklch(0.205 0.028 265);
|
|
112
|
+
--accent-foreground: oklch(0.965 0.008 265);
|
|
113
|
+
--destructive: oklch(0.545 0.195 27);
|
|
114
|
+
--border: oklch(0.245 0.028 265);
|
|
115
|
+
--input: oklch(0.245 0.028 265);
|
|
116
|
+
--ring: oklch(0.625 0.215 262);
|
|
117
|
+
--chart-1: oklch(0.625 0.215 262);
|
|
118
|
+
--chart-2: oklch(0.685 0.155 200);
|
|
119
|
+
--chart-3: oklch(0.685 0.155 340);
|
|
120
|
+
--chart-4: oklch(0.725 0.175 145);
|
|
121
|
+
--chart-5: oklch(0.725 0.175 38);
|
|
122
|
+
--sidebar: oklch(0.125 0.02 265);
|
|
123
|
+
--sidebar-foreground: oklch(0.965 0.008 265);
|
|
124
|
+
--sidebar-primary: oklch(0.625 0.215 262);
|
|
125
|
+
--sidebar-primary-foreground: oklch(0.965 0.008 265);
|
|
126
|
+
--sidebar-accent: oklch(0.205 0.028 265);
|
|
127
|
+
--sidebar-accent-foreground: oklch(0.965 0.008 265);
|
|
128
|
+
--sidebar-border: oklch(0.245 0.028 265);
|
|
129
|
+
--sidebar-ring: oklch(0.625 0.215 262);
|
|
130
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
131
|
+
--success: oklch(0.725 0.175 145);
|
|
132
|
+
--success-foreground: oklch(0.115 0.018 265);
|
|
133
|
+
--code-bg: oklch(0.18 0 0);
|
|
134
|
+
--code-foreground: oklch(0.93 0 0);
|
|
135
|
+
--code-muted: oklch(0.55 0 0);
|
|
136
|
+
--code-border: oklch(0.3 0 0);
|
|
137
|
+
--code-highlight: oklch(0.28 0 0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@layer base {
|
|
141
|
+
* {
|
|
142
|
+
@apply border-border outline-ring/50;
|
|
143
|
+
}
|
|
144
|
+
body {
|
|
145
|
+
@apply bg-background text-foreground;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BookOpen01Icon, Home01Icon, TestTube01Icon } from "hugeicons-react";
|
|
2
|
+
|
|
3
|
+
export interface NavItem {
|
|
4
|
+
label: string;
|
|
5
|
+
pageId: string;
|
|
6
|
+
icon: React.ElementType;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const navItems: NavItem[] = [
|
|
10
|
+
{
|
|
11
|
+
label: "Home",
|
|
12
|
+
pageId: "home",
|
|
13
|
+
icon: Home01Icon,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: "Documentation",
|
|
17
|
+
pageId: "documentation",
|
|
18
|
+
icon: BookOpen01Icon,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Demo",
|
|
22
|
+
pageId: "demo",
|
|
23
|
+
icon: TestTube01Icon,
|
|
24
|
+
},
|
|
25
|
+
];
|
package/src/lib/utils.ts
ADDED
package/src/main.tsx
ADDED