@khester/create-dynamics-app 2.0.0 → 2.2.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/README.md +28 -0
- package/dist/artifacts/registry.d.ts +4 -3
- package/dist/artifacts/registry.d.ts.map +1 -1
- package/dist/artifacts/registry.js +145 -11
- package/dist/artifacts/registry.js.map +1 -1
- package/dist/artifacts/types.d.ts +10 -1
- package/dist/artifacts/types.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/injectDevTools.d.ts.map +1 -1
- package/dist/injectDevTools.js +4 -2
- package/dist/injectDevTools.js.map +1 -1
- package/dist/scaffold.d.ts +23 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +27 -1
- package/dist/scaffold.js.map +1 -1
- package/package.json +3 -2
- package/templates/grid-starter/ARCHITECTURE.md +66 -0
- package/templates/grid-starter/README.md +122 -0
- package/templates/grid-starter/env.example +16 -0
- package/templates/grid-starter/gitignore +6 -0
- package/templates/grid-starter/index.html +16 -0
- package/templates/grid-starter/package.json +39 -0
- package/templates/grid-starter/src/App.tsx +23 -0
- package/templates/grid-starter/src/core/services/FetchApiService.ts +117 -0
- package/templates/grid-starter/src/core/services/IApiService.ts +37 -0
- package/templates/grid-starter/src/core/services/MockApiService.ts +72 -0
- package/templates/grid-starter/src/core/services/ServiceFactory.ts +58 -0
- package/templates/grid-starter/src/core/services/XrmApiService.ts +135 -0
- package/templates/grid-starter/src/core/services/crudLogging.ts +52 -0
- package/templates/grid-starter/src/dev-tools/DevPanel.tsx +239 -0
- package/templates/grid-starter/src/grid/GridPage.tsx +119 -0
- package/templates/grid-starter/src/index.tsx +18 -0
- package/templates/grid-starter/src/vite-env.d.ts +15 -0
- package/templates/grid-starter/tools/deploy/deploy-webresource.cjs +117 -0
- package/templates/grid-starter/tsconfig.json +19 -0
- package/templates/grid-starter/vite.config.ts +76 -0
- package/templates/pcf-field/_variants/ValueInput.boolean.tsx +2 -0
- package/templates/pcf-field/_variants/ValueInput.date.tsx +2 -0
- package/templates/pcf-field/_variants/ValueInput.number.tsx +2 -0
- package/templates/pcf-field/_variants/ValueInput.optionset.tsx +77 -0
- package/templates/pcf-field/_variants/ValueInput.text.tsx +2 -0
- package/templates/pcf-field/index.ts +1 -1
- package/templates/pcf-field/package.json +3 -1
- package/templates/pcf-field/{{componentName}}Component.tsx +2 -0
- package/templates/react-custom-page/ARCHITECTURE.md +75 -0
- package/templates/react-custom-page/README.md +74 -568
- package/templates/react-custom-page/env.example +16 -0
- package/templates/react-custom-page/gitignore +1 -0
- package/templates/react-custom-page/index.html +16 -0
- package/templates/react-custom-page/package.json +21 -49
- package/templates/react-custom-page/src/App.tsx +26 -0
- package/templates/react-custom-page/src/core/recordContext.test.ts +30 -0
- package/templates/react-custom-page/src/core/recordContext.ts +51 -0
- package/templates/react-custom-page/src/core/services/FetchApiService.ts +117 -0
- package/templates/react-custom-page/src/core/services/IApiService.ts +37 -0
- package/templates/react-custom-page/src/core/services/MockApiService.ts +73 -0
- package/templates/react-custom-page/src/core/services/ServiceFactory.ts +58 -0
- package/templates/react-custom-page/src/core/services/XrmApiService.ts +135 -0
- package/templates/react-custom-page/src/core/services/crudLogging.ts +52 -0
- package/templates/react-custom-page/src/dev-tools/DevPanel.tsx +238 -0
- package/templates/react-custom-page/src/domain/diff.test.ts +87 -0
- package/templates/react-custom-page/src/domain/diff.ts +38 -0
- package/templates/react-custom-page/src/example/ExamplePage.tsx +140 -0
- package/templates/react-custom-page/src/example/exampleError.ts +36 -0
- package/templates/react-custom-page/src/example/hooks/useExampleData.ts +40 -0
- package/templates/react-custom-page/src/example/hooks/useExampleForm.ts +99 -0
- package/templates/react-custom-page/src/example/mappers/accountMapper.test.ts +38 -0
- package/templates/react-custom-page/src/example/mappers/accountMapper.ts +55 -0
- package/templates/react-custom-page/src/example/models/Account.ts +74 -0
- package/templates/react-custom-page/src/index.tsx +18 -128
- package/templates/react-custom-page/src/vite-env.d.ts +15 -0
- package/templates/react-custom-page/tools/deploy/deploy-webresource.cjs +117 -0
- package/templates/react-custom-page/tsconfig.json +12 -22
- package/templates/react-custom-page/vite.config.ts +76 -0
- package/templates/starter-page/README.md +38 -0
- package/templates/starter-page/_variants/App.dashboard.v8.tsx +46 -0
- package/templates/starter-page/_variants/App.form.v8.tsx +59 -0
- package/templates/starter-page/_variants/App.master-detail.v8.tsx +61 -0
- package/templates/starter-page/_variants/App.panel.v8.tsx +99 -0
- package/templates/starter-page/gitignore +5 -0
- package/templates/starter-page/package.json +27 -0
- package/templates/starter-page/public/index.html +11 -0
- package/templates/starter-page/src/index.tsx +10 -0
- package/templates/starter-page/src/services/dataverse.ts +30 -0
- package/templates/starter-page/tsconfig.json +15 -0
- package/templates/starter-page/webpack.config.js +17 -0
- package/templates/react-custom-page/deployment/README.md +0 -484
- package/templates/react-custom-page/docs/ARCHITECTURE_OVERVIEW.md +0 -506
- package/templates/react-custom-page/docs/BEST_PRACTICES.md +0 -723
- package/templates/react-custom-page/docs/MIGRATION_GUIDE.md +0 -447
- package/templates/react-custom-page/public/index.html +0 -15
- package/templates/react-custom-page/scripts/custom-build.js +0 -255
- package/templates/react-custom-page/src/components/AccountForm.css +0 -71
- package/templates/react-custom-page/src/components/AccountForm.tsx +0 -541
- package/templates/react-custom-page/src/components/AccountManagement.css +0 -86
- package/templates/react-custom-page/src/components/AccountManagement.tsx +0 -370
- package/templates/react-custom-page/src/components/ContactForm.css +0 -48
- package/templates/react-custom-page/src/components/ContactForm.tsx +0 -327
- package/templates/react-custom-page/src/components/ContactManagement.css +0 -86
- package/templates/react-custom-page/src/components/ContactManagement.tsx +0 -357
- package/templates/react-custom-page/src/components/Logging/LogDialog.tsx +0 -291
- package/templates/react-custom-page/src/components/Logging/LoggingContext.tsx +0 -166
- package/templates/react-custom-page/src/components/Logging/LoggingDebugPanel.css +0 -192
- package/templates/react-custom-page/src/components/Logging/LoggingDebugPanel.tsx +0 -177
- package/templates/react-custom-page/src/components/Logging/LoggingProvider.tsx +0 -3
- package/templates/react-custom-page/src/components/Logging/logger.ts +0 -193
- package/templates/react-custom-page/src/constants/account.ts +0 -410
- package/templates/react-custom-page/src/constants/contact.ts +0 -362
- package/templates/react-custom-page/src/models/Account.ts +0 -480
- package/templates/react-custom-page/src/models/BaseEntity.ts +0 -204
- package/templates/react-custom-page/src/models/Contact.ts +0 -580
- package/templates/react-custom-page/src/pcf/ContactControlWrapper.tsx +0 -107
- package/templates/react-custom-page/src/pcf/MultiEntityControlWrapper.tsx +0 -205
- package/templates/react-custom-page/src/providers/DynamicsProvider.tsx +0 -353
- package/templates/react-custom-page/src/services/MockApiService.ts +0 -260
- package/templates/react-custom-page/src/services/ServiceFactory.ts +0 -65
- package/templates/react-custom-page/src/services/XrmApiService.ts +0 -213
- package/templates/react-custom-page/src/styles/index.css +0 -171
- package/templates/react-custom-page/tools/metadata-sync/index.js +0 -152
- package/templates/react-custom-page/webpack.config.js +0 -57
- /package/templates/_shared/dev-tools/auth/{get-token.js → get-token.cjs} +0 -0
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
createContext,
|
|
3
|
-
useContext,
|
|
4
|
-
useState,
|
|
5
|
-
useEffect,
|
|
6
|
-
ReactNode,
|
|
7
|
-
} from 'react';
|
|
8
|
-
import { Logger } from './logger';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Log entry interface
|
|
12
|
-
*/
|
|
13
|
-
export interface LogEntry {
|
|
14
|
-
timestamp: string;
|
|
15
|
-
message: string;
|
|
16
|
-
source?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Logging context properties
|
|
21
|
-
*/
|
|
22
|
-
interface LoggingContextProps {
|
|
23
|
-
logs: LogEntry[];
|
|
24
|
-
addLog: (message: string, source?: string) => void;
|
|
25
|
-
clearLogs: () => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Logging context
|
|
30
|
-
*/
|
|
31
|
-
const LoggingContext = createContext<LoggingContextProps | undefined>(
|
|
32
|
-
undefined
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Props for LoggingProvider
|
|
37
|
-
*/
|
|
38
|
-
interface LoggingProviderProps {
|
|
39
|
-
children: ReactNode;
|
|
40
|
-
interceptConsole?: boolean;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Logging provider component that manages logging state and provides context
|
|
45
|
-
*/
|
|
46
|
-
export const LoggingProvider: React.FC<LoggingProviderProps> = ({
|
|
47
|
-
children,
|
|
48
|
-
interceptConsole = false,
|
|
49
|
-
}) => {
|
|
50
|
-
const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Add a new log entry
|
|
54
|
-
*/
|
|
55
|
-
const addLog = (message: string, source?: string) => {
|
|
56
|
-
const timestamp = new Date().toISOString();
|
|
57
|
-
const newLog: LogEntry = {
|
|
58
|
-
timestamp,
|
|
59
|
-
message,
|
|
60
|
-
source,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
setLogs((prevLogs) => [...prevLogs, newLog]);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Clear all logs
|
|
68
|
-
*/
|
|
69
|
-
const clearLogs = () => {
|
|
70
|
-
setLogs([]);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Setup logger integration and optional console interception
|
|
75
|
-
*/
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
// Set the logger function to use our addLog method
|
|
78
|
-
Logger.setLoggerFunction(addLog);
|
|
79
|
-
|
|
80
|
-
// Optional console interception
|
|
81
|
-
if (interceptConsole) {
|
|
82
|
-
const originalConsoleLog = console.log;
|
|
83
|
-
const originalConsoleError = console.error;
|
|
84
|
-
const originalConsoleWarn = console.warn;
|
|
85
|
-
|
|
86
|
-
console.log = (...args: any[]) => {
|
|
87
|
-
const message = args
|
|
88
|
-
.map((arg) =>
|
|
89
|
-
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
|
|
90
|
-
)
|
|
91
|
-
.join(' ');
|
|
92
|
-
addLog(message, 'console.log');
|
|
93
|
-
originalConsoleLog(...args);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
console.error = (...args: any[]) => {
|
|
97
|
-
const message = args
|
|
98
|
-
.map((arg) =>
|
|
99
|
-
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
|
|
100
|
-
)
|
|
101
|
-
.join(' ');
|
|
102
|
-
addLog(`ERROR: ${message}`, 'console.error');
|
|
103
|
-
originalConsoleError(...args);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
console.warn = (...args: any[]) => {
|
|
107
|
-
const message = args
|
|
108
|
-
.map((arg) =>
|
|
109
|
-
typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
|
|
110
|
-
)
|
|
111
|
-
.join(' ');
|
|
112
|
-
addLog(`WARNING: ${message}`, 'console.warn');
|
|
113
|
-
originalConsoleWarn(...args);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Cleanup function to restore original console methods
|
|
117
|
-
return () => {
|
|
118
|
-
console.log = originalConsoleLog;
|
|
119
|
-
console.error = originalConsoleError;
|
|
120
|
-
console.warn = originalConsoleWarn;
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
}, [interceptConsole]);
|
|
124
|
-
|
|
125
|
-
const contextValue: LoggingContextProps = {
|
|
126
|
-
logs,
|
|
127
|
-
addLog,
|
|
128
|
-
clearLogs,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<LoggingContext.Provider value={contextValue}>
|
|
133
|
-
{children}
|
|
134
|
-
</LoggingContext.Provider>
|
|
135
|
-
);
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Hook to use logging context
|
|
140
|
-
*/
|
|
141
|
-
export const useLogging = (): LoggingContextProps => {
|
|
142
|
-
const context = useContext(LoggingContext);
|
|
143
|
-
if (!context) {
|
|
144
|
-
throw new Error('useLogging must be used within a LoggingProvider');
|
|
145
|
-
}
|
|
146
|
-
return context;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Higher-order component to wrap components with logging
|
|
151
|
-
*/
|
|
152
|
-
export const withLogging = <P extends object>(
|
|
153
|
-
Component: React.ComponentType<P>
|
|
154
|
-
): React.FC<P & { enableConsoleInterception?: boolean }> => {
|
|
155
|
-
const EnhancedComponent = ({
|
|
156
|
-
enableConsoleInterception = false,
|
|
157
|
-
...props
|
|
158
|
-
}) => (
|
|
159
|
-
<LoggingProvider interceptConsole={enableConsoleInterception}>
|
|
160
|
-
<Component {...(props as P)} />
|
|
161
|
-
</LoggingProvider>
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
EnhancedComponent.displayName = `withLogging(${Component.displayName || Component.name})`;
|
|
165
|
-
return EnhancedComponent;
|
|
166
|
-
};
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
.logging-debug-panel {
|
|
2
|
-
background: white;
|
|
3
|
-
border-radius: 8px;
|
|
4
|
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
5
|
-
width: 90vw;
|
|
6
|
-
height: 80vh;
|
|
7
|
-
max-width: 1000px;
|
|
8
|
-
max-height: 600px;
|
|
9
|
-
display: flex;
|
|
10
|
-
flex-direction: column;
|
|
11
|
-
overflow: hidden;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.logging-debug-panel__header {
|
|
15
|
-
display: flex;
|
|
16
|
-
justify-content: space-between;
|
|
17
|
-
align-items: center;
|
|
18
|
-
padding: 16px 20px;
|
|
19
|
-
border-bottom: 1px solid #edebe9;
|
|
20
|
-
background: #f8f8f8;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.logging-debug-panel__header h3 {
|
|
24
|
-
margin: 0;
|
|
25
|
-
font-size: 18px;
|
|
26
|
-
font-weight: 600;
|
|
27
|
-
color: #323130;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.logging-debug-panel__controls {
|
|
31
|
-
display: flex;
|
|
32
|
-
justify-content: space-between;
|
|
33
|
-
align-items: center;
|
|
34
|
-
padding: 12px 20px;
|
|
35
|
-
border-bottom: 1px solid #edebe9;
|
|
36
|
-
gap: 16px;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.logging-debug-panel__filters {
|
|
40
|
-
display: flex;
|
|
41
|
-
gap: 12px;
|
|
42
|
-
align-items: center;
|
|
43
|
-
flex: 1;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.logging-debug-panel__filters .ms-TextField {
|
|
47
|
-
min-width: 200px;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.level-filter {
|
|
51
|
-
padding: 6px 12px;
|
|
52
|
-
border: 1px solid #8a8886;
|
|
53
|
-
border-radius: 2px;
|
|
54
|
-
font-size: 14px;
|
|
55
|
-
background: white;
|
|
56
|
-
color: #323130;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.level-filter:focus {
|
|
60
|
-
outline: none;
|
|
61
|
-
border-color: #0078d4;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.logging-debug-panel__actions {
|
|
65
|
-
display: flex;
|
|
66
|
-
gap: 8px;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.logging-debug-panel__content {
|
|
70
|
-
flex: 1;
|
|
71
|
-
overflow: hidden;
|
|
72
|
-
display: flex;
|
|
73
|
-
flex-direction: column;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.logging-debug-panel__logs {
|
|
77
|
-
flex: 1;
|
|
78
|
-
overflow-y: auto;
|
|
79
|
-
padding: 8px;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.no-logs {
|
|
83
|
-
display: flex;
|
|
84
|
-
justify-content: center;
|
|
85
|
-
align-items: center;
|
|
86
|
-
height: 200px;
|
|
87
|
-
color: #605e5c;
|
|
88
|
-
font-style: italic;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.log-entry {
|
|
92
|
-
padding: 8px 12px;
|
|
93
|
-
border-bottom: 1px solid #f3f2f1;
|
|
94
|
-
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
95
|
-
font-size: 12px;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.log-entry:hover {
|
|
99
|
-
background-color: #faf9f8;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.log-entry__header {
|
|
103
|
-
display: flex;
|
|
104
|
-
gap: 8px;
|
|
105
|
-
align-items: center;
|
|
106
|
-
margin-bottom: 4px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.log-entry__timestamp {
|
|
110
|
-
color: #605e5c;
|
|
111
|
-
font-weight: 500;
|
|
112
|
-
min-width: 80px;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.log-entry__level {
|
|
116
|
-
font-weight: 600;
|
|
117
|
-
min-width: 60px;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.log-entry__context {
|
|
121
|
-
color: #0078d4;
|
|
122
|
-
font-weight: 500;
|
|
123
|
-
background: #deecf9;
|
|
124
|
-
padding: 2px 6px;
|
|
125
|
-
border-radius: 3px;
|
|
126
|
-
font-size: 11px;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.log-entry__message {
|
|
130
|
-
color: #323130;
|
|
131
|
-
line-height: 1.4;
|
|
132
|
-
margin-left: 96px;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.log-entry__data {
|
|
136
|
-
margin-left: 96px;
|
|
137
|
-
margin-top: 4px;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.log-entry__data pre {
|
|
141
|
-
background: #f8f8f8;
|
|
142
|
-
padding: 8px;
|
|
143
|
-
border-radius: 4px;
|
|
144
|
-
border-left: 3px solid #0078d4;
|
|
145
|
-
font-size: 11px;
|
|
146
|
-
overflow-x: auto;
|
|
147
|
-
margin: 0;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.logging-debug-panel__footer {
|
|
151
|
-
padding: 8px 20px;
|
|
152
|
-
border-top: 1px solid #edebe9;
|
|
153
|
-
background: #f8f8f8;
|
|
154
|
-
text-align: center;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.logging-debug-panel__footer small {
|
|
158
|
-
color: #605e5c;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/* Mobile responsiveness */
|
|
162
|
-
@media (max-width: 768px) {
|
|
163
|
-
.logging-debug-panel {
|
|
164
|
-
width: 95vw;
|
|
165
|
-
height: 90vh;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.logging-debug-panel__controls {
|
|
169
|
-
flex-direction: column;
|
|
170
|
-
align-items: stretch;
|
|
171
|
-
gap: 8px;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.logging-debug-panel__filters {
|
|
175
|
-
flex-direction: column;
|
|
176
|
-
gap: 8px;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.logging-debug-panel__filters .ms-TextField {
|
|
180
|
-
min-width: auto;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.log-entry__message,
|
|
184
|
-
.log-entry__data {
|
|
185
|
-
margin-left: 0;
|
|
186
|
-
margin-top: 4px;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.log-entry__header {
|
|
190
|
-
flex-wrap: wrap;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import React, { useState, useCallback } from 'react';
|
|
2
|
-
import { Button, TextField } from '@khester/dynamics-ui-components';
|
|
3
|
-
import { Logger } from './logger';
|
|
4
|
-
import './LoggingDebugPanel.css';
|
|
5
|
-
|
|
6
|
-
interface LoggingDebugPanelProps {
|
|
7
|
-
onClose: () => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface LogEntry {
|
|
11
|
-
timestamp: string;
|
|
12
|
-
level: string;
|
|
13
|
-
message: string;
|
|
14
|
-
context?: string;
|
|
15
|
-
data?: any;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Debug panel component for viewing application logs
|
|
20
|
-
*/
|
|
21
|
-
export const LoggingDebugPanel: React.FC<LoggingDebugPanelProps> = ({
|
|
22
|
-
onClose,
|
|
23
|
-
}) => {
|
|
24
|
-
const [filter, setFilter] = useState('');
|
|
25
|
-
const [selectedLevel, setSelectedLevel] = useState('all');
|
|
26
|
-
|
|
27
|
-
// Get logs from the Logger (this is a simplified version)
|
|
28
|
-
const [logs] = useState<LogEntry[]>(() => {
|
|
29
|
-
// In a real implementation, you'd get logs from the Logger class
|
|
30
|
-
// For now, create some sample logs to demonstrate the UI
|
|
31
|
-
return [
|
|
32
|
-
{
|
|
33
|
-
timestamp: new Date().toISOString(),
|
|
34
|
-
level: 'info',
|
|
35
|
-
message: 'Application initialized',
|
|
36
|
-
context: 'App',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
timestamp: new Date().toISOString(),
|
|
40
|
-
level: 'debug',
|
|
41
|
-
message: 'ServiceFactory: Running in mock environment',
|
|
42
|
-
context: 'ServiceFactory',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
timestamp: new Date().toISOString(),
|
|
46
|
-
level: 'user',
|
|
47
|
-
message: 'Contact Management loaded',
|
|
48
|
-
context: 'ContactManagement',
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const filteredLogs = logs.filter((log) => {
|
|
54
|
-
const matchesFilter =
|
|
55
|
-
!filter ||
|
|
56
|
-
log.message.toLowerCase().includes(filter.toLowerCase()) ||
|
|
57
|
-
log.context?.toLowerCase().includes(filter.toLowerCase());
|
|
58
|
-
|
|
59
|
-
const matchesLevel = selectedLevel === 'all' || log.level === selectedLevel;
|
|
60
|
-
|
|
61
|
-
return matchesFilter && matchesLevel;
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const clearLogs = useCallback(() => {
|
|
65
|
-
Logger.log('Debug panel: Logs cleared', 'LoggingDebugPanel');
|
|
66
|
-
// In a real implementation, you'd clear the actual logs
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
const exportLogs = useCallback(() => {
|
|
70
|
-
const logData = JSON.stringify(logs, null, 2);
|
|
71
|
-
const blob = new Blob([logData], { type: 'application/json' });
|
|
72
|
-
const url = URL.createObjectURL(blob);
|
|
73
|
-
const a = document.createElement('a');
|
|
74
|
-
a.href = url;
|
|
75
|
-
a.download = `dynamics-logs-${new Date().toISOString().split('T')[0]}.json`;
|
|
76
|
-
a.click();
|
|
77
|
-
URL.revokeObjectURL(url);
|
|
78
|
-
Logger.log('Debug panel: Logs exported', 'LoggingDebugPanel');
|
|
79
|
-
}, [logs]);
|
|
80
|
-
|
|
81
|
-
const getLevelColor = (level: string) => {
|
|
82
|
-
switch (level) {
|
|
83
|
-
case 'error':
|
|
84
|
-
return '#d13438';
|
|
85
|
-
case 'warn':
|
|
86
|
-
return '#ff8c00';
|
|
87
|
-
case 'info':
|
|
88
|
-
return '#0078d4';
|
|
89
|
-
case 'debug':
|
|
90
|
-
return '#605e5c';
|
|
91
|
-
case 'user':
|
|
92
|
-
return '#107c10';
|
|
93
|
-
default:
|
|
94
|
-
return '#323130';
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const formatTimestamp = (timestamp: string) => {
|
|
99
|
-
return new Date(timestamp).toLocaleTimeString();
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
return (
|
|
103
|
-
<div className="logging-debug-panel">
|
|
104
|
-
<div className="logging-debug-panel__header">
|
|
105
|
-
<h3>Debug Console</h3>
|
|
106
|
-
<Button text="✕" onClick={onClose} variant="default" />
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
<div className="logging-debug-panel__controls">
|
|
110
|
-
<div className="logging-debug-panel__filters">
|
|
111
|
-
<TextField
|
|
112
|
-
placeholder="Filter logs..."
|
|
113
|
-
value={filter}
|
|
114
|
-
onChange={(_, value) => setFilter(value || '')}
|
|
115
|
-
/>
|
|
116
|
-
|
|
117
|
-
<select
|
|
118
|
-
value={selectedLevel}
|
|
119
|
-
onChange={(e) => setSelectedLevel(e.target.value)}
|
|
120
|
-
className="level-filter"
|
|
121
|
-
>
|
|
122
|
-
<option value="all">All Levels</option>
|
|
123
|
-
<option value="error">Error</option>
|
|
124
|
-
<option value="warn">Warning</option>
|
|
125
|
-
<option value="info">Info</option>
|
|
126
|
-
<option value="debug">Debug</option>
|
|
127
|
-
<option value="user">User Action</option>
|
|
128
|
-
</select>
|
|
129
|
-
</div>
|
|
130
|
-
|
|
131
|
-
<div className="logging-debug-panel__actions">
|
|
132
|
-
<Button text="Clear" onClick={clearLogs} variant="default" />
|
|
133
|
-
<Button text="Export" onClick={exportLogs} variant="default" />
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
|
|
137
|
-
<div className="logging-debug-panel__content">
|
|
138
|
-
<div className="logging-debug-panel__logs">
|
|
139
|
-
{filteredLogs.length === 0 ? (
|
|
140
|
-
<div className="no-logs">No logs match the current filter</div>
|
|
141
|
-
) : (
|
|
142
|
-
filteredLogs.map((log, index) => (
|
|
143
|
-
<div key={index} className="log-entry">
|
|
144
|
-
<div className="log-entry__header">
|
|
145
|
-
<span className="log-entry__timestamp">
|
|
146
|
-
{formatTimestamp(log.timestamp)}
|
|
147
|
-
</span>
|
|
148
|
-
<span
|
|
149
|
-
className="log-entry__level"
|
|
150
|
-
style={{ color: getLevelColor(log.level) }}
|
|
151
|
-
>
|
|
152
|
-
[{log.level.toUpperCase()}]
|
|
153
|
-
</span>
|
|
154
|
-
{log.context && (
|
|
155
|
-
<span className="log-entry__context">{log.context}</span>
|
|
156
|
-
)}
|
|
157
|
-
</div>
|
|
158
|
-
<div className="log-entry__message">{log.message}</div>
|
|
159
|
-
{log.data && (
|
|
160
|
-
<div className="log-entry__data">
|
|
161
|
-
<pre>{JSON.stringify(log.data, null, 2)}</pre>
|
|
162
|
-
</div>
|
|
163
|
-
)}
|
|
164
|
-
</div>
|
|
165
|
-
))
|
|
166
|
-
)}
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
|
|
170
|
-
<div className="logging-debug-panel__footer">
|
|
171
|
-
<small>
|
|
172
|
-
Showing {filteredLogs.length} of {logs.length} log entries
|
|
173
|
-
</small>
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
);
|
|
177
|
-
};
|