@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.
Files changed (81) hide show
  1. package/dist/ai/AiChat.js +3 -3
  2. package/dist/ai/AiChat.js.map +2 -2
  3. package/dist/backend/CrudForm.js +19 -17
  4. package/dist/backend/CrudForm.js.map +2 -2
  5. package/dist/backend/DataTable.js +5 -5
  6. package/dist/backend/DataTable.js.map +2 -2
  7. package/dist/backend/JsonDisplay.js +3 -1
  8. package/dist/backend/JsonDisplay.js.map +2 -2
  9. package/dist/backend/confirm-dialog/useConfirmDialog.js +3 -3
  10. package/dist/backend/confirm-dialog/useConfirmDialog.js.map +2 -2
  11. package/dist/backend/dashboard/DashboardScreen.js +8 -6
  12. package/dist/backend/dashboard/DashboardScreen.js.map +2 -2
  13. package/dist/backend/dashboard/widgetRegistry.js +3 -1
  14. package/dist/backend/dashboard/widgetRegistry.js.map +2 -2
  15. package/dist/backend/detail/CustomDataSection.js +4 -2
  16. package/dist/backend/detail/CustomDataSection.js.map +2 -2
  17. package/dist/backend/detail/InlineEditors.js +3 -1
  18. package/dist/backend/detail/InlineEditors.js.map +2 -2
  19. package/dist/backend/detail/TagsSection.js +3 -1
  20. package/dist/backend/detail/TagsSection.js.map +2 -2
  21. package/dist/backend/injection/InjectionSpot.js +10 -8
  22. package/dist/backend/injection/InjectionSpot.js.map +2 -2
  23. package/dist/backend/injection/eventBridge.js +3 -1
  24. package/dist/backend/injection/eventBridge.js.map +2 -2
  25. package/dist/backend/injection/useGuardedMutation.js +3 -1
  26. package/dist/backend/injection/useGuardedMutation.js.map +2 -2
  27. package/dist/backend/injection/useInjectionDataWidgets.js +3 -1
  28. package/dist/backend/injection/useInjectionDataWidgets.js.map +2 -2
  29. package/dist/backend/injection/useRegisteredComponent.js +6 -9
  30. package/dist/backend/injection/useRegisteredComponent.js.map +2 -2
  31. package/dist/backend/injection/widgetRegistry.js +3 -1
  32. package/dist/backend/injection/widgetRegistry.js.map +2 -2
  33. package/dist/backend/inputs/LookupSelect.js +3 -1
  34. package/dist/backend/inputs/LookupSelect.js.map +2 -2
  35. package/dist/backend/notifications/NotificationDispatcher.js +4 -2
  36. package/dist/backend/notifications/NotificationDispatcher.js.map +2 -2
  37. package/dist/backend/sidebar/SidebarCustomizationEditor.js +7 -5
  38. package/dist/backend/sidebar/SidebarCustomizationEditor.js.map +2 -2
  39. package/dist/backend/utils/api.js +3 -1
  40. package/dist/backend/utils/api.js.map +2 -2
  41. package/dist/backend/utils/bulkDelete.js +4 -2
  42. package/dist/backend/utils/bulkDelete.js.map +2 -2
  43. package/dist/backend/version-history/VersionHistoryPanel.js +4 -2
  44. package/dist/backend/version-history/VersionHistoryPanel.js.map +2 -2
  45. package/dist/portal/hooks/usePortalDashboardWidgets.js +3 -1
  46. package/dist/portal/hooks/usePortalDashboardWidgets.js.map +2 -2
  47. package/dist/portal/hooks/usePortalEventBridge.js +3 -1
  48. package/dist/portal/hooks/usePortalEventBridge.js.map +2 -2
  49. package/dist/primitives/Notice.js +3 -3
  50. package/dist/primitives/Notice.js.map +2 -2
  51. package/dist/theme/ThemeProvider.js +4 -2
  52. package/dist/theme/ThemeProvider.js.map +2 -2
  53. package/package.json +3 -3
  54. package/src/ai/AiChat.tsx +4 -4
  55. package/src/backend/CrudForm.tsx +20 -17
  56. package/src/backend/DataTable.tsx +6 -9
  57. package/src/backend/JsonDisplay.tsx +4 -1
  58. package/src/backend/__tests__/component-replacement.test.tsx +1 -1
  59. package/src/backend/confirm-dialog/useConfirmDialog.tsx +4 -3
  60. package/src/backend/dashboard/DashboardScreen.tsx +9 -6
  61. package/src/backend/dashboard/widgetRegistry.ts +4 -1
  62. package/src/backend/detail/CustomDataSection.tsx +5 -2
  63. package/src/backend/detail/InlineEditors.tsx +4 -1
  64. package/src/backend/detail/TagsSection.tsx +4 -1
  65. package/src/backend/injection/InjectionSpot.tsx +11 -8
  66. package/src/backend/injection/eventBridge.ts +4 -1
  67. package/src/backend/injection/useGuardedMutation.ts +4 -1
  68. package/src/backend/injection/useInjectionDataWidgets.ts +4 -1
  69. package/src/backend/injection/useRegisteredComponent.tsx +7 -9
  70. package/src/backend/injection/widgetRegistry.ts +4 -1
  71. package/src/backend/inputs/LookupSelect.tsx +4 -1
  72. package/src/backend/notifications/NotificationDispatcher.ts +5 -2
  73. package/src/backend/sidebar/SidebarCustomizationEditor.tsx +8 -5
  74. package/src/backend/utils/__tests__/bulkDelete.test.ts +29 -23
  75. package/src/backend/utils/api.ts +4 -2
  76. package/src/backend/utils/bulkDelete.ts +5 -2
  77. package/src/backend/version-history/VersionHistoryPanel.tsx +5 -2
  78. package/src/portal/hooks/usePortalDashboardWidgets.ts +4 -1
  79. package/src/portal/hooks/usePortalEventBridge.ts +4 -1
  80. package/src/primitives/Notice.tsx +4 -4
  81. 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
- console.error(`[notifications] handler failed: ${handler.id}`, error)
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
- console.error('[notifications] effect listener failed', error)
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
- console.error('Failed to load sidebar variants', err)
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
- console.error('Failed to create sidebar variant', err)
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
- console.error('Failed to save sidebar variant', err)
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
- console.error('Failed to toggle variant active state', err)
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
- console.error('Failed to delete variant', err)
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 via console.warn when logTag is provided', async () => {
52
- const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
53
- try {
54
- const err = Object.assign(new Error('linked deals (2).'), { code: 'COMPANY_HAS_DEPENDENTS' })
55
- const deleteOne = jest.fn().mockRejectedValue(err)
56
-
57
- await runBulkDelete([{ id: 'x' }], deleteOne, { logTag: 'customers.companies.list' })
58
-
59
- expect(warnSpy).toHaveBeenCalledTimes(1)
60
- const [tag, payload] = warnSpy.mock.calls[0]
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 warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
70
- try {
71
- const deleteOne = jest.fn().mockRejectedValue(new Error('boom'))
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 () => {
@@ -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
- // eslint-disable-next-line no-console
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 && typeof console !== 'undefined' && typeof console.warn === 'function') {
91
- console.warn(`[${logTag}] bulk delete failed`, { id: row.id, code, message, err })
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
- console.error(t('audit_logs.actions.undo'), err)
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
- console.error(t('audit_logs.actions.redo'), err)
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
- console.error(`[usePortalDashboardWidgets] Failed to load widgets for spot ${spotId}:`, loadError)
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
- console.warn('[PortalEventBridge] Heartbeat timeout — reconnecting')
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
- console.warn(
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
- console.warn('[ThemeProvider] localStorage read failed:', error)
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
- console.warn('[ThemeProvider] localStorage write failed:', error)
91
+ logger.warn('localStorage write failed', { err: error })
89
92
  }
90
93
  }
91
94
  const resolved = newTheme === 'system' ? getSystemTheme() : newTheme