@open-mercato/ui 0.6.6-develop.6465.1.019f0fb26f → 0.6.6-develop.6471.1.9ab5bdd79a
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/dist/ai/AiChat.js +3 -3
- package/dist/ai/AiChat.js.map +2 -2
- package/dist/backend/CrudForm.js +19 -17
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +5 -5
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/JsonDisplay.js +3 -1
- package/dist/backend/JsonDisplay.js.map +2 -2
- package/dist/backend/confirm-dialog/useConfirmDialog.js +3 -3
- package/dist/backend/confirm-dialog/useConfirmDialog.js.map +2 -2
- package/dist/backend/dashboard/DashboardScreen.js +8 -6
- package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
- package/dist/backend/dashboard/widgetRegistry.js +3 -1
- package/dist/backend/dashboard/widgetRegistry.js.map +2 -2
- package/dist/backend/detail/CustomDataSection.js +4 -2
- package/dist/backend/detail/CustomDataSection.js.map +2 -2
- package/dist/backend/detail/InlineEditors.js +3 -1
- package/dist/backend/detail/InlineEditors.js.map +2 -2
- package/dist/backend/detail/TagsSection.js +3 -1
- package/dist/backend/detail/TagsSection.js.map +2 -2
- package/dist/backend/injection/InjectionSpot.js +10 -8
- package/dist/backend/injection/InjectionSpot.js.map +2 -2
- package/dist/backend/injection/eventBridge.js +3 -1
- package/dist/backend/injection/eventBridge.js.map +2 -2
- package/dist/backend/injection/useGuardedMutation.js +3 -1
- package/dist/backend/injection/useGuardedMutation.js.map +2 -2
- package/dist/backend/injection/useInjectionDataWidgets.js +3 -1
- package/dist/backend/injection/useInjectionDataWidgets.js.map +2 -2
- package/dist/backend/injection/useRegisteredComponent.js +6 -9
- package/dist/backend/injection/useRegisteredComponent.js.map +2 -2
- package/dist/backend/injection/widgetRegistry.js +3 -1
- package/dist/backend/injection/widgetRegistry.js.map +2 -2
- package/dist/backend/inputs/LookupSelect.js +3 -1
- package/dist/backend/inputs/LookupSelect.js.map +2 -2
- package/dist/backend/notifications/NotificationDispatcher.js +4 -2
- package/dist/backend/notifications/NotificationDispatcher.js.map +2 -2
- package/dist/backend/sidebar/SidebarCustomizationEditor.js +7 -5
- package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
- package/dist/backend/utils/api.js +3 -1
- package/dist/backend/utils/api.js.map +2 -2
- package/dist/backend/utils/bulkDelete.js +4 -2
- package/dist/backend/utils/bulkDelete.js.map +2 -2
- package/dist/backend/version-history/VersionHistoryPanel.js +4 -2
- package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
- package/dist/portal/hooks/usePortalDashboardWidgets.js +3 -1
- package/dist/portal/hooks/usePortalDashboardWidgets.js.map +2 -2
- package/dist/portal/hooks/usePortalEventBridge.js +3 -1
- package/dist/portal/hooks/usePortalEventBridge.js.map +2 -2
- package/dist/primitives/Notice.js +3 -3
- package/dist/primitives/Notice.js.map +2 -2
- package/dist/theme/ThemeProvider.js +4 -2
- package/dist/theme/ThemeProvider.js.map +2 -2
- package/package.json +3 -3
- package/src/ai/AiChat.tsx +4 -4
- package/src/backend/CrudForm.tsx +20 -17
- package/src/backend/DataTable.tsx +6 -9
- package/src/backend/JsonDisplay.tsx +4 -1
- package/src/backend/__tests__/component-replacement.test.tsx +1 -1
- package/src/backend/confirm-dialog/useConfirmDialog.tsx +4 -3
- package/src/backend/dashboard/DashboardScreen.tsx +9 -6
- package/src/backend/dashboard/widgetRegistry.ts +4 -1
- package/src/backend/detail/CustomDataSection.tsx +5 -2
- package/src/backend/detail/InlineEditors.tsx +4 -1
- package/src/backend/detail/TagsSection.tsx +4 -1
- package/src/backend/injection/InjectionSpot.tsx +11 -8
- package/src/backend/injection/eventBridge.ts +4 -1
- package/src/backend/injection/useGuardedMutation.ts +4 -1
- package/src/backend/injection/useInjectionDataWidgets.ts +4 -1
- package/src/backend/injection/useRegisteredComponent.tsx +7 -9
- package/src/backend/injection/widgetRegistry.ts +4 -1
- package/src/backend/inputs/LookupSelect.tsx +4 -1
- package/src/backend/notifications/NotificationDispatcher.ts +5 -2
- package/src/backend/sidebar/SidebarCustomizationEditor.tsx +8 -5
- package/src/backend/utils/__tests__/bulkDelete.test.ts +29 -23
- package/src/backend/utils/api.ts +4 -2
- package/src/backend/utils/bulkDelete.ts +5 -2
- package/src/backend/version-history/VersionHistoryPanel.tsx +5 -2
- package/src/portal/hooks/usePortalDashboardWidgets.ts +4 -1
- package/src/portal/hooks/usePortalEventBridge.ts +4 -1
- package/src/primitives/Notice.tsx +4 -4
- package/src/theme/ThemeProvider.tsx +5 -2
|
@@ -10,6 +10,9 @@ import type {
|
|
|
10
10
|
NotificationHandlerToastOptions,
|
|
11
11
|
} from '@open-mercato/shared/modules/notifications/handler'
|
|
12
12
|
import type { NotificationDto } from '@open-mercato/shared/modules/notifications/types'
|
|
13
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
14
|
+
|
|
15
|
+
const logger = createLogger('ui').child({ component: 'notifications' })
|
|
13
16
|
|
|
14
17
|
type RuntimeContext = {
|
|
15
18
|
userId?: string
|
|
@@ -116,7 +119,7 @@ class NotificationDispatcher {
|
|
|
116
119
|
try {
|
|
117
120
|
void Promise.resolve(handler.handle(notification, context))
|
|
118
121
|
} catch (error) {
|
|
119
|
-
|
|
122
|
+
logger.error('Notification handler failed', { handlerId: handler.id, err: error })
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -126,7 +129,7 @@ class NotificationDispatcher {
|
|
|
126
129
|
try {
|
|
127
130
|
listener.effect(notification)
|
|
128
131
|
} catch (error) {
|
|
129
|
-
|
|
132
|
+
logger.error('Notification effect listener failed', { err: error })
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
@@ -42,6 +42,9 @@ import {
|
|
|
42
42
|
type SidebarGroup,
|
|
43
43
|
type SidebarItem,
|
|
44
44
|
} from './customization-helpers'
|
|
45
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
46
|
+
|
|
47
|
+
const logger = createLogger('ui').child({ component: 'SidebarCustomizationEditor' })
|
|
45
48
|
|
|
46
49
|
export type SidebarCustomizationEditorProps = {
|
|
47
50
|
onSaved?: () => void
|
|
@@ -358,7 +361,7 @@ export function SidebarCustomizationEditor({
|
|
|
358
361
|
selectVariantInternal(initial, list)
|
|
359
362
|
setSelectedRoleIds([])
|
|
360
363
|
} catch (err) {
|
|
361
|
-
|
|
364
|
+
logger.error('Failed to load sidebar variants', { err })
|
|
362
365
|
setError(t('appShell.sidebarCustomizationLoadError'))
|
|
363
366
|
} finally {
|
|
364
367
|
setLoading(false)
|
|
@@ -437,7 +440,7 @@ export function SidebarCustomizationEditor({
|
|
|
437
440
|
flash(t('appShell.sidebarCustomizationVariantCreated', 'Variant created.'), 'success')
|
|
438
441
|
return { ok: true }
|
|
439
442
|
} catch (err) {
|
|
440
|
-
|
|
443
|
+
logger.error('Failed to create sidebar variant', { err })
|
|
441
444
|
const message = t('appShell.sidebarCustomizationSaveError')
|
|
442
445
|
if (!options.suppressPageError) setError(message)
|
|
443
446
|
return { ok: false, error: message }
|
|
@@ -763,7 +766,7 @@ export function SidebarCustomizationEditor({
|
|
|
763
766
|
)
|
|
764
767
|
onSaved?.()
|
|
765
768
|
} catch (err) {
|
|
766
|
-
|
|
769
|
+
logger.error('Failed to save sidebar variant', { err })
|
|
767
770
|
setError(t('appShell.sidebarCustomizationSaveError'))
|
|
768
771
|
} finally {
|
|
769
772
|
setSaving(false)
|
|
@@ -794,7 +797,7 @@ export function SidebarCustomizationEditor({
|
|
|
794
797
|
const fresh = list.find((v) => v.id === selectedVariant.id) ?? selectedVariant
|
|
795
798
|
selectVariantInternal(fresh, list)
|
|
796
799
|
} catch (err) {
|
|
797
|
-
|
|
800
|
+
logger.error('Failed to toggle variant active state', { err })
|
|
798
801
|
setError(t('appShell.sidebarCustomizationSaveError'))
|
|
799
802
|
}
|
|
800
803
|
}, [selectedVariant, saving, deleting, variantsApiPath, t, loadVariantsList, selectVariantInternal, runMutation, buildMutationContext])
|
|
@@ -831,7 +834,7 @@ export function SidebarCustomizationEditor({
|
|
|
831
834
|
const fallback = list[0] ?? null
|
|
832
835
|
selectVariantInternal(fallback, list)
|
|
833
836
|
} catch (err) {
|
|
834
|
-
|
|
837
|
+
logger.error('Failed to delete variant', { err })
|
|
835
838
|
setError(t('appShell.sidebarCustomizationSaveError'))
|
|
836
839
|
} finally {
|
|
837
840
|
setDeleting(false)
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { groupBulkDeleteFailures, runBulkDelete, type BulkDeleteFailure } from '../bulkDelete'
|
|
2
2
|
import { emitProgressUpdate } from '@open-mercato/shared/lib/frontend/progressEvents'
|
|
3
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
3
4
|
|
|
4
5
|
jest.mock('@open-mercato/shared/lib/frontend/progressEvents', () => ({
|
|
5
6
|
emitProgressUpdate: jest.fn(),
|
|
6
7
|
}))
|
|
7
8
|
|
|
9
|
+
jest.mock('@open-mercato/shared/lib/logger', () => {
|
|
10
|
+
const mocked = {
|
|
11
|
+
debug: jest.fn(),
|
|
12
|
+
info: jest.fn(),
|
|
13
|
+
warn: jest.fn(),
|
|
14
|
+
error: jest.fn(),
|
|
15
|
+
child: jest.fn(),
|
|
16
|
+
}
|
|
17
|
+
mocked.child.mockImplementation(() => mocked)
|
|
18
|
+
return { createLogger: jest.fn(() => mocked) }
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const loggerWarn = createLogger('ui').warn as jest.Mock
|
|
22
|
+
|
|
8
23
|
describe('runBulkDelete', () => {
|
|
9
24
|
beforeEach(() => {
|
|
10
25
|
jest.mocked(emitProgressUpdate).mockClear()
|
|
26
|
+
loggerWarn.mockClear()
|
|
11
27
|
})
|
|
12
28
|
|
|
13
29
|
it('captures error message and code from thrown error', async () => {
|
|
@@ -48,32 +64,22 @@ describe('runBulkDelete', () => {
|
|
|
48
64
|
expect(failures[0].code).toBeNull()
|
|
49
65
|
})
|
|
50
66
|
|
|
51
|
-
it('logs failures
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
expect(tag).toBe('[customers.companies.list] bulk delete failed')
|
|
62
|
-
expect(payload).toMatchObject({ id: 'x', code: 'COMPANY_HAS_DEPENDENTS' })
|
|
63
|
-
} finally {
|
|
64
|
-
warnSpy.mockRestore()
|
|
65
|
-
}
|
|
67
|
+
it('logs failures when logTag is provided', async () => {
|
|
68
|
+
const err = Object.assign(new Error('linked deals (2).'), { code: 'COMPANY_HAS_DEPENDENTS' })
|
|
69
|
+
const deleteOne = jest.fn().mockRejectedValue(err)
|
|
70
|
+
|
|
71
|
+
await runBulkDelete([{ id: 'x' }], deleteOne, { logTag: 'customers.companies.list' })
|
|
72
|
+
|
|
73
|
+
expect(loggerWarn).toHaveBeenCalledTimes(1)
|
|
74
|
+
const [message, fields] = loggerWarn.mock.calls[0]
|
|
75
|
+
expect(message).toBe('Bulk delete failed')
|
|
76
|
+
expect(fields).toMatchObject({ logTag: 'customers.companies.list', id: 'x', code: 'COMPANY_HAS_DEPENDENTS' })
|
|
66
77
|
})
|
|
67
78
|
|
|
68
79
|
it('does not log when logTag is omitted', async () => {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
await runBulkDelete([{ id: 'x' }], deleteOne)
|
|
73
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
74
|
-
} finally {
|
|
75
|
-
warnSpy.mockRestore()
|
|
76
|
-
}
|
|
80
|
+
const deleteOne = jest.fn().mockRejectedValue(new Error('boom'))
|
|
81
|
+
await runBulkDelete([{ id: 'x' }], deleteOne)
|
|
82
|
+
expect(loggerWarn).not.toHaveBeenCalled()
|
|
77
83
|
})
|
|
78
84
|
|
|
79
85
|
it('emits client progress events when progress metadata is provided', async () => {
|
package/src/backend/utils/api.ts
CHANGED
|
@@ -7,6 +7,9 @@ import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
|
|
|
7
7
|
import { pushOperation } from '../operations/store'
|
|
8
8
|
import { pushPartialIndexWarning } from '../indexes/store'
|
|
9
9
|
import { createScopedHeaderStack } from './scopedHeaderStack'
|
|
10
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
11
|
+
|
|
12
|
+
const logger = createLogger('ui').child({ component: 'apiFetch' })
|
|
10
13
|
|
|
11
14
|
const scopedHeaders = createScopedHeaderStack()
|
|
12
15
|
|
|
@@ -195,8 +198,7 @@ export async function apiFetch(input: RequestInfo | URL, init?: RequestInit): Pr
|
|
|
195
198
|
? input.url
|
|
196
199
|
: 'unknown'
|
|
197
200
|
try {
|
|
198
|
-
|
|
199
|
-
console.warn('[apiFetch] Forbidden response', {
|
|
201
|
+
logger.warn('Forbidden response', {
|
|
200
202
|
url: target,
|
|
201
203
|
status: res.status,
|
|
202
204
|
requiredRoles: roles,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import { emitProgressUpdate } from '@open-mercato/shared/lib/frontend/progressEvents'
|
|
4
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
5
|
+
|
|
6
|
+
const logger = createLogger('ui')
|
|
4
7
|
|
|
5
8
|
export type BulkDeleteFailure = {
|
|
6
9
|
id: string
|
|
@@ -87,8 +90,8 @@ export async function runBulkDelete<T extends { id: string }>(
|
|
|
87
90
|
err && typeof err === 'object' ? (err as { code?: unknown }).code : undefined
|
|
88
91
|
const code = typeof rawCode === 'string' && rawCode.length > 0 ? rawCode : null
|
|
89
92
|
failures.push({ id: row.id, code, message })
|
|
90
|
-
if (logTag
|
|
91
|
-
|
|
93
|
+
if (logTag) {
|
|
94
|
+
logger.warn('Bulk delete failed', { logTag, id: row.id, code, message, err })
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
if (progress && progressJobId) {
|
|
@@ -13,6 +13,9 @@ import { getVersionHistoryActionLabel, getVersionHistoryStatusLabel } from './la
|
|
|
13
13
|
import { useAuditPermissions, canUndoEntry, canRedoEntry } from './useAuditPermissions'
|
|
14
14
|
import { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'
|
|
15
15
|
import { humanizeResourceKind } from './labels'
|
|
16
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
17
|
+
|
|
18
|
+
const logger = createLogger('ui')
|
|
16
19
|
|
|
17
20
|
export type VersionHistoryPanelProps = {
|
|
18
21
|
open: boolean
|
|
@@ -107,7 +110,7 @@ export function VersionHistoryPanel({
|
|
|
107
110
|
window.location.reload()
|
|
108
111
|
}
|
|
109
112
|
} catch (err) {
|
|
110
|
-
|
|
113
|
+
logger.error('Undo failed', { err })
|
|
111
114
|
} finally {
|
|
112
115
|
setUndoingToken(null)
|
|
113
116
|
}
|
|
@@ -127,7 +130,7 @@ export function VersionHistoryPanel({
|
|
|
127
130
|
window.location.reload()
|
|
128
131
|
}
|
|
129
132
|
} catch (err) {
|
|
130
|
-
|
|
133
|
+
logger.error('Redo failed', { err })
|
|
131
134
|
} finally {
|
|
132
135
|
setRedoingId(null)
|
|
133
136
|
}
|
|
@@ -5,6 +5,9 @@ import type { InjectionSpotId } from '@open-mercato/shared/modules/widgets/injec
|
|
|
5
5
|
import { loadInjectionWidgetsForSpot, type LoadedInjectionWidget } from '@open-mercato/shared/modules/widgets/injection-loader'
|
|
6
6
|
import { hasAllFeatures } from '@open-mercato/shared/security/features'
|
|
7
7
|
import { apiCall } from '../../backend/utils/apiCall'
|
|
8
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('ui').child({ component: 'usePortalDashboardWidgets' })
|
|
8
11
|
|
|
9
12
|
type PortalFeatureCheckResponse = {
|
|
10
13
|
ok: boolean
|
|
@@ -76,7 +79,7 @@ export function usePortalDashboardWidgets(spotId: InjectionSpotId): {
|
|
|
76
79
|
setGrantedFeatures(granted)
|
|
77
80
|
} catch (loadError) {
|
|
78
81
|
if (!mounted) return
|
|
79
|
-
|
|
82
|
+
logger.error('Failed to load widgets for spot', { spotId, err: loadError })
|
|
80
83
|
setError(loadError instanceof Error ? loadError.message : String(loadError))
|
|
81
84
|
setWidgets([])
|
|
82
85
|
} finally {
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { useEffect, useRef } from 'react'
|
|
3
3
|
import type { AppEventPayload } from '@open-mercato/shared/modules/widgets/injection'
|
|
4
4
|
import { PORTAL_EVENT_DOM_NAME } from './usePortalAppEvent'
|
|
5
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
6
|
+
|
|
7
|
+
const logger = createLogger('ui').child({ component: 'PortalEventBridge' })
|
|
5
8
|
|
|
6
9
|
const PORTAL_SSE_ENDPOINT = '/api/customer_accounts/portal/events/stream'
|
|
7
10
|
const HEARTBEAT_TIMEOUT = 45_000
|
|
@@ -58,7 +61,7 @@ export function usePortalEventBridge(): void {
|
|
|
58
61
|
function resetHeartbeatTimer() {
|
|
59
62
|
if (heartbeatTimer.current) clearTimeout(heartbeatTimer.current)
|
|
60
63
|
heartbeatTimer.current = setTimeout(() => {
|
|
61
|
-
|
|
64
|
+
logger.warn('Heartbeat timeout — reconnecting')
|
|
62
65
|
disconnect()
|
|
63
66
|
scheduleReconnect()
|
|
64
67
|
}, HEARTBEAT_TIMEOUT)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
4
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
5
|
+
|
|
6
|
+
const logger = createLogger('ui')
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* @deprecated Use <Alert variant="destructive|warning|info"> instead.
|
|
@@ -48,10 +51,7 @@ export function Notice({
|
|
|
48
51
|
compact = false,
|
|
49
52
|
}: NoticeProps) {
|
|
50
53
|
if (process.env.NODE_ENV === 'development') {
|
|
51
|
-
|
|
52
|
-
'[DS] <Notice> is deprecated. Use <Alert variant="destructive|warning|info"> instead. ' +
|
|
53
|
-
'See: docs/design-system/migration-tables.md'
|
|
54
|
-
)
|
|
54
|
+
logger.warn('<Notice> is deprecated. Use <Alert variant="destructive|warning|info"> instead. See: docs/design-system/migration-tables.md')
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
const styles = variantStyles[variant]
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { createContext, useContext } from 'react'
|
|
5
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
6
|
+
|
|
7
|
+
const logger = createLogger('ui').child({ component: 'ThemeProvider' })
|
|
5
8
|
|
|
6
9
|
export type Theme = 'light' | 'dark' | 'system'
|
|
7
10
|
|
|
@@ -31,7 +34,7 @@ function getStoredTheme(): Theme {
|
|
|
31
34
|
// localStorage may be unavailable in private browsing, iframes, or restricted contexts
|
|
32
35
|
// Theme will default to system preference - this is expected graceful degradation
|
|
33
36
|
if (process.env.NODE_ENV === 'development') {
|
|
34
|
-
|
|
37
|
+
logger.warn('localStorage read failed', { err: error })
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
return 'system'
|
|
@@ -85,7 +88,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
|
85
88
|
} catch (error) {
|
|
86
89
|
// localStorage may be unavailable - theme still works for this session, just won't persist
|
|
87
90
|
if (process.env.NODE_ENV === 'development') {
|
|
88
|
-
|
|
91
|
+
logger.warn('localStorage write failed', { err: error })
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
const resolved = newTheme === 'system' ? getSystemTheme() : newTheme
|