@fgv/ts-res-ui-components 5.0.0-21 → 5.0.0-23
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 +401 -155
- package/config/jest.setup.js +10 -0
- package/dist/ts-res-ui-components.d.ts +1657 -76
- package/lib/components/common/QualifierContextControl.js +4 -1
- package/lib/components/common/ResourceTreeView.js +4 -1
- package/lib/components/forms/GenericQualifierTypeEditForm.d.ts +26 -0
- package/lib/components/forms/GenericQualifierTypeEditForm.js +166 -0
- package/lib/components/forms/QualifierEditForm.d.ts +1 -1
- package/lib/components/forms/index.d.ts +2 -0
- package/lib/components/forms/index.js +1 -0
- package/lib/components/orchestrator/ResourceOrchestrator.d.ts +3 -0
- package/lib/components/orchestrator/ResourceOrchestrator.js +118 -51
- package/lib/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
- package/lib/components/pickers/ResourcePicker/index.js +4 -2
- package/lib/components/views/CompiledView/index.js +75 -16
- package/lib/components/views/ConfigurationView/index.js +94 -35
- package/lib/components/views/FilterView/index.js +7 -4
- package/lib/components/views/GridView/EditableGridCell.d.ts +76 -0
- package/lib/components/views/GridView/EditableGridCell.js +224 -0
- package/lib/components/views/GridView/GridSelector.d.ts +43 -0
- package/lib/components/views/GridView/GridSelector.js +89 -0
- package/lib/components/views/GridView/MultiGridView.d.ts +85 -0
- package/lib/components/views/GridView/MultiGridView.js +196 -0
- package/lib/components/views/GridView/ResourceGrid.d.ts +38 -0
- package/lib/components/views/GridView/ResourceGrid.js +232 -0
- package/lib/components/views/GridView/SharedContextControls.d.ts +47 -0
- package/lib/components/views/GridView/SharedContextControls.js +95 -0
- package/lib/components/views/GridView/cells/BooleanCell.d.ts +44 -0
- package/lib/components/views/GridView/cells/BooleanCell.js +49 -0
- package/lib/components/views/GridView/cells/DropdownCell.d.ts +58 -0
- package/lib/components/views/GridView/cells/DropdownCell.js +182 -0
- package/lib/components/views/GridView/cells/StringCell.d.ts +57 -0
- package/lib/components/views/GridView/cells/StringCell.js +106 -0
- package/lib/components/views/GridView/cells/TriStateCell.d.ts +54 -0
- package/lib/components/views/GridView/cells/TriStateCell.js +112 -0
- package/lib/components/views/GridView/cells/index.d.ts +15 -0
- package/lib/components/views/GridView/cells/index.js +11 -0
- package/lib/components/views/GridView/index.d.ts +53 -0
- package/lib/components/views/GridView/index.js +212 -0
- package/lib/components/views/ImportView/index.js +22 -19
- package/lib/components/views/MessagesWindow/index.js +4 -1
- package/lib/components/views/ResolutionView/index.js +8 -5
- package/lib/contexts/ObservabilityContext.d.ts +85 -0
- package/lib/contexts/ObservabilityContext.js +98 -0
- package/lib/contexts/index.d.ts +2 -0
- package/lib/contexts/index.js +24 -0
- package/lib/hooks/useConfigurationState.d.ts +3 -3
- package/lib/hooks/useResolutionState.js +850 -246
- package/lib/hooks/useResourceData.d.ts +7 -4
- package/lib/hooks/useResourceData.js +185 -184
- package/lib/index.d.ts +5 -1
- package/lib/index.js +8 -1
- package/lib/namespaces/GridTools.d.ts +136 -0
- package/lib/namespaces/GridTools.js +138 -0
- package/lib/namespaces/ObservabilityTools.d.ts +3 -0
- package/lib/namespaces/ObservabilityTools.js +23 -0
- package/lib/namespaces/ResolutionTools.d.ts +2 -1
- package/lib/namespaces/ResolutionTools.js +2 -0
- package/lib/namespaces/index.d.ts +2 -0
- package/lib/namespaces/index.js +2 -0
- package/lib/test/integration/observability.integration.test.d.ts +2 -0
- package/lib/test/unit/hooks/useResourceData.test.d.ts +2 -0
- package/lib/test/unit/utils/downloadHelper.test.d.ts +2 -0
- package/lib/test/unit/workflows/resolutionWorkflows.test.d.ts +2 -0
- package/lib/test/unit/workflows/resourceCreation.test.d.ts +2 -0
- package/lib/test/unit/workflows/resultPatternExtensions.test.d.ts +2 -0
- package/lib/test/unit/workflows/validation.test.d.ts +2 -0
- package/lib/types/index.d.ts +387 -20
- package/lib/types/index.js +2 -1
- package/lib/utils/cellValidation.d.ts +113 -0
- package/lib/utils/cellValidation.js +248 -0
- package/lib/utils/downloadHelper.d.ts +66 -0
- package/lib/utils/downloadHelper.js +195 -0
- package/lib/utils/observability/factories.d.ts +29 -0
- package/lib/utils/observability/factories.js +58 -0
- package/lib/utils/observability/implementations.d.ts +61 -0
- package/lib/utils/observability/implementations.js +103 -0
- package/lib/utils/observability/index.d.ts +4 -0
- package/lib/utils/observability/index.js +26 -0
- package/lib/utils/observability/interfaces.d.ts +30 -0
- package/lib/utils/observability/interfaces.js +23 -0
- package/lib/utils/resolutionEditing.js +2 -1
- package/lib/utils/resourceSelector.d.ts +97 -0
- package/lib/utils/resourceSelector.js +195 -0
- package/lib/utils/resourceSelectors.d.ts +146 -0
- package/lib/utils/resourceSelectors.js +233 -0
- package/lib/utils/tsResIntegration.d.ts +6 -41
- package/lib/utils/tsResIntegration.js +20 -16
- package/lib/utils/zipLoader/zipProcessingHelpers.d.ts +3 -2
- package/lib/utils/zipLoader/zipProcessingHelpers.js +6 -5
- package/lib-commonjs/components/common/QualifierContextControl.js +4 -1
- package/lib-commonjs/components/common/ResourceTreeView.js +4 -1
- package/lib-commonjs/components/forms/GenericQualifierTypeEditForm.js +171 -0
- package/lib-commonjs/components/forms/index.js +3 -1
- package/lib-commonjs/components/orchestrator/ResourceOrchestrator.js +118 -51
- package/lib-commonjs/components/pickers/ResourcePicker/ResourcePickerTree.js +32 -10
- package/lib-commonjs/components/pickers/ResourcePicker/index.js +4 -2
- package/lib-commonjs/components/views/CompiledView/index.js +75 -16
- package/lib-commonjs/components/views/ConfigurationView/index.js +93 -34
- package/lib-commonjs/components/views/FilterView/index.js +7 -4
- package/lib-commonjs/components/views/GridView/EditableGridCell.js +232 -0
- package/lib-commonjs/components/views/GridView/GridSelector.js +94 -0
- package/lib-commonjs/components/views/GridView/MultiGridView.js +201 -0
- package/lib-commonjs/components/views/GridView/ResourceGrid.js +237 -0
- package/lib-commonjs/components/views/GridView/SharedContextControls.js +100 -0
- package/lib-commonjs/components/views/GridView/cells/BooleanCell.js +54 -0
- package/lib-commonjs/components/views/GridView/cells/DropdownCell.js +187 -0
- package/lib-commonjs/components/views/GridView/cells/StringCell.js +111 -0
- package/lib-commonjs/components/views/GridView/cells/TriStateCell.js +117 -0
- package/lib-commonjs/components/views/GridView/cells/index.js +18 -0
- package/lib-commonjs/components/views/GridView/index.js +217 -0
- package/lib-commonjs/components/views/ImportView/index.js +22 -19
- package/lib-commonjs/components/views/MessagesWindow/index.js +4 -1
- package/lib-commonjs/components/views/ResolutionView/index.js +8 -5
- package/lib-commonjs/contexts/ObservabilityContext.js +104 -0
- package/lib-commonjs/contexts/index.js +30 -0
- package/lib-commonjs/hooks/useResolutionState.js +849 -245
- package/lib-commonjs/hooks/useResourceData.js +184 -215
- package/lib-commonjs/index.js +15 -1
- package/lib-commonjs/namespaces/GridTools.js +161 -0
- package/lib-commonjs/namespaces/ObservabilityTools.js +33 -0
- package/lib-commonjs/namespaces/ResolutionTools.js +10 -1
- package/lib-commonjs/namespaces/index.js +3 -1
- package/lib-commonjs/types/index.js +10 -0
- package/lib-commonjs/utils/cellValidation.js +253 -0
- package/lib-commonjs/utils/downloadHelper.js +198 -0
- package/lib-commonjs/utils/observability/factories.js +63 -0
- package/lib-commonjs/utils/observability/implementations.js +109 -0
- package/lib-commonjs/utils/observability/index.js +36 -0
- package/lib-commonjs/utils/observability/interfaces.js +24 -0
- package/lib-commonjs/utils/resolutionEditing.js +2 -1
- package/lib-commonjs/utils/resourceSelector.js +200 -0
- package/lib-commonjs/utils/resourceSelectors.js +242 -0
- package/lib-commonjs/utils/tsResIntegration.js +21 -16
- package/lib-commonjs/utils/zipLoader/zipProcessingHelpers.js +7 -5
- package/package.json +7 -7
- package/src/components/common/QualifierContextControl.tsx +0 -338
- package/src/components/common/ResolutionContextOptionsControl.tsx +0 -450
- package/src/components/common/ResolutionResults/index.tsx +0 -481
- package/src/components/common/ResourceListView.tsx +0 -167
- package/src/components/common/ResourcePickerOptionsControl.tsx +0 -351
- package/src/components/common/ResourceTreeView.tsx +0 -417
- package/src/components/common/SourceResourceDetail/index.tsx +0 -493
- package/src/components/forms/HierarchyEditor.tsx +0 -285
- package/src/components/forms/QualifierEditForm.tsx +0 -487
- package/src/components/forms/QualifierTypeEditForm.tsx +0 -458
- package/src/components/forms/ResourceTypeEditForm.tsx +0 -437
- package/src/components/forms/index.ts +0 -11
- package/src/components/orchestrator/ResourceOrchestrator.tsx +0 -444
- package/src/components/pickers/ResourcePicker/README.md +0 -570
- package/src/components/pickers/ResourcePicker/ResourceItem.tsx +0 -127
- package/src/components/pickers/ResourcePicker/ResourcePickerList.tsx +0 -114
- package/src/components/pickers/ResourcePicker/ResourcePickerTree.tsx +0 -461
- package/src/components/pickers/ResourcePicker/index.tsx +0 -234
- package/src/components/pickers/ResourcePicker/types.ts +0 -301
- package/src/components/pickers/ResourcePicker/utils/treeNavigation.ts +0 -210
- package/src/components/views/CompiledView/index.tsx +0 -1342
- package/src/components/views/ConfigurationView/index.tsx +0 -848
- package/src/components/views/FilterView/index.tsx +0 -681
- package/src/components/views/ImportView/index.tsx +0 -789
- package/src/components/views/MessagesWindow/index.tsx +0 -325
- package/src/components/views/ResolutionView/EditableJsonView.tsx +0 -386
- package/src/components/views/ResolutionView/NewResourceModal.tsx +0 -158
- package/src/components/views/ResolutionView/UnifiedChangeControls.tsx +0 -163
- package/src/components/views/ResolutionView/index.tsx +0 -751
- package/src/components/views/SourceView/index.tsx +0 -291
- package/src/hooks/useConfigurationState.ts +0 -436
- package/src/hooks/useFilterState.ts +0 -150
- package/src/hooks/useResolutionState.ts +0 -893
- package/src/hooks/useResourceData.ts +0 -596
- package/src/hooks/useViewState.ts +0 -97
- package/src/index.ts +0 -68
- package/src/namespaces/ConfigurationTools.ts +0 -59
- package/src/namespaces/FilterTools.ts +0 -47
- package/src/namespaces/ImportTools.ts +0 -42
- package/src/namespaces/PickerTools.ts +0 -104
- package/src/namespaces/ResolutionTools.ts +0 -68
- package/src/namespaces/ResourceTools.ts +0 -106
- package/src/namespaces/TsResTools.ts +0 -49
- package/src/namespaces/ViewStateTools.ts +0 -91
- package/src/namespaces/ZipTools.ts +0 -49
- package/src/namespaces/index.ts +0 -49
- package/src/types/index.ts +0 -1273
- package/src/utils/configurationUtils.ts +0 -339
- package/src/utils/fileProcessing.ts +0 -164
- package/src/utils/filterResources.ts +0 -356
- package/src/utils/resolutionEditing.ts +0 -346
- package/src/utils/resolutionUtils.ts +0 -740
- package/src/utils/tsResIntegration.ts +0 -475
- package/src/utils/zipLoader/index.ts +0 -5
- package/src/utils/zipLoader/zipProcessingHelpers.ts +0 -46
- package/src/utils/zipLoader/zipUtils.ts +0 -7
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import React, { useMemo, useCallback, useEffect } from 'react';
|
|
2
|
+
import { TableCellsIcon } from '@heroicons/react/24/outline';
|
|
3
|
+
import { selectResources } from '../../../utils/resourceSelector';
|
|
4
|
+
import { QualifierContextControl } from '../../common/QualifierContextControl';
|
|
5
|
+
import { UnifiedChangeControls } from '../ResolutionView/UnifiedChangeControls';
|
|
6
|
+
import { ResourceGrid } from './ResourceGrid';
|
|
7
|
+
/**
|
|
8
|
+
* GridView component for displaying multiple resources in a tabular format.
|
|
9
|
+
*
|
|
10
|
+
* Provides a grid-based interface for viewing and editing multiple resources
|
|
11
|
+
* simultaneously, with configurable column mappings and shared context management.
|
|
12
|
+
* Leverages the same state management and batch processing as ResolutionView.
|
|
13
|
+
*
|
|
14
|
+
* **Key Features:**
|
|
15
|
+
* - **Multi-resource display**: View multiple resources in rows with configurable columns
|
|
16
|
+
* - **Column mapping**: Host-defined extraction of properties from resolved resources
|
|
17
|
+
* - **Batch editing**: Edit multiple resource values with unified batch application
|
|
18
|
+
* - **Context integration**: Same context management as ResolutionView
|
|
19
|
+
* - **Resource filtering**: Flexible resource selection via built-in and custom selectors
|
|
20
|
+
* - **Change management**: Leverages existing UnifiedChangeControls for batch operations
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { GridView } from '@fgv/ts-res-ui-components';
|
|
25
|
+
*
|
|
26
|
+
* // Define grid configuration
|
|
27
|
+
* const gridConfig = {
|
|
28
|
+
* id: 'user-messages',
|
|
29
|
+
* title: 'User Messages',
|
|
30
|
+
* resourceSelection: { type: 'prefix', prefix: 'user.' },
|
|
31
|
+
* columnMapping: [{
|
|
32
|
+
* resourceType: 'text-resource',
|
|
33
|
+
* columns: [
|
|
34
|
+
* { id: 'text', title: 'Message Text', dataPath: 'text', editable: true },
|
|
35
|
+
* { id: 'locale', title: 'Locale', dataPath: 'locale' }
|
|
36
|
+
* ]
|
|
37
|
+
* }]
|
|
38
|
+
* };
|
|
39
|
+
*
|
|
40
|
+
* function MyGridApp() {
|
|
41
|
+
* return (
|
|
42
|
+
* <GridView
|
|
43
|
+
* gridConfig={gridConfig}
|
|
44
|
+
* resources={processedResources}
|
|
45
|
+
* resolutionState={resolutionState}
|
|
46
|
+
* resolutionActions={resolutionActions}
|
|
47
|
+
* availableQualifiers={['language', 'territory', 'platform']}
|
|
48
|
+
* />
|
|
49
|
+
* );
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export const GridView = ({ gridConfig, resources, resolutionState, resolutionActions, availableQualifiers = [], contextOptions, filterState, filterResult, showContextControls = true, showChangeControls = true, onMessage, className = '' }) => {
|
|
56
|
+
// Use filtered resources when filtering is active and successful
|
|
57
|
+
const isFilteringActive = filterState?.enabled && filterResult?.success === true;
|
|
58
|
+
const baseProcessedResources = isFilteringActive ? filterResult?.processedResources : resources;
|
|
59
|
+
// Select resources for this grid based on the configuration
|
|
60
|
+
const selectedResourceIds = useMemo(() => {
|
|
61
|
+
if (!baseProcessedResources || !gridConfig.resourceSelection) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const selectionResult = selectResources(gridConfig.resourceSelection, baseProcessedResources);
|
|
65
|
+
if (selectionResult.isFailure()) {
|
|
66
|
+
onMessage?.('error', `Resource selection failed: ${selectionResult.message}`);
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return selectionResult.value;
|
|
70
|
+
}, [baseProcessedResources, gridConfig.resourceSelection, onMessage]);
|
|
71
|
+
// Resolve all selected resources with current context
|
|
72
|
+
const resourceResolutions = useMemo(() => {
|
|
73
|
+
if (!resolutionState?.currentResolver || !selectedResourceIds.length) {
|
|
74
|
+
return new Map();
|
|
75
|
+
}
|
|
76
|
+
const resolutions = new Map();
|
|
77
|
+
const context = resolutionState.contextValues;
|
|
78
|
+
selectedResourceIds.forEach((resourceId) => {
|
|
79
|
+
try {
|
|
80
|
+
const resolver = resolutionState.currentResolver;
|
|
81
|
+
const resourceResult = resolver.resourceManager.getBuiltResource(resourceId);
|
|
82
|
+
if (resourceResult.isSuccess()) {
|
|
83
|
+
const resource = resourceResult.value;
|
|
84
|
+
// Resolve the resource with current context
|
|
85
|
+
const resolveResult = resolver.resolveComposedResourceValue(resourceId);
|
|
86
|
+
if (resolveResult.isSuccess()) {
|
|
87
|
+
resolutions.set(resourceId, {
|
|
88
|
+
success: true,
|
|
89
|
+
resourceId,
|
|
90
|
+
resource,
|
|
91
|
+
composedValue: resolveResult.value
|
|
92
|
+
// Note: For grid view, we mainly need the composed value
|
|
93
|
+
// Full candidate analysis is available but not needed for basic grid display
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
resolutions.set(resourceId, {
|
|
98
|
+
success: false,
|
|
99
|
+
resourceId,
|
|
100
|
+
error: resolveResult.message
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
resolutions.set(resourceId, {
|
|
107
|
+
success: false,
|
|
108
|
+
resourceId,
|
|
109
|
+
error: error instanceof Error ? error.message : 'Unknown resolution error'
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return resolutions;
|
|
114
|
+
}, [selectedResourceIds, resolutionState?.currentResolver, resolutionState?.contextValues]);
|
|
115
|
+
// Handle context value changes using the shared pattern from ResolutionView
|
|
116
|
+
const handleQualifierChange = useCallback((qualifierName, value) => {
|
|
117
|
+
// Don't update context if this qualifier is host-managed
|
|
118
|
+
const qualifierOptions = contextOptions?.qualifierOptions?.[qualifierName];
|
|
119
|
+
const isHostManaged = qualifierOptions?.hostValue !== undefined;
|
|
120
|
+
if (!isHostManaged) {
|
|
121
|
+
resolutionActions?.updateContextValue(qualifierName, value);
|
|
122
|
+
}
|
|
123
|
+
}, [resolutionActions, contextOptions?.qualifierOptions]);
|
|
124
|
+
// Apply host-managed values when they change (same pattern as ResolutionView)
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!contextOptions?.hostManagedValues || !resolutionActions?.applyContext)
|
|
127
|
+
return;
|
|
128
|
+
resolutionActions.applyContext(contextOptions.hostManagedValues);
|
|
129
|
+
}, [contextOptions?.hostManagedValues, resolutionActions]);
|
|
130
|
+
// Determine which qualifiers to show
|
|
131
|
+
const visibleQualifiers = useMemo(() => {
|
|
132
|
+
if (!contextOptions?.qualifierOptions) {
|
|
133
|
+
return availableQualifiers;
|
|
134
|
+
}
|
|
135
|
+
return availableQualifiers.filter((qualifierName) => {
|
|
136
|
+
const options = contextOptions.qualifierOptions[qualifierName];
|
|
137
|
+
return options?.visible !== false;
|
|
138
|
+
});
|
|
139
|
+
}, [availableQualifiers, contextOptions?.qualifierOptions]);
|
|
140
|
+
// Get effective context values
|
|
141
|
+
const effectiveContextValues = useMemo(() => {
|
|
142
|
+
return resolutionState?.contextValues || {};
|
|
143
|
+
}, [resolutionState?.contextValues]);
|
|
144
|
+
if (!resources) {
|
|
145
|
+
return (React.createElement("div", { className: `p-6 ${className}` },
|
|
146
|
+
React.createElement("div", { className: "flex items-center space-x-3 mb-6" },
|
|
147
|
+
React.createElement(TableCellsIcon, { className: "h-8 w-8 text-blue-600" }),
|
|
148
|
+
React.createElement("h2", { className: "text-2xl font-bold text-gray-900" }, gridConfig.title || 'Resource Grid')),
|
|
149
|
+
React.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-8 text-center" },
|
|
150
|
+
React.createElement("div", { className: "max-w-2xl mx-auto" },
|
|
151
|
+
React.createElement("h3", { className: "text-xl font-semibold text-gray-900 mb-4" }, "No Resources Loaded"),
|
|
152
|
+
React.createElement("p", { className: "text-gray-600 mb-6" }, "Import resources first to view them in a grid format with customizable columns."),
|
|
153
|
+
React.createElement("div", { className: "bg-blue-50 rounded-lg p-4" },
|
|
154
|
+
React.createElement("p", { className: "text-sm text-blue-800" },
|
|
155
|
+
React.createElement("strong", null, "Grid View:"),
|
|
156
|
+
" Display multiple resources in a table format. Configure columns to extract and edit specific properties from resolved resources."))))));
|
|
157
|
+
}
|
|
158
|
+
return (React.createElement("div", { className: `p-6 ${className}` },
|
|
159
|
+
React.createElement("div", { className: "flex items-center space-x-3 mb-6" },
|
|
160
|
+
React.createElement(TableCellsIcon, { className: "h-8 w-8 text-blue-600" }),
|
|
161
|
+
React.createElement("h2", { className: "text-2xl font-bold text-gray-900" }, gridConfig.title || 'Resource Grid'),
|
|
162
|
+
isFilteringActive && (React.createElement("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800" }, "Filtered")),
|
|
163
|
+
React.createElement("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800" },
|
|
164
|
+
selectedResourceIds.length,
|
|
165
|
+
" resource",
|
|
166
|
+
selectedResourceIds.length !== 1 ? 's' : '')),
|
|
167
|
+
gridConfig.description && React.createElement("p", { className: "text-gray-600 mb-6" }, gridConfig.description),
|
|
168
|
+
React.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-6" },
|
|
169
|
+
showContextControls && contextOptions?.showContextControls !== false && (React.createElement("div", { className: "mb-6" },
|
|
170
|
+
React.createElement("h3", { className: "text-lg font-semibold text-gray-900 mb-4" }, contextOptions?.contextPanelTitle || 'Context Configuration'),
|
|
171
|
+
React.createElement("div", { className: `bg-gray-50 rounded-lg p-4 ${contextOptions?.contextPanelClassName || ''}` },
|
|
172
|
+
React.createElement("div", { className: "mb-4" },
|
|
173
|
+
React.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3" }, visibleQualifiers.map((qualifierName) => {
|
|
174
|
+
const qualifierOptions = contextOptions?.qualifierOptions?.[qualifierName];
|
|
175
|
+
const hostManagedValue = contextOptions?.hostManagedValues?.[qualifierName];
|
|
176
|
+
const globalPlaceholder = typeof contextOptions?.globalPlaceholder === 'function'
|
|
177
|
+
? contextOptions.globalPlaceholder(qualifierName)
|
|
178
|
+
: contextOptions?.globalPlaceholder;
|
|
179
|
+
const mergedOptions = {
|
|
180
|
+
...qualifierOptions,
|
|
181
|
+
hostValue: hostManagedValue !== undefined ? hostManagedValue : qualifierOptions?.hostValue
|
|
182
|
+
};
|
|
183
|
+
return (React.createElement(QualifierContextControl, { key: qualifierName, qualifierName: qualifierName, value: resolutionState?.pendingContextValues[qualifierName], onChange: handleQualifierChange, placeholder: globalPlaceholder || `Enter ${qualifierName} value`, resources: baseProcessedResources, options: mergedOptions }));
|
|
184
|
+
}))),
|
|
185
|
+
contextOptions?.showCurrentContext !== false && (React.createElement("div", { className: "flex items-center justify-between" },
|
|
186
|
+
React.createElement("div", { className: "text-sm text-gray-600" },
|
|
187
|
+
"Current:",
|
|
188
|
+
' ',
|
|
189
|
+
Object.entries(effectiveContextValues)
|
|
190
|
+
.map(([key, value]) => `${key}=${value === undefined ? '(undefined)' : value}`)
|
|
191
|
+
.join(', ')),
|
|
192
|
+
contextOptions?.showContextActions !== false && (React.createElement("div", { className: "flex items-center space-x-2" },
|
|
193
|
+
React.createElement("button", { onClick: resolutionActions?.resetCache, className: "px-3 py-1 text-xs font-medium text-gray-600 bg-gray-100 rounded hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500", title: "Clear resolution cache" }, "Clear Cache"),
|
|
194
|
+
React.createElement("button", { onClick: () => resolutionActions?.applyContext(), disabled: !resolutionState?.hasPendingChanges, className: `px-4 py-2 rounded-md text-sm font-medium ${resolutionState?.hasPendingChanges
|
|
195
|
+
? 'bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500'
|
|
196
|
+
: 'bg-gray-300 text-gray-500 cursor-not-allowed'}` }, resolutionState?.hasPendingChanges
|
|
197
|
+
? 'Apply Changes'
|
|
198
|
+
: resolutionState?.currentResolver
|
|
199
|
+
? 'Context Applied'
|
|
200
|
+
: 'Apply Context')))))))),
|
|
201
|
+
React.createElement(ResourceGrid, { gridConfig: gridConfig, selectedResourceIds: selectedResourceIds, resourceResolutions: resourceResolutions, resolutionActions: resolutionActions, resolutionState: resolutionState, onMessage: onMessage }),
|
|
202
|
+
showChangeControls &&
|
|
203
|
+
(resolutionState?.hasUnsavedEdits || resolutionState?.hasPendingResourceChanges) && (React.createElement("div", { className: "mt-6" },
|
|
204
|
+
React.createElement(UnifiedChangeControls, { editCount: resolutionState?.editedResources?.size || 0, addCount: resolutionState?.pendingResources?.size || 0, deleteCount: resolutionState?.pendingResourceDeletions?.size || 0, isApplying: resolutionState?.isApplyingEdits, disabled: !resolutionState?.currentResolver, onApplyAll: async () => {
|
|
205
|
+
await resolutionActions?.applyPendingResources();
|
|
206
|
+
}, onDiscardAll: () => {
|
|
207
|
+
resolutionActions?.discardEdits?.();
|
|
208
|
+
resolutionActions?.discardPendingResources?.();
|
|
209
|
+
} }))))));
|
|
210
|
+
};
|
|
211
|
+
export default GridView;
|
|
212
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -2,6 +2,7 @@ import React, { useState, useCallback, useRef } from 'react';
|
|
|
2
2
|
import { DocumentArrowUpIcon, CheckCircleIcon, ExclamationTriangleIcon, ArchiveBoxIcon, FolderOpenIcon } from '@heroicons/react/24/outline';
|
|
3
3
|
import { Bundle, ZipArchive } from '@fgv/ts-res';
|
|
4
4
|
import { isZipFile } from '../../../utils/zipLoader';
|
|
5
|
+
import { useObservability } from '../../../contexts';
|
|
5
6
|
/**
|
|
6
7
|
* ImportView component for importing resource files, directories, and bundles.
|
|
7
8
|
*
|
|
@@ -49,7 +50,9 @@ import { isZipFile } from '../../../utils/zipLoader';
|
|
|
49
50
|
*
|
|
50
51
|
* @public
|
|
51
52
|
*/
|
|
52
|
-
export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFileTypes = ['.json', '.zip'], onMessage, className = '' }) => {
|
|
53
|
+
export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFileTypes = ['.json', '.zip'], onMessage, className = '', importError }) => {
|
|
54
|
+
// Get observability context
|
|
55
|
+
const o11y = useObservability();
|
|
53
56
|
const [isLoading, setIsLoading] = useState(false);
|
|
54
57
|
const [importStatus, setImportStatus] = useState({
|
|
55
58
|
hasImported: false,
|
|
@@ -65,7 +68,7 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
65
68
|
const isFileSystemAccessSupported = 'showDirectoryPicker' in window || 'showOpenFilePicker' in window;
|
|
66
69
|
// Helper function to process ZIP files using zip-archive packlet
|
|
67
70
|
const processZipFile = useCallback(async (file) => {
|
|
68
|
-
|
|
71
|
+
o11y.diag.info(`[ImportView] Processing ZIP file: ${file.name}`);
|
|
69
72
|
onMessage?.('info', `Processing ZIP file: ${file.name}`);
|
|
70
73
|
const loader = new ZipArchive.ZipArchiveLoader();
|
|
71
74
|
const loadResult = await loader.loadFromFile(file, {
|
|
@@ -75,7 +78,7 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
75
78
|
throw new Error(`Failed to load ZIP: ${loadResult.message}`);
|
|
76
79
|
}
|
|
77
80
|
const zipData = loadResult.value;
|
|
78
|
-
|
|
81
|
+
o11y.diag.info(`[ImportView] ZIP loaded successfully:`, zipData);
|
|
79
82
|
// Pass the ZIP data to the appropriate handler
|
|
80
83
|
if (onZipImport) {
|
|
81
84
|
// Pass both the directory/files and any configuration found
|
|
@@ -107,7 +110,7 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
107
110
|
const file = files[0];
|
|
108
111
|
// Check if it's a ZIP file first
|
|
109
112
|
if (isZipFile(file.name)) {
|
|
110
|
-
|
|
113
|
+
o11y.diag.info(`[ImportView] ✅ ${file.name} detected as ZIP file`);
|
|
111
114
|
const zipData = await processZipFile(file);
|
|
112
115
|
setImportStatus({
|
|
113
116
|
hasImported: true,
|
|
@@ -137,10 +140,10 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
137
140
|
let isCurrentFileBundle = false;
|
|
138
141
|
try {
|
|
139
142
|
const parsedData = JSON.parse(content);
|
|
140
|
-
|
|
143
|
+
o11y.diag.info(`[ImportView] Checking if ${file.name} is a bundle...`);
|
|
141
144
|
// Use BundleUtils for proper bundle detection
|
|
142
145
|
if (Bundle.BundleUtils.isBundleFile(parsedData)) {
|
|
143
|
-
|
|
146
|
+
o11y.diag.info(`[ImportView] ✅ ${file.name} detected as bundle file`);
|
|
144
147
|
bundleFile = { ...importedFile, bundle: parsedData };
|
|
145
148
|
isCurrentFileBundle = true;
|
|
146
149
|
}
|
|
@@ -149,11 +152,11 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
149
152
|
console.warn(`[ImportView] ⚠️ File ${file.name} appears to be a bundle by name but content doesn't match bundle structure`);
|
|
150
153
|
}
|
|
151
154
|
else {
|
|
152
|
-
|
|
155
|
+
o11y.diag.info(`[ImportView] ❌ ${file.name} is not a bundle file`);
|
|
153
156
|
}
|
|
154
157
|
}
|
|
155
158
|
catch (parseError) {
|
|
156
|
-
|
|
159
|
+
o11y.diag.info(`[ImportView] ❌ ${file.name} failed JSON parsing:`, parseError);
|
|
157
160
|
// Not valid JSON or not a bundle, treat as regular file
|
|
158
161
|
}
|
|
159
162
|
// Only add to regular files if this specific file is not a bundle
|
|
@@ -163,7 +166,7 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
163
166
|
}
|
|
164
167
|
// Process results
|
|
165
168
|
if (bundleFile) {
|
|
166
|
-
|
|
169
|
+
o11y.diag.info(`[ImportView] Processing bundle file: ${bundleFile.name}`, bundleFile.bundle);
|
|
167
170
|
setImportStatus({
|
|
168
171
|
hasImported: true,
|
|
169
172
|
fileCount: 1,
|
|
@@ -173,11 +176,11 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
173
176
|
});
|
|
174
177
|
onMessage?.('info', `Bundle file detected: ${bundleFile.name}`);
|
|
175
178
|
if (onBundleImport && bundleFile.bundle) {
|
|
176
|
-
|
|
179
|
+
o11y.diag.info(`[ImportView] Calling onBundleImport with bundle data`);
|
|
177
180
|
onBundleImport(bundleFile.bundle);
|
|
178
181
|
}
|
|
179
182
|
else {
|
|
180
|
-
|
|
183
|
+
o11y.diag.warn(`[ImportView] No bundle import handler or bundle data missing`);
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
else if (importedFiles.length > 0) {
|
|
@@ -346,7 +349,7 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
346
349
|
if (fileHandles.length === 1) {
|
|
347
350
|
const file = await fileHandles[0].getFile();
|
|
348
351
|
if (isZipFile(file.name)) {
|
|
349
|
-
|
|
352
|
+
o11y.diag.info(`[ImportView] Modern API - ✅ ${file.name} detected as ZIP file`);
|
|
350
353
|
const zipData = await processZipFile(file);
|
|
351
354
|
setImportStatus({
|
|
352
355
|
hasImported: true,
|
|
@@ -379,10 +382,10 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
379
382
|
let isCurrentFileBundle = false;
|
|
380
383
|
try {
|
|
381
384
|
const parsedData = JSON.parse(content);
|
|
382
|
-
|
|
385
|
+
o11y.diag.info(`[ImportView] Modern API - Checking if ${file.name} is a bundle...`);
|
|
383
386
|
// Use BundleUtils for proper bundle detection
|
|
384
387
|
if (Bundle.BundleUtils.isBundleFile(parsedData)) {
|
|
385
|
-
|
|
388
|
+
o11y.diag.info(`[ImportView] Modern API - ✅ ${file.name} detected as bundle file`);
|
|
386
389
|
bundleFile = { ...importedFile, bundle: parsedData };
|
|
387
390
|
isCurrentFileBundle = true;
|
|
388
391
|
}
|
|
@@ -390,11 +393,11 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
390
393
|
console.warn(`[ImportView] Modern API - ⚠️ File ${file.name} appears to be a bundle by name but content doesn't match bundle structure`);
|
|
391
394
|
}
|
|
392
395
|
else {
|
|
393
|
-
|
|
396
|
+
o11y.diag.info(`[ImportView] Modern API - ❌ ${file.name} is not a bundle file`);
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
catch (parseError) {
|
|
397
|
-
|
|
400
|
+
o11y.diag.info(`[ImportView] Modern API - ❌ ${file.name} failed JSON parsing:`, parseError);
|
|
398
401
|
}
|
|
399
402
|
// Only add to regular files if this specific file is not a bundle
|
|
400
403
|
if (!isCurrentFileBundle) {
|
|
@@ -513,13 +516,13 @@ export const ImportView = ({ onImport, onBundleImport, onZipImport, acceptedFile
|
|
|
513
516
|
React.createElement(ArchiveBoxIcon, { className: "w-5 h-5 text-purple-500" }),
|
|
514
517
|
React.createElement("span", { className: "text-sm text-purple-900" }, "ZIP archive processed")))),
|
|
515
518
|
importStatus.hasImported && (React.createElement("button", { onClick: handleReset, className: "mt-4 px-4 py-2 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors" }, "Clear Import"))),
|
|
516
|
-
error && (React.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-red-200 p-6" },
|
|
519
|
+
(error || importError) && (React.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-red-200 p-6" },
|
|
517
520
|
React.createElement("div", { className: "flex items-start space-x-2" },
|
|
518
521
|
React.createElement(ExclamationTriangleIcon, { className: "w-5 h-5 text-red-600 mt-0.5" }),
|
|
519
522
|
React.createElement("div", { className: "text-sm text-red-800" },
|
|
520
523
|
React.createElement("p", { className: "font-medium" }, "Error"),
|
|
521
|
-
React.createElement("p", null, error))))),
|
|
522
|
-
importStatus.hasImported && !error && (React.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-green-200 p-6" },
|
|
524
|
+
React.createElement("p", null, importError || error))))),
|
|
525
|
+
importStatus.hasImported && !error && !importError && (React.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-green-200 p-6" },
|
|
523
526
|
React.createElement("div", { className: "flex items-start space-x-2" },
|
|
524
527
|
React.createElement(CheckCircleIcon, { className: "w-5 h-5 text-green-600 mt-0.5" }),
|
|
525
528
|
React.createElement("div", { className: "text-sm text-green-800" },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useMemo } from 'react';
|
|
2
2
|
import { InformationCircleIcon, ExclamationTriangleIcon, XCircleIcon, CheckCircleIcon, ChevronUpIcon, ChevronDownIcon, FunnelIcon, DocumentDuplicateIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
|
|
3
3
|
import { CheckIcon } from '@heroicons/react/24/solid';
|
|
4
|
+
import { useObservability } from '../../../contexts';
|
|
4
5
|
/**
|
|
5
6
|
* MessagesWindow component for displaying and managing application messages.
|
|
6
7
|
*
|
|
@@ -95,6 +96,8 @@ import { CheckIcon } from '@heroicons/react/24/solid';
|
|
|
95
96
|
* @public
|
|
96
97
|
*/
|
|
97
98
|
export const MessagesWindow = ({ messages, onClearMessages, className = '' }) => {
|
|
99
|
+
// Get observability context
|
|
100
|
+
const o11y = useObservability();
|
|
98
101
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
99
102
|
const [filter, setFilter] = useState('all');
|
|
100
103
|
const [searchTerm, setSearchTerm] = useState('');
|
|
@@ -119,7 +122,7 @@ export const MessagesWindow = ({ messages, onClearMessages, className = '' }) =>
|
|
|
119
122
|
setTimeout(() => setCopySuccess(false), 2000);
|
|
120
123
|
})
|
|
121
124
|
.catch((err) => {
|
|
122
|
-
|
|
125
|
+
o11y.diag.error('Failed to copy messages:', err);
|
|
123
126
|
});
|
|
124
127
|
};
|
|
125
128
|
if (messages.length === 0) {
|
|
@@ -7,6 +7,7 @@ import { ResourcePickerOptionsControl } from '../../common/ResourcePickerOptions
|
|
|
7
7
|
import { ResolutionContextOptionsControl } from '../../common/ResolutionContextOptionsControl';
|
|
8
8
|
import { ResolutionResults } from '../../common/ResolutionResults';
|
|
9
9
|
import { NewResourceModal } from './NewResourceModal';
|
|
10
|
+
import { useObservability } from '../../../contexts';
|
|
10
11
|
/**
|
|
11
12
|
* ResolutionView component for resource resolution testing and editing.
|
|
12
13
|
*
|
|
@@ -57,6 +58,8 @@ import { NewResourceModal } from './NewResourceModal';
|
|
|
57
58
|
* @public
|
|
58
59
|
*/
|
|
59
60
|
export const ResolutionView = ({ resources, filterState, filterResult, resolutionState, resolutionActions, availableQualifiers = [], resourceEditorFactory, onMessage, pickerOptions, pickerOptionsPresentation = 'hidden', contextOptions, lockedViewMode, sectionTitles, allowResourceCreation = false, defaultResourceType, resourceTypeFactory, onPendingResourcesApplied, showPendingResourcesInList = true, className = '' }) => {
|
|
61
|
+
// Get observability context
|
|
62
|
+
const o11y = useObservability();
|
|
60
63
|
// State for picker options control
|
|
61
64
|
const [currentPickerOptions, setCurrentPickerOptions] = useState(pickerOptions || {});
|
|
62
65
|
// State for context options control
|
|
@@ -209,10 +212,10 @@ export const ResolutionView = ({ resources, filterState, filterResult, resolutio
|
|
|
209
212
|
? currentContextOptions.hostManagedValues
|
|
210
213
|
: contextOptions?.hostManagedValues
|
|
211
214
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
215
|
+
o11y.diag.info('ResolutionView - effectiveContextOptions:', merged);
|
|
216
|
+
o11y.diag.info('ResolutionView - contextOptions hostManagedValues:', contextOptions?.hostManagedValues);
|
|
217
|
+
o11y.diag.info('ResolutionView - currentContextOptions hostManagedValues:', currentContextOptions?.hostManagedValues);
|
|
218
|
+
o11y.diag.info('ResolutionView - final hostManagedValues:', merged.hostManagedValues);
|
|
216
219
|
return merged;
|
|
217
220
|
}, [contextOptions, currentContextOptions]);
|
|
218
221
|
// Handle context value changes using the shared component's callback pattern
|
|
@@ -304,7 +307,7 @@ export const ResolutionView = ({ resources, filterState, filterResult, resolutio
|
|
|
304
307
|
}, [resolutionActions]);
|
|
305
308
|
// Handle new resource creation
|
|
306
309
|
const handleStartNewResource = useCallback(() => {
|
|
307
|
-
resolutionActions?.startNewResource(defaultResourceType);
|
|
310
|
+
resolutionActions?.startNewResource({ defaultTypeName: defaultResourceType });
|
|
308
311
|
setShowNewResourceModal(true);
|
|
309
312
|
}, [resolutionActions]);
|
|
310
313
|
const handleCloseNewResourceModal = useCallback(() => {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import * as ObservabilityTools from '../utils/observability';
|
|
3
|
+
/**
|
|
4
|
+
* React context for observability infrastructure.
|
|
5
|
+
* Provides access to diagnostic and user loggers throughout the component tree.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export declare const ObservabilityContext: React.Context<ObservabilityTools.IObservabilityContext>;
|
|
9
|
+
/**
|
|
10
|
+
* Props for the ObservabilityProvider component.
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export interface ObservabilityProviderProps {
|
|
14
|
+
/** Child components that will have access to the observability context */
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
/** Optional observability context to provide (defaults to console-based context) */
|
|
17
|
+
observabilityContext?: ObservabilityTools.IObservabilityContext;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Provider component that makes observability context available to all child components.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* // Basic usage with default console logging
|
|
25
|
+
* <ObservabilityProvider>
|
|
26
|
+
* <MyApp />
|
|
27
|
+
* </ObservabilityProvider>
|
|
28
|
+
*
|
|
29
|
+
* // Custom observability context
|
|
30
|
+
* const customContext = ObservabilityTools.createConsoleObservabilityContext('debug', 'info');
|
|
31
|
+
* <ObservabilityProvider observabilityContext={customContext}>
|
|
32
|
+
* <MyApp />
|
|
33
|
+
* </ObservabilityProvider>
|
|
34
|
+
*
|
|
35
|
+
* // With custom user logger that forwards to app's message system
|
|
36
|
+
* const contextWithMessages = new ObservabilityTools.ObservabilityContext(
|
|
37
|
+
* new ObservabilityTools.ConsoleUserLogger('info'),
|
|
38
|
+
* createCallbackUserLogger((type, message) => showToast(type, message))
|
|
39
|
+
* );
|
|
40
|
+
* <ObservabilityProvider observabilityContext={contextWithMessages}>
|
|
41
|
+
* <MyApp />
|
|
42
|
+
* </ObservabilityProvider>
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param props - Provider configuration
|
|
46
|
+
* @returns JSX provider element
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
export declare const ObservabilityProvider: React.FC<ObservabilityProviderProps>;
|
|
50
|
+
/**
|
|
51
|
+
* Hook to access the current observability context.
|
|
52
|
+
*
|
|
53
|
+
* Provides access to both diagnostic logging (for developers/debugging) and
|
|
54
|
+
* user logging (for user-facing messages and feedback).
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* function MyComponent() {
|
|
59
|
+
* const observability = useObservability();
|
|
60
|
+
*
|
|
61
|
+
* const handleAction = () => {
|
|
62
|
+
* // Log diagnostic info for developers
|
|
63
|
+
* observability.diag.info('User clicked action button');
|
|
64
|
+
*
|
|
65
|
+
* try {
|
|
66
|
+
* performAction();
|
|
67
|
+
* // Show success message to user
|
|
68
|
+
* observability.user.success('Action completed successfully!');
|
|
69
|
+
* } catch (error) {
|
|
70
|
+
* // Log error for debugging
|
|
71
|
+
* observability.diag.error('Action failed:', error);
|
|
72
|
+
* // Show error to user
|
|
73
|
+
* observability.user.error('Action failed. Please try again.');
|
|
74
|
+
* }
|
|
75
|
+
* };
|
|
76
|
+
*
|
|
77
|
+
* return <button onClick={handleAction}>Perform Action</button>;
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @returns The current observability context with diag and user loggers
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
export declare const useObservability: () => ObservabilityTools.IObservabilityContext;
|
|
85
|
+
//# sourceMappingURL=ObservabilityContext.d.ts.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Erik Fortune
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
import React, { createContext, useContext } from 'react';
|
|
23
|
+
import * as ObservabilityTools from '../utils/observability';
|
|
24
|
+
/**
|
|
25
|
+
* React context for observability infrastructure.
|
|
26
|
+
* Provides access to diagnostic and user loggers throughout the component tree.
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
export const ObservabilityContext = createContext(ObservabilityTools.DefaultObservabilityContext);
|
|
30
|
+
/**
|
|
31
|
+
* Provider component that makes observability context available to all child components.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* // Basic usage with default console logging
|
|
36
|
+
* <ObservabilityProvider>
|
|
37
|
+
* <MyApp />
|
|
38
|
+
* </ObservabilityProvider>
|
|
39
|
+
*
|
|
40
|
+
* // Custom observability context
|
|
41
|
+
* const customContext = ObservabilityTools.createConsoleObservabilityContext('debug', 'info');
|
|
42
|
+
* <ObservabilityProvider observabilityContext={customContext}>
|
|
43
|
+
* <MyApp />
|
|
44
|
+
* </ObservabilityProvider>
|
|
45
|
+
*
|
|
46
|
+
* // With custom user logger that forwards to app's message system
|
|
47
|
+
* const contextWithMessages = new ObservabilityTools.ObservabilityContext(
|
|
48
|
+
* new ObservabilityTools.ConsoleUserLogger('info'),
|
|
49
|
+
* createCallbackUserLogger((type, message) => showToast(type, message))
|
|
50
|
+
* );
|
|
51
|
+
* <ObservabilityProvider observabilityContext={contextWithMessages}>
|
|
52
|
+
* <MyApp />
|
|
53
|
+
* </ObservabilityProvider>
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param props - Provider configuration
|
|
57
|
+
* @returns JSX provider element
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
export const ObservabilityProvider = ({ children, observabilityContext = ObservabilityTools.DefaultObservabilityContext }) => React.createElement(ObservabilityContext.Provider, { value: observabilityContext }, children);
|
|
61
|
+
/**
|
|
62
|
+
* Hook to access the current observability context.
|
|
63
|
+
*
|
|
64
|
+
* Provides access to both diagnostic logging (for developers/debugging) and
|
|
65
|
+
* user logging (for user-facing messages and feedback).
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* function MyComponent() {
|
|
70
|
+
* const observability = useObservability();
|
|
71
|
+
*
|
|
72
|
+
* const handleAction = () => {
|
|
73
|
+
* // Log diagnostic info for developers
|
|
74
|
+
* observability.diag.info('User clicked action button');
|
|
75
|
+
*
|
|
76
|
+
* try {
|
|
77
|
+
* performAction();
|
|
78
|
+
* // Show success message to user
|
|
79
|
+
* observability.user.success('Action completed successfully!');
|
|
80
|
+
* } catch (error) {
|
|
81
|
+
* // Log error for debugging
|
|
82
|
+
* observability.diag.error('Action failed:', error);
|
|
83
|
+
* // Show error to user
|
|
84
|
+
* observability.user.error('Action failed. Please try again.');
|
|
85
|
+
* }
|
|
86
|
+
* };
|
|
87
|
+
*
|
|
88
|
+
* return <button onClick={handleAction}>Perform Action</button>;
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @returns The current observability context with diag and user loggers
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
export const useObservability = () => {
|
|
96
|
+
return useContext(ObservabilityContext);
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=ObservabilityContext.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Erik Fortune
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
* furnished to do so, subject to the following conditions:
|
|
10
|
+
*
|
|
11
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
* copies or substantial portions of the Software.
|
|
13
|
+
*
|
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
* SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
// Export observability context components and hooks
|
|
23
|
+
export { ObservabilityContext, ObservabilityProvider, useObservability } from './ObservabilityContext';
|
|
24
|
+
//# sourceMappingURL=index.js.map
|