create-mercato-app 0.4.6-develop-e321a4e2a1 → 0.4.6-main-24e64eef39
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/bin/create-mercato-app +2 -2
- package/package.json +1 -1
- package/template/src/app/(backend)/backend/[...slug]/page.tsx +2 -6
- package/template/src/app/(backend)/backend/layout.tsx +27 -30
- package/template/src/bootstrap.ts +0 -4
- package/template/src/components/ClientBootstrap.tsx +5 -2
- package/template/src/modules/example/api/todos/route.ts +1 -1
- package/template/src/modules/example/backend/umes-handlers/page.tsx +1 -1
- package/template/src/modules/example/data/enrichers.ts +4 -32
- package/template/src/modules/example/data/entities.ts +0 -28
- package/template/src/modules/example/data/validators.ts +0 -22
- package/template/src/modules/example/i18n/de.json +10 -88
- package/template/src/modules/example/i18n/en.json +10 -88
- package/template/src/modules/example/i18n/es.json +10 -88
- package/template/src/modules/example/i18n/pl.json +10 -88
- package/template/src/modules/example/widgets/__tests__/injection-table.test.ts +6 -39
- package/template/src/modules/example/widgets/injection/catalog-seo-report/widget.client.tsx +1 -1
- package/template/src/modules/example/widgets/injection/crud-validation/widget.client.tsx +13 -13
- package/template/src/modules/example/widgets/injection/crud-validation/widget.ts +0 -3
- package/template/src/modules/example/widgets/injection-table.ts +1 -54
- package/template/src/app/(backend)/backend/__tests__/backend-require-features.test.tsx +0 -108
- package/template/src/components/ComponentOverridesBootstrap.tsx +0 -20
- package/template/src/modules/example/__integration__/TC-UMES-004.spec.ts +0 -337
- package/template/src/modules/example/__integration__/TC-UMES-006.spec.ts +0 -55
- package/template/src/modules/example/__integration__/meta.ts +0 -3
- package/template/src/modules/example/api/customer-priorities/route.ts +0 -154
- package/template/src/modules/example/api/interceptors.ts +0 -174
- package/template/src/modules/example/backend/umes-extensions/page.meta.ts +0 -24
- package/template/src/modules/example/backend/umes-extensions/page.tsx +0 -365
- package/template/src/modules/example/backend/umes-integrations/page.meta.ts +0 -25
- package/template/src/modules/example/backend/umes-integrations/page.tsx +0 -301
- package/template/src/modules/example/migrations/Migration20260226161000_example.ts +0 -15
- package/template/src/modules/example/widgets/components.ts +0 -23
- package/template/src/modules/example/widgets/injection/customer-priority-bulk-actions/widget.ts +0 -67
- package/template/src/modules/example/widgets/injection/customer-priority-column/widget.ts +0 -23
- package/template/src/modules/example/widgets/injection/customer-priority-detail/widget.client.tsx +0 -136
- package/template/src/modules/example/widgets/injection/customer-priority-detail/widget.ts +0 -13
- package/template/src/modules/example/widgets/injection/customer-priority-field/widget.ts +0 -68
- package/template/src/modules/example/widgets/injection/customer-priority-filter/widget.ts +0 -25
- package/template/src/modules/example/widgets/injection/customer-priority-row-action/widget.ts +0 -26
package/bin/create-mercato-app
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { existsSync } from 'node:fs';
|
|
8
8
|
import { join, dirname } from 'node:path';
|
|
9
|
-
import { fileURLToPath
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const distIndex = join(__dirname, '..', 'dist', 'index.js');
|
|
@@ -18,4 +18,4 @@ if (!existsSync(distIndex)) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Import the actual CLI
|
|
21
|
-
await import(
|
|
21
|
+
await import(distIndex);
|
package/package.json
CHANGED
|
@@ -7,7 +7,6 @@ import { ApplyBreadcrumb } from '@open-mercato/ui/backend/AppShell'
|
|
|
7
7
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
8
8
|
import { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
9
9
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
10
|
-
import { ComponentReplacementHandles, resolveRegisteredComponent } from '@open-mercato/shared/modules/widgets/component-registry'
|
|
11
10
|
|
|
12
11
|
type Awaitable<T> = T | Promise<T>
|
|
13
12
|
|
|
@@ -48,15 +47,12 @@ export default async function BackendCatchAll(props: { params: Awaitable<{ slug?
|
|
|
48
47
|
if (!ok) redirect('/login?requireFeature=' + encodeURIComponent(features.join(',')))
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
|
-
const
|
|
52
|
-
const Component = resolveRegisteredComponent(pageHandle, match.route.Component)
|
|
50
|
+
const Component = match.route.Component
|
|
53
51
|
|
|
54
52
|
return (
|
|
55
53
|
<>
|
|
56
54
|
<ApplyBreadcrumb breadcrumb={match.route.breadcrumb} title={match.route.title} titleKey={match.route.titleKey} />
|
|
57
|
-
<
|
|
58
|
-
<Component params={match.params} />
|
|
59
|
-
</div>
|
|
55
|
+
<Component params={match.params} />
|
|
60
56
|
</>
|
|
61
57
|
)
|
|
62
58
|
}
|
|
@@ -38,7 +38,6 @@ import { APP_VERSION } from '@open-mercato/shared/lib/version'
|
|
|
38
38
|
import { PageInjectionBoundary } from '@open-mercato/ui/backend/injection/PageInjectionBoundary'
|
|
39
39
|
import { AiAssistantIntegration, AiChatHeaderButton } from '@open-mercato/ai-assistant/frontend'
|
|
40
40
|
import { CustomEntity } from '@open-mercato/core/modules/entities/data/entities'
|
|
41
|
-
import { ComponentOverridesBootstrap } from '@/components/ComponentOverridesBootstrap'
|
|
42
41
|
|
|
43
42
|
type NavItem = {
|
|
44
43
|
href: string
|
|
@@ -384,36 +383,34 @@ export default async function BackendLayout({ children, params }: { children: Re
|
|
|
384
383
|
<>
|
|
385
384
|
<Script async src="https://w.appzi.io/w.js?token=TtIV6" strategy="afterInteractive" />
|
|
386
385
|
<I18nProvider locale={locale} dict={dict}>
|
|
387
|
-
<
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
386
|
+
<AiAssistantIntegration
|
|
387
|
+
tenantId={auth?.tenantId ?? null}
|
|
388
|
+
organizationId={auth?.orgId ?? null}
|
|
389
|
+
>
|
|
390
|
+
<AppShell
|
|
391
|
+
key={path}
|
|
392
|
+
productName={productName}
|
|
393
|
+
email={auth?.email}
|
|
394
|
+
groups={groups}
|
|
395
|
+
currentTitle={currentTitle}
|
|
396
|
+
breadcrumb={breadcrumb}
|
|
397
|
+
sidebarCollapsedDefault={initialCollapsed}
|
|
398
|
+
rightHeaderSlot={rightHeaderContent}
|
|
399
|
+
mobileSidebarSlot={mobileSidebarContent}
|
|
400
|
+
adminNavApi="/api/auth/admin/nav"
|
|
401
|
+
version={APP_VERSION}
|
|
402
|
+
settingsPathPrefixes={settingsPathPrefixes}
|
|
403
|
+
settingsSections={filteredSettingsSections}
|
|
404
|
+
settingsSectionTitle={translate('backend.nav.settings', 'Settings')}
|
|
405
|
+
profileSections={profileSections}
|
|
406
|
+
profileSectionTitle={translate('profile.page.title', 'Profile')}
|
|
407
|
+
profilePathPrefixes={profilePathPrefixes}
|
|
391
408
|
>
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
currentTitle={currentTitle}
|
|
398
|
-
breadcrumb={breadcrumb}
|
|
399
|
-
sidebarCollapsedDefault={initialCollapsed}
|
|
400
|
-
rightHeaderSlot={rightHeaderContent}
|
|
401
|
-
mobileSidebarSlot={mobileSidebarContent}
|
|
402
|
-
adminNavApi="/api/auth/admin/nav"
|
|
403
|
-
version={APP_VERSION}
|
|
404
|
-
settingsPathPrefixes={settingsPathPrefixes}
|
|
405
|
-
settingsSections={filteredSettingsSections}
|
|
406
|
-
settingsSectionTitle={translate('backend.nav.settings', 'Settings')}
|
|
407
|
-
profileSections={profileSections}
|
|
408
|
-
profileSectionTitle={translate('profile.page.title', 'Profile')}
|
|
409
|
-
profilePathPrefixes={profilePathPrefixes}
|
|
410
|
-
>
|
|
411
|
-
<PageInjectionBoundary path={path} context={injectionContext}>
|
|
412
|
-
{children}
|
|
413
|
-
</PageInjectionBoundary>
|
|
414
|
-
</AppShell>
|
|
415
|
-
</AiAssistantIntegration>
|
|
416
|
-
</ComponentOverridesBootstrap>
|
|
409
|
+
<PageInjectionBoundary path={path} context={injectionContext}>
|
|
410
|
+
{children}
|
|
411
|
+
</PageInjectionBoundary>
|
|
412
|
+
</AppShell>
|
|
413
|
+
</AiAssistantIntegration>
|
|
417
414
|
</I18nProvider>
|
|
418
415
|
</>
|
|
419
416
|
)
|
|
@@ -44,8 +44,6 @@ import { eventModuleConfigs, allEvents } from '@/.mercato/generated/events.gener
|
|
|
44
44
|
import { registerEventModuleConfigs } from '@open-mercato/shared/modules/events'
|
|
45
45
|
import { analyticsModuleConfigs } from '@/.mercato/generated/analytics.generated'
|
|
46
46
|
import { enricherEntries } from '@/.mercato/generated/enrichers.generated'
|
|
47
|
-
import { interceptorEntries } from '@/.mercato/generated/interceptors.generated'
|
|
48
|
-
import { componentOverrideEntries } from '@/.mercato/generated/component-overrides.generated'
|
|
49
47
|
import { messageTypes } from '@/.mercato/generated/message-types.generated'
|
|
50
48
|
import { messageObjectTypes } from '@/.mercato/generated/message-objects.generated'
|
|
51
49
|
import { registerMessageTypes } from '@open-mercato/core/modules/messages/lib/message-types-registry'
|
|
@@ -72,8 +70,6 @@ export const bootstrap = createBootstrap({
|
|
|
72
70
|
searchModuleConfigs,
|
|
73
71
|
analyticsModuleConfigs,
|
|
74
72
|
enricherEntries,
|
|
75
|
-
interceptorEntries,
|
|
76
|
-
componentOverrideEntries,
|
|
77
73
|
})
|
|
78
74
|
|
|
79
75
|
export { isBootstrapped }
|
|
@@ -11,8 +11,8 @@ import { dashboardWidgetEntries } from '@/.mercato/generated/dashboard-widgets.g
|
|
|
11
11
|
import { registerDashboardWidgets } from '@open-mercato/ui/backend/dashboard/widgetRegistry'
|
|
12
12
|
// Side-effect: registers translatable fields for client-side TranslationManager
|
|
13
13
|
import '@/.mercato/generated/translations-fields.generated'
|
|
14
|
-
|
|
15
|
-
import '
|
|
14
|
+
import { getMessageUiComponentRegistry } from '@/.mercato/generated/messages.client.generated'
|
|
15
|
+
import { configureMessageUiComponentRegistry } from '@open-mercato/core/modules/messages/components/utils/typeUiRegistry'
|
|
16
16
|
|
|
17
17
|
let _clientBootstrapped = false
|
|
18
18
|
|
|
@@ -27,6 +27,9 @@ function clientBootstrap() {
|
|
|
27
27
|
|
|
28
28
|
// Register dashboard widgets
|
|
29
29
|
registerDashboardWidgets(dashboardWidgetEntries)
|
|
30
|
+
|
|
31
|
+
// Configure message UI components from generated client registry.
|
|
32
|
+
configureMessageUiComponentRegistry(getMessageUiComponentRegistry())
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
export function ClientBootstrapProvider({ children }: { children: React.ReactNode }) {
|
|
@@ -34,7 +34,7 @@ const querySchema = z
|
|
|
34
34
|
organizationId: z.string().uuid().optional(),
|
|
35
35
|
createdFrom: z.string().optional(),
|
|
36
36
|
createdTo: z.string().optional(),
|
|
37
|
-
format: z.enum(['json', 'csv']).optional(),
|
|
37
|
+
format: z.enum(['json', 'csv']).optional().default('json'),
|
|
38
38
|
})
|
|
39
39
|
.passthrough()
|
|
40
40
|
|
|
@@ -15,7 +15,7 @@ function print(value: unknown) {
|
|
|
15
15
|
return JSON.stringify(value ?? null)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const hintClassName = 'inline-flex items-center rounded-md border border-amber-
|
|
18
|
+
const hintClassName = 'inline-flex items-center rounded-md border border-amber-400/40 bg-amber-400/10 px-2 py-1 text-xs text-amber-100/90'
|
|
19
19
|
|
|
20
20
|
type CustomerRecord = {
|
|
21
21
|
id?: string
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ResponseEnricher, EnricherContext } from '@open-mercato/shared/lib/crud/response-enricher'
|
|
9
|
-
import {
|
|
9
|
+
import { Todo } from './entities'
|
|
10
10
|
|
|
11
11
|
type CustomerRecord = Record<string, unknown> & { id: string }
|
|
12
12
|
|
|
@@ -14,7 +14,6 @@ type TodoEnrichment = {
|
|
|
14
14
|
_example: {
|
|
15
15
|
todoCount: number
|
|
16
16
|
openTodoCount: number
|
|
17
|
-
priority: 'low' | 'normal' | 'high' | 'critical'
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
|
|
@@ -53,7 +52,7 @@ const customerTodoCountEnricher: ResponseEnricher<CustomerRecord, TodoEnrichment
|
|
|
53
52
|
priority: 10,
|
|
54
53
|
timeout: 2000,
|
|
55
54
|
fallback: {
|
|
56
|
-
_example: { todoCount: 0, openTodoCount: 0
|
|
55
|
+
_example: { todoCount: 0, openTodoCount: 0 },
|
|
57
56
|
},
|
|
58
57
|
|
|
59
58
|
async enrichOne(record, context) {
|
|
@@ -65,20 +64,10 @@ const customerTodoCountEnricher: ResponseEnricher<CustomerRecord, TodoEnrichment
|
|
|
65
64
|
})
|
|
66
65
|
const statsByBucket = buildBucketStats(todos)
|
|
67
66
|
const scoped = statsByBucket.get(getPersonBucket(record.id)) ?? { todoCount: 0, openTodoCount: 0 }
|
|
68
|
-
const priority = await em.findOne(ExampleCustomerPriority, {
|
|
69
|
-
customerId: record.id,
|
|
70
|
-
organizationId: context.organizationId,
|
|
71
|
-
tenantId: context.tenantId,
|
|
72
|
-
deletedAt: null,
|
|
73
|
-
}, { orderBy: { updatedAt: 'desc', createdAt: 'desc' } })
|
|
74
67
|
|
|
75
68
|
return {
|
|
76
69
|
...record,
|
|
77
|
-
_example: {
|
|
78
|
-
todoCount: scoped.todoCount,
|
|
79
|
-
openTodoCount: scoped.openTodoCount,
|
|
80
|
-
priority: (priority?.priority as TodoEnrichment['_example']['priority']) ?? 'normal',
|
|
81
|
-
},
|
|
70
|
+
_example: { todoCount: scoped.todoCount, openTodoCount: scoped.openTodoCount },
|
|
82
71
|
}
|
|
83
72
|
},
|
|
84
73
|
|
|
@@ -90,27 +79,10 @@ const customerTodoCountEnricher: ResponseEnricher<CustomerRecord, TodoEnrichment
|
|
|
90
79
|
deletedAt: null,
|
|
91
80
|
})
|
|
92
81
|
const statsByBucket = buildBucketStats(todos)
|
|
93
|
-
const customerIds = records.map((record) => record.id)
|
|
94
|
-
const priorities: ExampleCustomerPriority[] = customerIds.length > 0
|
|
95
|
-
? await em.find(ExampleCustomerPriority, {
|
|
96
|
-
customerId: { $in: customerIds },
|
|
97
|
-
organizationId: context.organizationId,
|
|
98
|
-
tenantId: context.tenantId,
|
|
99
|
-
deletedAt: null,
|
|
100
|
-
}, { orderBy: { updatedAt: 'desc', createdAt: 'desc' } })
|
|
101
|
-
: []
|
|
102
|
-
const priorityByCustomerId = new Map<string, ExampleCustomerPriority['priority']>()
|
|
103
|
-
for (const entry of priorities) {
|
|
104
|
-
if (priorityByCustomerId.has(entry.customerId)) continue
|
|
105
|
-
priorityByCustomerId.set(entry.customerId, entry.priority)
|
|
106
|
-
}
|
|
107
82
|
|
|
108
83
|
return records.map((record) => ({
|
|
109
84
|
...record,
|
|
110
|
-
_example: {
|
|
111
|
-
...(statsByBucket.get(getPersonBucket(record.id)) ?? { todoCount: 0, openTodoCount: 0 }),
|
|
112
|
-
priority: (priorityByCustomerId.get(record.id) as TodoEnrichment['_example']['priority'] | undefined) ?? 'normal',
|
|
113
|
-
},
|
|
85
|
+
_example: statsByBucket.get(getPersonBucket(record.id)) ?? { todoCount: 0, openTodoCount: 0 },
|
|
114
86
|
}))
|
|
115
87
|
},
|
|
116
88
|
}
|
|
@@ -42,31 +42,3 @@ export class Todo {
|
|
|
42
42
|
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
43
43
|
deletedAt?: Date | null
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
@Entity({ tableName: 'example_customer_priorities' })
|
|
47
|
-
export class ExampleCustomerPriority {
|
|
48
|
-
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
49
|
-
id!: string
|
|
50
|
-
|
|
51
|
-
@Property({ name: 'customer_id', type: 'uuid' })
|
|
52
|
-
customerId!: string
|
|
53
|
-
|
|
54
|
-
/** Customer priority level. Valid values: 'low' | 'normal' | 'high' | 'critical'. */
|
|
55
|
-
@Property({ type: 'text', default: 'normal' })
|
|
56
|
-
priority: 'low' | 'normal' | 'high' | 'critical' = 'normal'
|
|
57
|
-
|
|
58
|
-
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
59
|
-
tenantId!: string
|
|
60
|
-
|
|
61
|
-
@Property({ name: 'organization_id', type: 'uuid' })
|
|
62
|
-
organizationId!: string
|
|
63
|
-
|
|
64
|
-
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
65
|
-
createdAt: Date = new Date()
|
|
66
|
-
|
|
67
|
-
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
68
|
-
updatedAt: Date = new Date()
|
|
69
|
-
|
|
70
|
-
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
71
|
-
deletedAt?: Date | null
|
|
72
|
-
}
|
|
@@ -5,25 +5,3 @@ export const exampleItemCreateSchema = z.object({
|
|
|
5
5
|
})
|
|
6
6
|
|
|
7
7
|
export type ExampleItemCreateInput = z.infer<typeof exampleItemCreateSchema>
|
|
8
|
-
|
|
9
|
-
export const customerPriorityValueSchema = z.enum(['low', 'normal', 'high', 'critical'])
|
|
10
|
-
|
|
11
|
-
export const customerPriorityCreateSchema = z.object({
|
|
12
|
-
customerId: z.string().uuid(),
|
|
13
|
-
priority: customerPriorityValueSchema.default('normal'),
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
export const customerPriorityUpdateSchema = z.object({
|
|
17
|
-
id: z.string().uuid(),
|
|
18
|
-
customerId: z.string().uuid().optional(),
|
|
19
|
-
priority: customerPriorityValueSchema.optional(),
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
export const customerPriorityListSchema = z.object({
|
|
23
|
-
id: z.string().uuid().optional(),
|
|
24
|
-
customerId: z.string().uuid().optional(),
|
|
25
|
-
page: z.coerce.number().min(1).default(1),
|
|
26
|
-
pageSize: z.coerce.number().min(1).max(100).default(50),
|
|
27
|
-
sortField: z.enum(['id', 'customer_id', 'priority', 'created_at']).optional().default('created_at'),
|
|
28
|
-
sortDir: z.enum(['asc', 'desc']).optional().default('desc'),
|
|
29
|
-
})
|
|
@@ -6,27 +6,8 @@
|
|
|
6
6
|
"example.manageEntities": "Verwalte Beispiel-Entitäten.",
|
|
7
7
|
"example.menu.quickAddTodo": "Schnell Aufgabe anlegen",
|
|
8
8
|
"example.menu.todosShortcut": "Beispielaufgaben",
|
|
9
|
-
"example.menu.umesExtensions": "Phase E-H handlers",
|
|
10
|
-
"example.menu.umesIntegrations": "Phase L integrations",
|
|
11
|
-
"example.messageObjects.notFound": "Nicht gefunden",
|
|
12
|
-
"example.messageObjects.todo.title": "To-do",
|
|
13
9
|
"example.moduleTitle": "Beispielmodul",
|
|
14
10
|
"example.nav.group": "Beispiel",
|
|
15
|
-
"example.priority.action.open": "Kunde öffnen",
|
|
16
|
-
"example.priority.bulk.setNormal": "Priorität auf Normal setzen",
|
|
17
|
-
"example.priority.column": "Beispielpriorität",
|
|
18
|
-
"example.priority.critical": "Kritisch",
|
|
19
|
-
"example.priority.detail.description": "Priorität für diesen Kundeneintrag festlegen.",
|
|
20
|
-
"example.priority.detail.error.load": "Priorität konnte nicht geladen werden.",
|
|
21
|
-
"example.priority.detail.error.save": "Priorität konnte nicht gespeichert werden.",
|
|
22
|
-
"example.priority.detail.label": "Kundenpriorität",
|
|
23
|
-
"example.priority.detail.loading": "Priorität wird geladen...",
|
|
24
|
-
"example.priority.detail.saving": "Priorität wird gespeichert...",
|
|
25
|
-
"example.priority.field": "Priorität",
|
|
26
|
-
"example.priority.filter": "Priorität",
|
|
27
|
-
"example.priority.high": "Hoch",
|
|
28
|
-
"example.priority.low": "Niedrig",
|
|
29
|
-
"example.priority.normal": "Normal",
|
|
30
11
|
"example.publicPage": "Öffentliche Beispielseite.",
|
|
31
12
|
"example.salesTodos.tabLabel": "Aufgaben (Beispiel)",
|
|
32
13
|
"example.todos.create.title": "To-do erstellen",
|
|
@@ -74,43 +55,6 @@
|
|
|
74
55
|
"example.todos.table.organization.none": "Keine Organisation",
|
|
75
56
|
"example.todos.table.organization.unknown": "Unbekannt",
|
|
76
57
|
"example.todos.table.title": "Allgemeine Aufgaben",
|
|
77
|
-
"example.umes.extensions.description": "Validierungsseite für API-Interceptoren, DataTable/CrudForm-Erweiterungsoberflächen und Ersetzungs-Handles.",
|
|
78
|
-
"example.umes.extensions.form.note": "Notiz",
|
|
79
|
-
"example.umes.extensions.form.title": "Titel",
|
|
80
|
-
"example.umes.extensions.hintHeading": "What should be visible and how it should work",
|
|
81
|
-
"example.umes.extensions.phaseE.description": "Run the full probe suite: metadata merge, wildcard route matching, query revalidation, timeout fail-closed, and crash fail-closed.",
|
|
82
|
-
"example.umes.extensions.phaseE.hint1": "1. `default` probe: must return `_example.interceptor` metadata in `/api/example/todos` response.",
|
|
83
|
-
"example.umes.extensions.phaseE.hint2": "2. `wildcard` probe: must return `_example.wildcardProbe=true` for wildcard route interceptor.",
|
|
84
|
-
"example.umes.extensions.phaseE.hint3": "3. `bad-query` probe: must fail with HTTP `400` (route schema revalidation after interceptor rewrite).",
|
|
85
|
-
"example.umes.extensions.phaseE.hint4": "4. `timeout` probe: must fail closed with HTTP `504`.",
|
|
86
|
-
"example.umes.extensions.phaseE.hint5": "5. `crash` probe: must fail closed with HTTP `500`.",
|
|
87
|
-
"example.umes.extensions.phaseE.hint6": "Note: red network entries for probes 3-5 are expected and indicate correct fail-closed behavior.",
|
|
88
|
-
"example.umes.extensions.phaseE.missing": "Eine oder mehrere Interceptor-Prüfungen fehlgeschlagen. Zeilen unten überprüfen.",
|
|
89
|
-
"example.umes.extensions.phaseE.run": "Interceptor-Sonde ausführen",
|
|
90
|
-
"example.umes.extensions.phaseE.title": "Phase E — API-Interceptoren",
|
|
91
|
-
"example.umes.extensions.phaseF.description": "This table exposes `replacementHandle` and known component handles. Use it to validate replacement registration and then verify injected columns/actions on Customers list.",
|
|
92
|
-
"example.umes.extensions.phaseF.expect": "Expect: injected column, row action, filters, and bulk action.",
|
|
93
|
-
"example.umes.extensions.phaseF.hint1": "1. On `/backend/customers/people` table: column `Example priority` should be visible.",
|
|
94
|
-
"example.umes.extensions.phaseF.hint2": "2. In filters drawer: select filter `Priority` should be visible.",
|
|
95
|
-
"example.umes.extensions.phaseF.hint3": "3. In row actions menu: action `Open customer` should be visible.",
|
|
96
|
-
"example.umes.extensions.phaseF.hint4": "4. After selecting rows: bulk action `Set normal priority` should update priorities via API.",
|
|
97
|
-
"example.umes.extensions.phaseF.openCustomers": "Open customers table",
|
|
98
|
-
"example.umes.extensions.phaseF.title": "Phase F — DataTable-Erweiterungen",
|
|
99
|
-
"example.umes.extensions.phaseG.description": "This harness keeps the injected widget active on `crud-form:example.todo`. Submit once to confirm the field/event pipeline executes.",
|
|
100
|
-
"example.umes.extensions.phaseG.hint1": "1. Injected widget card `Example Injection Widget` should be visible above form fields.",
|
|
101
|
-
"example.umes.extensions.phaseG.hint2": "2. Saving valid form should update `submitResult` below the form.",
|
|
102
|
-
"example.umes.extensions.phaseG.hint3": "3. In customer detail form (`/backend/customers/people/:id`), injected `_example.priority` field should persist via onSave handler.",
|
|
103
|
-
"example.umes.extensions.phaseG.title": "Phase G — CrudForm-Feldeinspeisung",
|
|
104
|
-
"example.umes.extensions.phaseH.description": "Active replacement handles in this area: page, DataTable, CrudForm, and the `ui.detail:NotesSection` wrapper declared in `example/widgets/components.ts`.",
|
|
105
|
-
"example.umes.extensions.phaseH.hint1": "1. This page root should expose `data-component-handle=\"page:/backend/umes-extensions\"`.",
|
|
106
|
-
"example.umes.extensions.phaseH.hint2": "2. Handles list table should expose `data-table:example.umes.extensions` replacement handle.",
|
|
107
|
-
"example.umes.extensions.phaseH.hint3": "3. Form should expose `crud-form:example.todo` replacement handle.",
|
|
108
|
-
"example.umes.extensions.phaseH.hint4": "4. Customer detail notes section should render wrapped border from `ExampleNotesSectionWrapper`.",
|
|
109
|
-
"example.umes.extensions.phaseH.title": "Phase H — Komponentenersetzung",
|
|
110
|
-
"example.umes.extensions.table.handle": "Handle",
|
|
111
|
-
"example.umes.extensions.table.label": "Bezeichnung",
|
|
112
|
-
"example.umes.extensions.table.title": "Ersetzungs-Handles",
|
|
113
|
-
"example.umes.extensions.title": "UMES Phasen E-H Erweiterungen",
|
|
114
58
|
"example.umes.handlers.actions.emitServerEvent": "Server todo.created auslösen",
|
|
115
59
|
"example.umes.handlers.actions.loadBlockedSaveExample": "Blockiertes-Speichern-Beispiel laden",
|
|
116
60
|
"example.umes.handlers.actions.loadTransformSaveExample": "Transform-Speichern-Beispiel laden",
|
|
@@ -183,38 +127,6 @@
|
|
|
183
127
|
"example.umes.handlers.title": "Phase A-D Handler",
|
|
184
128
|
"example.umes.handlers.validation.titleRequired": "Titel ist erforderlich.",
|
|
185
129
|
"example.umes.handlers.widgetHidden": "Widget ist derzeit ausgeblendet (Sichtbarkeitsstatus: false)",
|
|
186
|
-
"example.umes.integrations.badges.cycle": "Cycle statuses",
|
|
187
|
-
"example.umes.integrations.badges.description": "Four status badge renderers with fixed loaders demonstrating healthy, warning, error, and unknown states.",
|
|
188
|
-
"example.umes.integrations.badges.hint1": "1. Four badges should render with colored dots (green, yellow, red, gray).",
|
|
189
|
-
"example.umes.integrations.badges.hint2": "2. Hovering over badges with tooltips should show the tooltip text.",
|
|
190
|
-
"example.umes.integrations.badges.hint3": "3. Clicking `Cycle statuses` should rotate all badge statuses forward.",
|
|
191
|
-
"example.umes.integrations.badges.title": "L.2 Status Badge Injection",
|
|
192
|
-
"example.umes.integrations.description": "Validation page for Phase L integration extensions: multi-step wizard, status badges, external ID mapping, and integration registry.",
|
|
193
|
-
"example.umes.integrations.externalIds.description": "ExternalIdsWidget renders mock integration mappings for Shopify and Stripe.",
|
|
194
|
-
"example.umes.integrations.externalIds.hint1": "1. Two integration rows should display: Shopify (synced, green dot) and Stripe (pending, yellow dot).",
|
|
195
|
-
"example.umes.integrations.externalIds.hint2": "2. Shopify row should show an external link icon.",
|
|
196
|
-
"example.umes.integrations.externalIds.hint3": "3. Each row shows the external ID in a monospace code badge.",
|
|
197
|
-
"example.umes.integrations.externalIds.title": "L.3 External ID Mapping Display",
|
|
198
|
-
"example.umes.integrations.hintHeading": "What should be visible and how it should work",
|
|
199
|
-
"example.umes.integrations.registry.description": "Demonstrates registerIntegration(), getAllIntegrations(), and getIntegrationTitle() from the shared registry.",
|
|
200
|
-
"example.umes.integrations.registry.hint1": "1. Two integrations should be registered on mount (sync_shopify, gateway_stripe).",
|
|
201
|
-
"example.umes.integrations.registry.hint2": "2. `getIntegrationTitle('sync_shopify')` should return 'Shopify' (not the raw ID).",
|
|
202
|
-
"example.umes.integrations.registry.hint3": "3. `getIntegrationTitle('unknown_id')` should fall back to 'unknown_id'.",
|
|
203
|
-
"example.umes.integrations.registry.title": "L.4 Integration Registry",
|
|
204
|
-
"example.umes.integrations.title": "UMES Phase L — Integration Extensions",
|
|
205
|
-
"example.umes.integrations.wizard.description": "Complete the 3-step wizard to validate step navigation, per-step validation, and onComplete callback.",
|
|
206
|
-
"example.umes.integrations.wizard.hint1": "1. Step indicator should show 3 numbered circles with connecting lines.",
|
|
207
|
-
"example.umes.integrations.wizard.hint2": "2. Step 1 requires both `apiKey` and `apiSecret` — leaving them empty should show validation error.",
|
|
208
|
-
"example.umes.integrations.wizard.hint3": "3. Completing all steps should display the accumulated wizard data below.",
|
|
209
|
-
"example.umes.integrations.wizard.step1.description": "Enter API credentials for the external system.",
|
|
210
|
-
"example.umes.integrations.wizard.step1.label": "Credentials",
|
|
211
|
-
"example.umes.integrations.wizard.step2.description": "Configure synchronization direction.",
|
|
212
|
-
"example.umes.integrations.wizard.step2.label": "Scope",
|
|
213
|
-
"example.umes.integrations.wizard.step3.description": "Set the sync frequency.",
|
|
214
|
-
"example.umes.integrations.wizard.step3.label": "Schedule",
|
|
215
|
-
"example.umes.integrations.wizard.title": "L.1 Multi-Step Wizard Widget",
|
|
216
|
-
"example.umes.integrations.wizard.validationError": "Both API key and API secret are required.",
|
|
217
|
-
"example.umes.integrations.wizard.wizardTitle": "Integration Setup Wizard",
|
|
218
130
|
"example.widgets.catalogSeoReport.edit": "Bearbeiten",
|
|
219
131
|
"example.widgets.catalogSeoReport.error": "SEO-Bericht konnte nicht geladen werden.",
|
|
220
132
|
"example.widgets.catalogSeoReport.example": "Beispiel",
|
|
@@ -248,5 +160,15 @@
|
|
|
248
160
|
"example.widgets.todo.settings.showCompleted": "Erledigte Einträge anzeigen",
|
|
249
161
|
"example.widgets.todo.state.allCaughtUp": "Alles erledigt!",
|
|
250
162
|
"example.widgets.todo.state.empty": "Keine To-dos gefunden.",
|
|
163
|
+
"example.umes.next.form.group": "Testfelder für rekursive Widgets",
|
|
164
|
+
"example.umes.next.form.title": "Phase J — Formular für rekursive Widgets",
|
|
165
|
+
"example.umes.next.page.description": "Demonstriert UMES-Folgephasen einschließlich rekursiver Widget-Erweiterbarkeit (Phase J).",
|
|
166
|
+
"example.umes.next.page.title": "UMES Nächste Phasen",
|
|
167
|
+
"example.umes.next.phaseJ.label": "Rekursive Widget-Injektion",
|
|
168
|
+
"example.umes.next.readiness.description": "Live-Bereitschaft für UMES-Folgephasen. Erkanntes Addon bedeutet, dass der rekursive Injektionspunkt funktioniert.",
|
|
169
|
+
"example.umes.next.readiness.title": "Phasenbereitschaft",
|
|
170
|
+
"example.umes.next.recursive.description": "Ein Widget (crud-validation) deklariert einen eigenen InjectionSpot. Ein anderes Widget (crud-validation-addon) injiziert sich dort hinein und erzeugt eine geschichtete Komposition.",
|
|
171
|
+
"example.umes.next.recursive.spotLabel": "Injektionspunkt:",
|
|
172
|
+
"example.umes.next.recursive.title": "Phase J — Rekursive Widget-Erweiterbarkeit",
|
|
251
173
|
"example.workPlan.nav.group": "Arbeitsplan"
|
|
252
174
|
}
|
|
@@ -6,27 +6,8 @@
|
|
|
6
6
|
"example.manageEntities": "Manage example entities.",
|
|
7
7
|
"example.menu.quickAddTodo": "Quick Add Todo",
|
|
8
8
|
"example.menu.todosShortcut": "Example Todos",
|
|
9
|
-
"example.menu.umesExtensions": "Phase E-H handlers",
|
|
10
|
-
"example.menu.umesIntegrations": "Phase L integrations",
|
|
11
|
-
"example.messageObjects.notFound": "Not found",
|
|
12
|
-
"example.messageObjects.todo.title": "Todo",
|
|
13
9
|
"example.moduleTitle": "Example Module",
|
|
14
10
|
"example.nav.group": "Example",
|
|
15
|
-
"example.priority.action.open": "Open customer",
|
|
16
|
-
"example.priority.bulk.setNormal": "Set normal priority",
|
|
17
|
-
"example.priority.column": "Example priority",
|
|
18
|
-
"example.priority.critical": "Critical",
|
|
19
|
-
"example.priority.detail.description": "Set priority for this customer record.",
|
|
20
|
-
"example.priority.detail.error.load": "Failed to load priority.",
|
|
21
|
-
"example.priority.detail.error.save": "Failed to save priority.",
|
|
22
|
-
"example.priority.detail.label": "Customer priority",
|
|
23
|
-
"example.priority.detail.loading": "Loading priority...",
|
|
24
|
-
"example.priority.detail.saving": "Saving priority...",
|
|
25
|
-
"example.priority.field": "Priority",
|
|
26
|
-
"example.priority.filter": "Priority",
|
|
27
|
-
"example.priority.high": "High",
|
|
28
|
-
"example.priority.low": "Low",
|
|
29
|
-
"example.priority.normal": "Normal",
|
|
30
11
|
"example.publicPage": "Public example page.",
|
|
31
12
|
"example.salesTodos.tabLabel": "Todos (example)",
|
|
32
13
|
"example.todos.create.title": "Create Todo",
|
|
@@ -74,43 +55,6 @@
|
|
|
74
55
|
"example.todos.table.organization.none": "No organization",
|
|
75
56
|
"example.todos.table.organization.unknown": "Unknown",
|
|
76
57
|
"example.todos.table.title": "General tasks",
|
|
77
|
-
"example.umes.extensions.description": "Validation page for API interceptors, DataTable/CrudForm extension surfaces, and replacement handles.",
|
|
78
|
-
"example.umes.extensions.form.note": "Note",
|
|
79
|
-
"example.umes.extensions.form.title": "Title",
|
|
80
|
-
"example.umes.extensions.hintHeading": "What should be visible and how it should work",
|
|
81
|
-
"example.umes.extensions.phaseE.description": "Run the full probe suite: metadata merge, wildcard route matching, query revalidation, timeout fail-closed, and crash fail-closed.",
|
|
82
|
-
"example.umes.extensions.phaseE.hint1": "1. `default` probe: must return `_example.interceptor` metadata in `/api/example/todos` response.",
|
|
83
|
-
"example.umes.extensions.phaseE.hint2": "2. `wildcard` probe: must return `_example.wildcardProbe=true` for wildcard route interceptor.",
|
|
84
|
-
"example.umes.extensions.phaseE.hint3": "3. `bad-query` probe: must fail with HTTP `400` (route schema revalidation after interceptor rewrite).",
|
|
85
|
-
"example.umes.extensions.phaseE.hint4": "4. `timeout` probe: must fail closed with HTTP `504`.",
|
|
86
|
-
"example.umes.extensions.phaseE.hint5": "5. `crash` probe: must fail closed with HTTP `500`.",
|
|
87
|
-
"example.umes.extensions.phaseE.hint6": "Note: red network entries for probes 3-5 are expected and indicate correct fail-closed behavior.",
|
|
88
|
-
"example.umes.extensions.phaseE.missing": "One or more interceptor checks failed. Review rows below.",
|
|
89
|
-
"example.umes.extensions.phaseE.run": "Run interceptor probe",
|
|
90
|
-
"example.umes.extensions.phaseE.title": "Phase E — API interceptors",
|
|
91
|
-
"example.umes.extensions.phaseF.description": "This table exposes `replacementHandle` and known component handles. Use it to validate replacement registration and then verify injected columns/actions on Customers list.",
|
|
92
|
-
"example.umes.extensions.phaseF.expect": "Expect: injected column, row action, filters, and bulk action.",
|
|
93
|
-
"example.umes.extensions.phaseF.hint1": "1. On `/backend/customers/people` table: column `Example priority` should be visible.",
|
|
94
|
-
"example.umes.extensions.phaseF.hint2": "2. In filters drawer: select filter `Priority` should be visible.",
|
|
95
|
-
"example.umes.extensions.phaseF.hint3": "3. In row actions menu: action `Open customer` should be visible.",
|
|
96
|
-
"example.umes.extensions.phaseF.hint4": "4. After selecting rows: bulk action `Set normal priority` should update priorities via API.",
|
|
97
|
-
"example.umes.extensions.phaseF.openCustomers": "Open customers table",
|
|
98
|
-
"example.umes.extensions.phaseF.title": "Phase F — DataTable extensions",
|
|
99
|
-
"example.umes.extensions.phaseG.description": "This harness keeps the injected widget active on `crud-form:example.todo`. Submit once to confirm the field/event pipeline executes.",
|
|
100
|
-
"example.umes.extensions.phaseG.hint1": "1. Injected widget card `Example Injection Widget` should be visible above form fields.",
|
|
101
|
-
"example.umes.extensions.phaseG.hint2": "2. Saving valid form should update `submitResult` below the form.",
|
|
102
|
-
"example.umes.extensions.phaseG.hint3": "3. In customer detail form (`/backend/customers/people/:id`), injected `_example.priority` field should persist via onSave handler.",
|
|
103
|
-
"example.umes.extensions.phaseG.title": "Phase G — CrudForm field injection",
|
|
104
|
-
"example.umes.extensions.phaseH.description": "Active replacement handles in this area: page, DataTable, CrudForm, and the `ui.detail:NotesSection` wrapper declared in `example/widgets/components.ts`.",
|
|
105
|
-
"example.umes.extensions.phaseH.hint1": "1. This page root should expose `data-component-handle=\"page:/backend/umes-extensions\"`.",
|
|
106
|
-
"example.umes.extensions.phaseH.hint2": "2. Handles list table should expose `data-table:example.umes.extensions` replacement handle.",
|
|
107
|
-
"example.umes.extensions.phaseH.hint3": "3. Form should expose `crud-form:example.todo` replacement handle.",
|
|
108
|
-
"example.umes.extensions.phaseH.hint4": "4. Customer detail notes section should render wrapped border from `ExampleNotesSectionWrapper`.",
|
|
109
|
-
"example.umes.extensions.phaseH.title": "Phase H — Component replacement",
|
|
110
|
-
"example.umes.extensions.table.handle": "Handle",
|
|
111
|
-
"example.umes.extensions.table.label": "Label",
|
|
112
|
-
"example.umes.extensions.table.title": "Replacement Handles",
|
|
113
|
-
"example.umes.extensions.title": "UMES Phase E-H Extensions",
|
|
114
58
|
"example.umes.handlers.actions.emitServerEvent": "Emit server todo.created",
|
|
115
59
|
"example.umes.handlers.actions.loadBlockedSaveExample": "Load blocked-save example",
|
|
116
60
|
"example.umes.handlers.actions.loadTransformSaveExample": "Load transform-save example",
|
|
@@ -183,38 +127,6 @@
|
|
|
183
127
|
"example.umes.handlers.title": "Phase A-D handlers",
|
|
184
128
|
"example.umes.handlers.validation.titleRequired": "Title is required.",
|
|
185
129
|
"example.umes.handlers.widgetHidden": "Widget currently hidden (visibility state: false)",
|
|
186
|
-
"example.umes.integrations.badges.cycle": "Cycle statuses",
|
|
187
|
-
"example.umes.integrations.badges.description": "Four status badge renderers with fixed loaders demonstrating healthy, warning, error, and unknown states.",
|
|
188
|
-
"example.umes.integrations.badges.hint1": "1. Four badges should render with colored dots (green, yellow, red, gray).",
|
|
189
|
-
"example.umes.integrations.badges.hint2": "2. Hovering over badges with tooltips should show the tooltip text.",
|
|
190
|
-
"example.umes.integrations.badges.hint3": "3. Clicking `Cycle statuses` should rotate all badge statuses forward.",
|
|
191
|
-
"example.umes.integrations.badges.title": "L.2 Status Badge Injection",
|
|
192
|
-
"example.umes.integrations.description": "Validation page for Phase L integration extensions: multi-step wizard, status badges, external ID mapping, and integration registry.",
|
|
193
|
-
"example.umes.integrations.externalIds.description": "ExternalIdsWidget renders mock integration mappings for Shopify and Stripe.",
|
|
194
|
-
"example.umes.integrations.externalIds.hint1": "1. Two integration rows should display: Shopify (synced, green dot) and Stripe (pending, yellow dot).",
|
|
195
|
-
"example.umes.integrations.externalIds.hint2": "2. Shopify row should show an external link icon.",
|
|
196
|
-
"example.umes.integrations.externalIds.hint3": "3. Each row shows the external ID in a monospace code badge.",
|
|
197
|
-
"example.umes.integrations.externalIds.title": "L.3 External ID Mapping Display",
|
|
198
|
-
"example.umes.integrations.hintHeading": "What should be visible and how it should work",
|
|
199
|
-
"example.umes.integrations.registry.description": "Demonstrates registerIntegration(), getAllIntegrations(), and getIntegrationTitle() from the shared registry.",
|
|
200
|
-
"example.umes.integrations.registry.hint1": "1. Two integrations should be registered on mount (sync_shopify, gateway_stripe).",
|
|
201
|
-
"example.umes.integrations.registry.hint2": "2. `getIntegrationTitle('sync_shopify')` should return 'Shopify' (not the raw ID).",
|
|
202
|
-
"example.umes.integrations.registry.hint3": "3. `getIntegrationTitle('unknown_id')` should fall back to 'unknown_id'.",
|
|
203
|
-
"example.umes.integrations.registry.title": "L.4 Integration Registry",
|
|
204
|
-
"example.umes.integrations.title": "UMES Phase L — Integration Extensions",
|
|
205
|
-
"example.umes.integrations.wizard.description": "Complete the 3-step wizard to validate step navigation, per-step validation, and onComplete callback.",
|
|
206
|
-
"example.umes.integrations.wizard.hint1": "1. Step indicator should show 3 numbered circles with connecting lines.",
|
|
207
|
-
"example.umes.integrations.wizard.hint2": "2. Step 1 requires both `apiKey` and `apiSecret` — leaving them empty should show validation error.",
|
|
208
|
-
"example.umes.integrations.wizard.hint3": "3. Completing all steps should display the accumulated wizard data below.",
|
|
209
|
-
"example.umes.integrations.wizard.step1.description": "Enter API credentials for the external system.",
|
|
210
|
-
"example.umes.integrations.wizard.step1.label": "Credentials",
|
|
211
|
-
"example.umes.integrations.wizard.step2.description": "Configure synchronization direction.",
|
|
212
|
-
"example.umes.integrations.wizard.step2.label": "Scope",
|
|
213
|
-
"example.umes.integrations.wizard.step3.description": "Set the sync frequency.",
|
|
214
|
-
"example.umes.integrations.wizard.step3.label": "Schedule",
|
|
215
|
-
"example.umes.integrations.wizard.title": "L.1 Multi-Step Wizard Widget",
|
|
216
|
-
"example.umes.integrations.wizard.validationError": "Both API key and API secret are required.",
|
|
217
|
-
"example.umes.integrations.wizard.wizardTitle": "Integration Setup Wizard",
|
|
218
130
|
"example.widgets.catalogSeoReport.edit": "Edit",
|
|
219
131
|
"example.widgets.catalogSeoReport.error": "Failed to load SEO report.",
|
|
220
132
|
"example.widgets.catalogSeoReport.example": "Example",
|
|
@@ -248,5 +160,15 @@
|
|
|
248
160
|
"example.widgets.todo.settings.showCompleted": "Show completed items",
|
|
249
161
|
"example.widgets.todo.state.allCaughtUp": "All caught up!",
|
|
250
162
|
"example.widgets.todo.state.empty": "No todos found.",
|
|
163
|
+
"example.umes.next.form.group": "Recursive widget test fields",
|
|
164
|
+
"example.umes.next.form.title": "Phase J — Recursive widget form",
|
|
165
|
+
"example.umes.next.page.description": "Demonstrates UMES next phases including recursive widget extensibility (Phase J).",
|
|
166
|
+
"example.umes.next.page.title": "UMES Next Phases",
|
|
167
|
+
"example.umes.next.phaseJ.label": "Recursive widget injection",
|
|
168
|
+
"example.umes.next.readiness.description": "Live readiness for UMES next phases. Addon detected means the recursive injection spot is working.",
|
|
169
|
+
"example.umes.next.readiness.title": "Phase readiness",
|
|
170
|
+
"example.umes.next.recursive.description": "A widget (crud-validation) declares its own InjectionSpot. Another widget (crud-validation-addon) injects into it, creating a layered composition.",
|
|
171
|
+
"example.umes.next.recursive.spotLabel": "Injection spot:",
|
|
172
|
+
"example.umes.next.recursive.title": "Phase J — Recursive widget extensibility",
|
|
251
173
|
"example.workPlan.nav.group": "Work plan"
|
|
252
174
|
}
|