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.
Files changed (46) hide show
  1. package/.env.example +22 -0
  2. package/README.md +75 -0
  3. package/bin/cli.js +84 -0
  4. package/components.json +22 -0
  5. package/eslint.config.js +23 -0
  6. package/getToken.js +197 -0
  7. package/index.html +30 -0
  8. package/package.json +69 -0
  9. package/src/App.tsx +28 -0
  10. package/src/assets/images/novalogica-logo.svg +24 -0
  11. package/src/components/nl-header.tsx +165 -0
  12. package/src/components/page-layout.tsx +78 -0
  13. package/src/components/page-render.tsx +16 -0
  14. package/src/components/ui/alert.tsx +66 -0
  15. package/src/components/ui/badge.tsx +49 -0
  16. package/src/components/ui/button.tsx +165 -0
  17. package/src/components/ui/card.tsx +92 -0
  18. package/src/components/ui/dialog.tsx +156 -0
  19. package/src/components/ui/input.tsx +23 -0
  20. package/src/components/ui/label.tsx +23 -0
  21. package/src/components/ui/separator.tsx +26 -0
  22. package/src/components/ui/table.tsx +116 -0
  23. package/src/components/ui/tabs.tsx +91 -0
  24. package/src/components/ui/theme-toggle.tsx +28 -0
  25. package/src/config/pages.config.ts +34 -0
  26. package/src/contexts/dataverse-context.tsx +12 -0
  27. package/src/contexts/navigation-context.tsx +14 -0
  28. package/src/hooks/useAccounts.ts +194 -0
  29. package/src/hooks/useDataverse.ts +11 -0
  30. package/src/hooks/useNavigation.ts +41 -0
  31. package/src/index.css +147 -0
  32. package/src/lib/nav-items.ts +25 -0
  33. package/src/lib/utils.ts +6 -0
  34. package/src/main.tsx +12 -0
  35. package/src/pages/Demo.tsx +465 -0
  36. package/src/pages/Documentation.tsx +850 -0
  37. package/src/pages/Home.tsx +132 -0
  38. package/src/pages/index.ts +4 -0
  39. package/src/providers/dataverse-provider.tsx +81 -0
  40. package/src/providers/navigation-provider.tsx +33 -0
  41. package/src/providers/theme-provider.tsx +92 -0
  42. package/src/public/novalogica-logo.svg +24 -0
  43. package/tsconfig.app.json +32 -0
  44. package/tsconfig.json +17 -0
  45. package/tsconfig.node.json +26 -0
  46. 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
+ ];
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
package/src/main.tsx ADDED
@@ -0,0 +1,12 @@
1
+ import "./index.css";
2
+
3
+ import { StrictMode } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+
6
+ import App from "./App.tsx";
7
+
8
+ createRoot(document.getElementById("root")!).render(
9
+ <StrictMode>
10
+ <App />
11
+ </StrictMode>,
12
+ );