@revealui/core 0.5.5 → 0.5.6

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 (117) hide show
  1. package/README.md +30 -24
  2. package/dist/api/rate-limit.d.ts +1 -1
  3. package/dist/api/rate-limit.js +3 -3
  4. package/dist/api/response-cache.d.ts.map +1 -1
  5. package/dist/api/response-cache.js +3 -1
  6. package/dist/api/rest.d.ts +1 -1
  7. package/dist/api/rest.js +2 -2
  8. package/dist/cache/query-cache.d.ts +1 -1
  9. package/dist/cache/query-cache.js +1 -1
  10. package/dist/caching/index.d.ts +1 -1
  11. package/dist/caching/index.js +1 -1
  12. package/dist/client/admin/components/AdminDashboard.d.ts.map +1 -1
  13. package/dist/client/admin/components/AdminDashboard.js +46 -3
  14. package/dist/client/admin/components/CollectionList.d.ts +3 -1
  15. package/dist/client/admin/components/CollectionList.d.ts.map +1 -1
  16. package/dist/client/admin/components/CollectionList.js +51 -2
  17. package/dist/client/admin/components/DocumentForm.js +2 -2
  18. package/dist/client/admin/page.js +1 -1
  19. package/dist/client/admin/utils/apiClient.d.ts +17 -1
  20. package/dist/client/admin/utils/apiClient.d.ts.map +1 -1
  21. package/dist/client/admin/utils/apiClient.js +25 -1
  22. package/dist/client/hooks.d.ts +1 -1
  23. package/dist/client/hooks.js +1 -1
  24. package/dist/client/richtext/plugins/PastePlugin.d.ts.map +1 -1
  25. package/dist/client/richtext/plugins/PastePlugin.js +30 -0
  26. package/dist/client/ui/index.d.ts +2 -2
  27. package/dist/client/ui/index.js +2 -2
  28. package/dist/collections/operations/fieldHooks.d.ts +2 -2
  29. package/dist/collections/operations/fieldHooks.js +2 -2
  30. package/dist/collections/operations/update.js +1 -1
  31. package/dist/config/index.js +1 -1
  32. package/dist/config/runtime.d.ts +3 -3
  33. package/dist/config/runtime.d.ts.map +1 -1
  34. package/dist/config/runtime.js +2 -2
  35. package/dist/config/utils.d.ts.map +1 -1
  36. package/dist/config/utils.js +5 -0
  37. package/dist/database/safe-parse.d.ts +1 -1
  38. package/dist/database/safe-parse.js +3 -3
  39. package/dist/database/universal-postgres.d.ts +1 -1
  40. package/dist/database/universal-postgres.js +1 -1
  41. package/dist/error-handling/error-reporter.js +4 -4
  42. package/dist/features.d.ts +9 -9
  43. package/dist/features.d.ts.map +1 -1
  44. package/dist/features.js +3 -3
  45. package/dist/generated/index.d.ts +1 -1
  46. package/dist/generated/index.js +1 -1
  47. package/dist/generated/types/{cms.d.ts → admin.d.ts} +1 -1
  48. package/dist/generated/types/admin.d.ts.map +1 -0
  49. package/dist/generated/types/index.d.ts +2 -2
  50. package/dist/generated/types/index.d.ts.map +1 -1
  51. package/dist/generated/types/index.js +2 -2
  52. package/dist/index.d.ts +4 -4
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +8 -8
  55. package/dist/instance/RevealUIInstance.js +2 -2
  56. package/dist/jobs/queue.d.ts +1 -1
  57. package/dist/jobs/queue.d.ts.map +1 -1
  58. package/dist/license-encryption.js +2 -2
  59. package/dist/license.d.ts +1 -1
  60. package/dist/license.d.ts.map +1 -1
  61. package/dist/license.js +15 -6
  62. package/dist/monitoring/zombie-detector.js +1 -1
  63. package/dist/observability/health-check.js +1 -1
  64. package/dist/plugins/nested-docs.d.ts +1 -1
  65. package/dist/plugins/nested-docs.d.ts.map +1 -1
  66. package/dist/plugins/nested-docs.js +1 -1
  67. package/dist/relationships/analyzer.d.ts +1 -1
  68. package/dist/relationships/analyzer.js +2 -2
  69. package/dist/relationships/populate-core.d.ts +1 -1
  70. package/dist/relationships/populate-core.d.ts.map +1 -1
  71. package/dist/relationships/populate-core.js +5 -1
  72. package/dist/relationships/population.js +1 -1
  73. package/dist/richtext/exports/client/rcc.js +1 -1
  74. package/dist/richtext/exports/server/rsc.js +1 -1
  75. package/dist/richtext/index.d.ts +1 -1
  76. package/dist/richtext/index.js +1 -1
  77. package/dist/security/index.d.ts +1 -1
  78. package/dist/security/index.js +1 -1
  79. package/dist/server/renderPage.js +1 -1
  80. package/dist/types/admin.d.ts +8 -0
  81. package/dist/types/admin.d.ts.map +1 -0
  82. package/dist/types/admin.js +6 -0
  83. package/dist/types/config.d.ts +2 -2
  84. package/dist/types/config.d.ts.map +1 -1
  85. package/dist/types/config.js +1 -1
  86. package/dist/types/extensions.d.ts +1 -1
  87. package/dist/types/extensions.d.ts.map +1 -1
  88. package/dist/types/generated.d.ts +4 -4
  89. package/dist/types/generated.d.ts.map +1 -1
  90. package/dist/types/generated.js +2 -2
  91. package/dist/types/hooks.d.ts +1 -1
  92. package/dist/types/hooks.d.ts.map +1 -1
  93. package/dist/types/index.d.ts +4 -4
  94. package/dist/types/index.d.ts.map +1 -1
  95. package/dist/types/index.js +6 -6
  96. package/dist/types/jobs.d.ts +1 -1
  97. package/dist/types/jobs.js +1 -1
  98. package/dist/types/legacy.d.ts +1 -1
  99. package/dist/types/legacy.d.ts.map +1 -1
  100. package/dist/types/plugins.d.ts +1 -1
  101. package/dist/types/plugins.d.ts.map +1 -1
  102. package/dist/types/query.d.ts +2 -2
  103. package/dist/types/query.d.ts.map +1 -1
  104. package/dist/types/runtime.d.ts +2 -2
  105. package/dist/types/runtime.d.ts.map +1 -1
  106. package/dist/types/schema.d.ts +1 -1
  107. package/dist/types/schema.d.ts.map +1 -1
  108. package/dist/utils/error-responses.js +1 -1
  109. package/dist/utils/field-conversion.js +1 -1
  110. package/dist/utils/type-guards.d.ts +1 -1
  111. package/dist/utils/type-guards.d.ts.map +1 -1
  112. package/package.json +35 -21
  113. package/dist/generated/types/cms.d.ts.map +0 -1
  114. package/dist/types/cms.d.ts +0 -8
  115. package/dist/types/cms.d.ts.map +0 -1
  116. package/dist/types/cms.js +0 -6
  117. /package/dist/generated/types/{cms.js → admin.js} +0 -0
package/README.md CHANGED
@@ -1,19 +1,25 @@
1
1
  # @revealui/core
2
2
 
3
- The core runtime engine for RevealUI — collections, admin UI, rich text, security, observability, and plugins.
3
+ [![npm version](https://img.shields.io/npm/v/@revealui/core)](https://www.npmjs.com/package/@revealui/core)
4
+ [![npm downloads](https://img.shields.io/npm/dw/@revealui/core)](https://www.npmjs.com/package/@revealui/core)
5
+ [![license](https://img.shields.io/npm/l/@revealui/core)](https://github.com/RevealUIStudio/revealui/blob/main/LICENSE)
6
+
7
+ The core runtime engine for [RevealUI](https://revealui.com) - collections, admin UI, rich text, security, observability, and plugins.
8
+
9
+ Part of the [RevealUI monorepo](https://github.com/RevealUIStudio/revealui) - open-source agentic business runtime.
4
10
 
5
11
  ## Features
6
12
 
7
- - **Collections & CRUD**Define content types with field hooks, access control, and validation
8
- - **Admin Dashboard**Ready-to-use React admin UI (collection browser, document editor, global forms)
9
- - **Rich Text**Lexical-based editor with 20+ features (bold, headings, lists, links, images, blocks)
10
- - **Security**CORS, CSP, HSTS, RBAC/ABAC policy engine, encryption (AES-256-GCM), audit logging
11
- - **GDPR Compliance**Consent management, data export, deletion, anonymization, breach reporting
12
- - **Observability**Structured logging, process health monitoring, alert system, graceful shutdown
13
- - **Plugins**Extensible plugin system (form builder, nested docs, redirects)
14
- - **Feature Gating**Tier-based licensing (free, pro, max, enterprise/Forge) with JWT license keys
15
- - **Database**PostgreSQL adapters (NeonDB + PGlite for testing), connection pooling, SSL/TLS
16
- - **Storage**Pluggable storage interface (Vercel Blob adapter included)
13
+ - **Collections & CRUD** - Define content types with field hooks, access control, and validation
14
+ - **Admin Dashboard** - Ready-to-use React admin UI (collection browser, document editor, global forms)
15
+ - **Rich Text Fields** - Lexical-powered content fields in the admin editor (bold, headings, lists, links, images, blocks)
16
+ - **Security** - CORS, CSP, HSTS, RBAC/ABAC policy engine, encryption (AES-256-GCM), audit logging
17
+ - **GDPR Compliance** - Consent management, data export, deletion, anonymization, breach reporting
18
+ - **Observability** - Structured logging, process health monitoring, alert system, graceful shutdown
19
+ - **Plugins** - Extensible plugin system (form builder, nested docs, redirects)
20
+ - **Feature Gating** - Tier-based licensing (free, pro, max, enterprise/Forge) with JWT license keys
21
+ - **Database** - PostgreSQL adapters (NeonDB + PGlite for testing), connection pooling, SSL/TLS
22
+ - **Storage** - Pluggable storage interface (Vercel Blob adapter included)
17
23
 
18
24
  ## Installation
19
25
 
@@ -78,15 +84,15 @@ function App() {
78
84
  }
79
85
  ```
80
86
 
81
- ### Rich Text
87
+ ### Rich Text Fields
82
88
 
83
- ```typescript
84
- import { RichTextEditor, BoldFeature, HeadingFeature, ListFeature } from '@revealui/core/richtext/client'
89
+ Rich text editing is available as a field type in the admin dashboard.
90
+ Define a `richText` field in your collection schema - the admin UI renders
91
+ a Lexical editor automatically. Not a standalone component.
85
92
 
86
- <RichTextEditor
87
- features={[BoldFeature(), HeadingFeature(), ListFeature()]}
88
- onChange={(json) => console.log(json)}
89
- />
93
+ ```typescript
94
+ // In your collection definition:
95
+ defineField({ name: 'body', type: 'richText' })
90
96
  ```
91
97
 
92
98
  ### Feature Gating
@@ -150,20 +156,20 @@ pnpm dev
150
156
  - You're building a content-driven app and need collections, admin UI, and CRUD out of the box
151
157
  - You need RBAC/ABAC access control, GDPR compliance, or feature gating by license tier
152
158
  - You want a rich text editor (Lexical) integrated with your CMS
153
- - **Not** for standalone UI componentsuse `@revealui/presentation`
154
- - **Not** for raw database queriesuse `@revealui/db` directly
159
+ - **Not** for standalone UI components - use `@revealui/presentation`
160
+ - **Not** for raw database queries - use `@revealui/db` directly
155
161
 
156
162
  ## JOSHUA Alignment
157
163
 
158
- - **Sovereign**: Self-hosted runtime engineno SaaS dependency for content management, auth, or storage
164
+ - **Sovereign**: Self-hosted runtime engine - no vendor dependency for content management, auth, or storage
159
165
  - **Unified**: One `buildConfig()` call wires collections, globals, plugins, security, and feature gates into a single configuration
160
166
  - **Adaptive**: Plugin system and tier-based feature gating let the platform evolve without breaking existing deployments
161
167
 
162
168
  ## Related
163
169
 
164
- - [Contracts Package](../contracts/README.md)Zod schemas and TypeScript types
165
- - [DB Package](../db/README.md)Drizzle ORM schema
166
- - [Auth Package](../auth/README.md)Authentication system
170
+ - [Contracts Package](../contracts/README.md) - Zod schemas and TypeScript types
171
+ - [DB Package](../db/README.md) - Drizzle ORM schema
172
+ - [Auth Package](../auth/README.md) - Authentication system
167
173
  - [Architecture Guide](../../docs/ARCHITECTURE.md)
168
174
 
169
175
  ## License
@@ -68,7 +68,7 @@ export declare function getRateLimitPresets(): Readonly<Record<string, RateLimit
68
68
  */
69
69
  export declare function resetRateLimitPresets(): void;
70
70
  /**
71
- * Rate limit presetsbackward-compatible accessor.
71
+ * Rate limit presets - backward-compatible accessor.
72
72
  *
73
73
  * Reads from the mutable `rateLimitPresets` so overrides from
74
74
  * `configureRateLimitPresets()` are reflected automatically.
@@ -5,9 +5,9 @@
5
5
  */
6
6
  import { NextResponse } from 'next/server';
7
7
  import { logger } from '../observability/logger.js';
8
- // WARNING: In-memory storeresets on every serverless cold start or process restart.
8
+ // WARNING: In-memory store - resets on every serverless cold start or process restart.
9
9
  // This module is NOT used by any app in production (API uses apps/api/src/middleware/rate-limit.ts,
10
- // CMS uses @revealui/auth/server which is DB-backed). Exported for framework consumers only.
10
+ // admin uses @revealui/auth/server which is DB-backed). Exported for framework consumers only.
11
11
  // Replace with a database-backed store before using in production.
12
12
  const rateLimitStore = new Map();
13
13
  /**
@@ -231,7 +231,7 @@ export function resetRateLimitPresets() {
231
231
  rateLimitPresets = { ...DEFAULT_RATE_LIMIT_PRESETS };
232
232
  }
233
233
  /**
234
- * Rate limit presetsbackward-compatible accessor.
234
+ * Rate limit presets - backward-compatible accessor.
235
235
  *
236
236
  * Reads from the mutable `rateLimitPresets` so overrides from
237
237
  * `configureRateLimitPresets()` are reflected automatically.
@@ -1 +1 @@
1
- {"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/api/response-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7D,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAiBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAW7D;AAyBD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA0B1F;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAY9D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAW1D;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,YAAY,CA6B3F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,YAAiB,IAChD,SAAS,WAAW,EAAE,MAAM,MAAM,OAAO,CAAC,YAAY,CAAC,oCAqBtE;AAED;;GAEG;AACH,wBAAgB,aAAa;;;;;;EA0B5B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,GAAE,MAAc,GAAG,MAAM,CAAC,OAAO,CAO5E;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAQ9E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGrE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,YAAY,CAKxD;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+ChB,CAAC;AAEX;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC5B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAsCvC"}
1
+ {"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/api/response-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7D,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAiBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAW7D;AAyBD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA0B1F;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,YAAY,EACtB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAc9D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAW1D;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,YAAY,CA6B3F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,YAAiB,IAChD,SAAS,WAAW,EAAE,MAAM,MAAM,OAAO,CAAC,YAAY,CAAC,oCAqBtE;AAED;;GAEG;AACH,wBAAgB,aAAa;;;;;;EA0B5B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,GAAE,MAAc,GAAG,MAAM,CAAC,OAAO,CAO5E;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAQ9E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGrE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,YAAY,CAKxD;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+ChB,CAAC;AAEX;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC5B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAsCvC"}
@@ -100,7 +100,9 @@ export function invalidateCacheKey(key) {
100
100
  */
101
101
  export function invalidateCachePattern(pattern) {
102
102
  let count = 0;
103
- const regex = new RegExp(pattern.replace('*', '.*'));
103
+ // Escape all regex special characters, then convert glob wildcards (*) to .*
104
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
105
+ const regex = new RegExp(`^${escaped.replaceAll('\\*', '.*')}$`);
104
106
  for (const key of cacheStore.keys()) {
105
107
  if (regex.test(key)) {
106
108
  cacheStore.delete(key);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * RevealUI REST API Implementation
3
3
  *
4
- * Based on RevealUI CMS REST API but adapted for RevealUI.
4
+ * Based on RevealUI admin REST API but adapted for RevealUI.
5
5
  *
6
6
  * WARNING: This module is server-only.
7
7
  * Do NOT import in client-side code or edge runtime.
package/dist/api/rest.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * RevealUI REST API Implementation
3
3
  *
4
- * Based on RevealUI CMS REST API but adapted for RevealUI.
4
+ * Based on RevealUI admin REST API but adapted for RevealUI.
5
5
  *
6
6
  * WARNING: This module is server-only.
7
7
  * Do NOT import in client-side code or edge runtime.
@@ -24,7 +24,7 @@ function parseQueryParams(searchParams) {
24
24
  if (fallbackLocale) {
25
25
  options.fallbackLocale = fallbackLocale;
26
26
  }
27
- // Parse overrideAccessSECURITY: This flag bypasses collection access control.
27
+ // Parse overrideAccess - SECURITY: This flag bypasses collection access control.
28
28
  // It is stripped from external API requests below to prevent abuse.
29
29
  // Only server-side code should set overrideAccess=true (e.g., internal hooks, seed scripts).
30
30
  const overrideAccess = searchParams.get('overrideAccess');
@@ -2,7 +2,7 @@
2
2
  * Query Result Caching
3
3
  *
4
4
  * In-memory Map-based cache for database queries. No external dependencies
5
- * (Redis, Memcached, etc.)RevealUI uses PostgreSQL + ElectricSQL/PGlite
5
+ * (Redis, Memcached, etc.) - RevealUI uses PostgreSQL + ElectricSQL/PGlite
6
6
  * for all persistence and sync.
7
7
  */
8
8
  interface CacheOptions {
@@ -2,7 +2,7 @@
2
2
  * Query Result Caching
3
3
  *
4
4
  * In-memory Map-based cache for database queries. No external dependencies
5
- * (Redis, Memcached, etc.)RevealUI uses PostgreSQL + ElectricSQL/PGlite
5
+ * (Redis, Memcached, etc.) - RevealUI uses PostgreSQL + ElectricSQL/PGlite
6
6
  * for all persistence and sync.
7
7
  */
8
8
  import { logger } from '../observability/logger.js';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Caching utilitiesre-exported from @revealui/cache
2
+ * Caching utilities - re-exported from @revealui/cache
3
3
  * New code should import directly from '@revealui/cache'.
4
4
  */
5
5
  export * from '@revealui/cache';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Caching utilitiesre-exported from @revealui/cache
2
+ * Caching utilities - re-exported from @revealui/cache
3
3
  * New code should import directly from '@revealui/cache'.
4
4
  */
5
5
  export * from '@revealui/cache';
@@ -1 +1 @@
1
- {"version":3,"file":"AdminDashboard.d.ts","sourceRoot":"","sources":["../../../../src/client/admin/components/AdminDashboard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,YAAY,EAGb,MAAM,yBAAyB,CAAC;AAUjC,UAAU,mBAAmB;IAC3B,MAAM,EAAE,YAAY,CAAC;CACtB;AA6XD,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,mBAAmB,2CAqS7D"}
1
+ {"version":3,"file":"AdminDashboard.d.ts","sourceRoot":"","sources":["../../../../src/client/admin/components/AdminDashboard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,YAAY,EAGb,MAAM,yBAAyB,CAAC;AAUjC,UAAU,mBAAmB;IAC3B,MAAM,EAAE,YAAY,CAAC;CACtB;AA6XD,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,mBAAmB,2CAkV7D"}
@@ -72,7 +72,7 @@ function SignOutButton() {
72
72
  });
73
73
  }
74
74
  catch {
75
- // Sign out even if the API call failsclear client state regardless
75
+ // Sign out even if the API call fails - clear client state regardless
76
76
  }
77
77
  window.location.href = '/login';
78
78
  }, []);
@@ -82,7 +82,7 @@ function SignOutButton() {
82
82
  // Dashboard home view
83
83
  // =============================================================================
84
84
  function DashboardHome({ collections, globals, onCollectionClick, onGlobalClick, }) {
85
- return (_jsxs("div", { className: "min-h-screen bg-gray-50", children: [_jsx("header", { className: "bg-white shadow-sm border-b", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: _jsxs("div", { className: "flex justify-between items-center py-4", children: [_jsx("div", { className: "flex items-center", children: _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "RevealUI Admin" }) }), _jsxs("div", { className: "flex items-center space-x-4", children: [_jsx("span", { className: "text-sm text-gray-500", children: "v0.1.0" }), _jsx(SignOutButton, {})] })] }) }) }), _jsx("main", { className: "max-w-7xl mx-auto py-6 sm:px-6 lg:px-8", children: _jsx("div", { className: "px-4 py-6 sm:px-0", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: [_jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", "aria-label": "Collections", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", role: "img", children: [_jsx("title", { children: "Collections" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Collections" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: collections.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: collections.length > 0 ? (_jsx("ul", { className: "space-y-1 max-h-48 overflow-y-auto", children: collections.map((collection) => (_jsx("li", { className: "text-gray-600 hover:text-gray-900", children: _jsx("button", { type: "button", onClick: () => onCollectionClick(collection), className: "hover:underline cursor-pointer", children: String(collection.slug) }) }, String(collection.slug)))) })) : (_jsx("p", { className: "text-gray-500", children: "No collections configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-labelledby": "globals-icon-title", role: "img", children: [_jsx("title", { id: "globals-icon-title", children: "Globals" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Globals" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: globals.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: globals.length > 0 ? (_jsx("ul", { className: "space-y-1 max-h-32 overflow-y-auto", children: globals.map((global) => (_jsx("li", { className: "text-gray-600 hover:text-gray-900", children: _jsx("button", { type: "button", onClick: () => onGlobalClick(global), className: "hover:underline cursor-pointer", children: global.label || String(global.slug) }) }, String(global.slug)))) })) : (_jsx("p", { className: "text-gray-500", children: "No globals configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx("div", { className: "h-8 w-8 bg-green-100 rounded-full flex items-center justify-center", children: _jsxs("svg", { className: "h-5 w-5 text-green-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-labelledby": "system-status-icon-title", role: "img", children: [_jsx("title", { id: "system-status-icon-title", children: "System Status: Healthy" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" })] }) }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "System Status" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: "Healthy" })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm text-gray-600", children: "RevealUI CMS is running successfully" }) })] })] }) }) })] }));
85
+ return (_jsxs("div", { className: "min-h-screen bg-gray-50", children: [_jsx("header", { className: "bg-white shadow-sm border-b", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: _jsxs("div", { className: "flex justify-between items-center py-4", children: [_jsx("div", { className: "flex items-center", children: _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "RevealUI Admin" }) }), _jsxs("div", { className: "flex items-center space-x-4", children: [_jsx("span", { className: "text-sm text-gray-500", children: "v0.1.0" }), _jsx(SignOutButton, {})] })] }) }) }), _jsx("main", { className: "max-w-7xl mx-auto py-6 sm:px-6 lg:px-8", children: _jsx("div", { className: "px-4 py-6 sm:px-0", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: [_jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", "aria-label": "Collections", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", role: "img", children: [_jsx("title", { children: "Collections" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Collections" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: collections.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: collections.length > 0 ? (_jsx("ul", { className: "space-y-1 max-h-48 overflow-y-auto", children: collections.map((collection) => (_jsx("li", { className: "text-gray-600 hover:text-gray-900", children: _jsx("button", { type: "button", onClick: () => onCollectionClick(collection), className: "hover:underline cursor-pointer", children: String(collection.slug) }) }, String(collection.slug)))) })) : (_jsx("p", { className: "text-gray-500", children: "No collections configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-labelledby": "globals-icon-title", role: "img", children: [_jsx("title", { id: "globals-icon-title", children: "Globals" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Globals" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: globals.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: globals.length > 0 ? (_jsx("ul", { className: "space-y-1 max-h-32 overflow-y-auto", children: globals.map((global) => (_jsx("li", { className: "text-gray-600 hover:text-gray-900", children: _jsx("button", { type: "button", onClick: () => onGlobalClick(global), className: "hover:underline cursor-pointer", children: global.label || String(global.slug) }) }, String(global.slug)))) })) : (_jsx("p", { className: "text-gray-500", children: "No globals configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx("div", { className: "h-8 w-8 bg-green-100 rounded-full flex items-center justify-center", children: _jsxs("svg", { className: "h-5 w-5 text-green-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-labelledby": "system-status-icon-title", role: "img", children: [_jsx("title", { id: "system-status-icon-title", children: "System Status: Healthy" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" })] }) }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "System Status" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: "Healthy" })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm text-gray-600", children: "RevealUI admin is running successfully" }) })] })] }) }) })] }));
86
86
  }
87
87
  // =============================================================================
88
88
  // Error handling helpers
@@ -185,6 +185,49 @@ export function AdminDashboard({ config }) {
185
185
  });
186
186
  }
187
187
  };
188
+ const handleBulkDelete = async (ids) => {
189
+ if (!state.view.collection)
190
+ return;
191
+ const confirmed = window.confirm(`Are you sure you want to delete ${ids.length} ${String(state.view.collection.slug)}? This action cannot be undone.`);
192
+ if (!confirmed)
193
+ return;
194
+ try {
195
+ dispatch({ type: 'SET_ERROR', error: null });
196
+ await apiClient.batchDelete({
197
+ collection: String(state.view.collection.slug),
198
+ ids,
199
+ });
200
+ if (state.view.collection) {
201
+ await fetchCollection(state.view.collection, state.page);
202
+ }
203
+ dispatch({ type: 'SET_SUCCESS', message: `${ids.length} documents deleted` });
204
+ }
205
+ catch (err) {
206
+ const msg = extractErrorMessage(err, 'Bulk delete failed. Please try again.');
207
+ logApiError(err, 'Bulk delete failed');
208
+ dispatch({ type: 'SET_ERROR', error: msg });
209
+ }
210
+ };
211
+ const handleBulkPublish = async (ids) => {
212
+ if (!state.view.collection)
213
+ return;
214
+ try {
215
+ dispatch({ type: 'SET_ERROR', error: null });
216
+ await apiClient.batchUpdate({
217
+ collection: String(state.view.collection.slug),
218
+ items: ids.map((id) => ({ id, status: 'published' })),
219
+ });
220
+ if (state.view.collection) {
221
+ await fetchCollection(state.view.collection, state.page);
222
+ }
223
+ dispatch({ type: 'SET_SUCCESS', message: `${ids.length} documents published` });
224
+ }
225
+ catch (err) {
226
+ const msg = extractErrorMessage(err, 'Bulk publish failed. Please try again.');
227
+ logApiError(err, 'Bulk publish failed');
228
+ dispatch({ type: 'SET_ERROR', error: msg });
229
+ }
230
+ };
188
231
  const handleDelete = async (document) => {
189
232
  if (!(state.view.collection && document.id))
190
233
  return;
@@ -293,7 +336,7 @@ export function AdminDashboard({ config }) {
293
336
  const collection = state.view.collection;
294
337
  if (collection)
295
338
  void fetchCollection(collection, nextPage);
296
- }, deleting: state.deleting })] })] }));
339
+ }, deleting: state.deleting, onBulkDelete: (ids) => void handleBulkDelete(ids), onBulkPublish: (ids) => void handleBulkPublish(ids) })] })] }));
297
340
  }
298
341
  // ── Document edit/create view ─────────────────────────────────────────
299
342
  if (state.view.type === 'edit' && state.view.collection) {
@@ -10,7 +10,9 @@ interface CollectionListProps {
10
10
  onDelete: (doc: RevealDocument) => void;
11
11
  onPageChange: (page: number) => void;
12
12
  deleting?: string | null;
13
+ onBulkDelete?: (ids: string[]) => void;
14
+ onBulkPublish?: (ids: string[]) => void;
13
15
  }
14
- export declare function CollectionList({ collection, documents, totalDocs, page, totalPages, onCreate, onEdit, onDelete, onPageChange, deleting, }: CollectionListProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function CollectionList({ collection, documents, totalDocs, page, totalPages, onCreate, onEdit, onDelete, onPageChange, deleting, onBulkDelete, onBulkPublish, }: CollectionListProps): import("react/jsx-runtime").JSX.Element;
15
17
  export {};
16
18
  //# sourceMappingURL=CollectionList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionList.d.ts","sourceRoot":"","sources":["../../../../src/client/admin/components/CollectionList.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,sBAAsB,EACtB,cAAc,EAEf,MAAM,yBAAyB,CAAC;AAoCjC,UAAU,mBAAmB;IAC3B,UAAU,EAAE,sBAAsB,CAAC;IACnC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,SAAS,EACT,SAAS,EACT,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,QAAQ,GACT,EAAE,mBAAmB,2CAqKrB"}
1
+ {"version":3,"file":"CollectionList.d.ts","sourceRoot":"","sources":["../../../../src/client/admin/components/CollectionList.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,sBAAsB,EACtB,cAAc,EAEf,MAAM,yBAAyB,CAAC;AAoCjC,UAAU,mBAAmB;IAC3B,UAAU,EAAE,sBAAsB,CAAC;IACnC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACzC;AAED,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,SAAS,EACT,SAAS,EACT,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,aAAa,GACd,EAAE,mBAAmB,2CA6QrB"}
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
3
4
  function getFieldLabel(field) {
4
5
  const { label } = field;
5
6
  if (typeof label === 'function') {
@@ -33,14 +34,62 @@ function formatDateValue(value) {
33
34
  }
34
35
  return '';
35
36
  }
36
- export function CollectionList({ collection, documents, totalDocs, page, totalPages, onCreate, onEdit, onDelete, onPageChange, deleting, }) {
37
+ export function CollectionList({ collection, documents, totalDocs, page, totalPages, onCreate, onEdit, onDelete, onPageChange, deleting, onBulkDelete, onBulkPublish, }) {
38
+ const [selectedIds, setSelectedIds] = useState(new Set());
39
+ const [bulkLoading, setBulkLoading] = useState(false);
40
+ const hasBulk = Boolean(onBulkDelete || onBulkPublish);
41
+ const allSelected = documents.length > 0 && selectedIds.size === documents.length;
42
+ const someSelected = selectedIds.size > 0 && !allSelected;
43
+ const toggleAll = () => {
44
+ if (allSelected) {
45
+ setSelectedIds(new Set());
46
+ }
47
+ else {
48
+ setSelectedIds(new Set(documents.map((d) => String(d.id))));
49
+ }
50
+ };
51
+ const toggleOne = (id) => {
52
+ setSelectedIds((prev) => {
53
+ const next = new Set(prev);
54
+ if (next.has(id)) {
55
+ next.delete(id);
56
+ }
57
+ else {
58
+ next.add(id);
59
+ }
60
+ return next;
61
+ });
62
+ };
63
+ const handleBulkAction = async (action) => {
64
+ const ids = [...selectedIds];
65
+ if (ids.length === 0)
66
+ return;
67
+ setBulkLoading(true);
68
+ try {
69
+ if (action === 'delete' && onBulkDelete)
70
+ await onBulkDelete(ids);
71
+ if (action === 'publish' && onBulkPublish)
72
+ await onBulkPublish(ids);
73
+ setSelectedIds(new Set());
74
+ }
75
+ finally {
76
+ setBulkLoading(false);
77
+ }
78
+ };
37
79
  // Filter to only include fields with names (exclude layout fields) that are visible
38
80
  const displayFields = collection.fields
39
81
  .filter((field) => {
40
82
  return field.name && field.admin?.position !== 'sidebar' && !field.admin?.hidden;
41
83
  })
42
84
  .slice(0, 5); // Show first 5 visible fields
43
- return (_jsxs("div", { className: "bg-white shadow overflow-hidden sm:rounded-md", children: [_jsxs("div", { className: "px-4 py-5 sm:px-6 flex justify-between items-center", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg leading-6 font-medium text-gray-900 capitalize", children: collection.slug }), _jsxs("p", { className: "mt-1 max-w-2xl text-sm text-gray-500", children: [totalDocs, " ", totalDocs === 1 ? 'document' : 'documents'] })] }), _jsxs("button", { type: "button", onClick: onCreate, className: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", children: [_jsxs("svg", { className: "-ml-1 mr-2 h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", role: "img", focusable: "false", children: [_jsx("title", { children: "Create New" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" })] }), "Create New"] })] }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [_jsx("thead", { className: "bg-gray-50", children: _jsxs("tr", { children: [displayFields.map((field) => (_jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: getFieldLabel(field) }, field.name))), _jsx("th", { scope: "col", className: "relative px-6 py-3", children: _jsx("span", { className: "sr-only", children: "Actions" }) })] }) }), _jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: documents.length === 0 ? (_jsx("tr", { children: _jsxs("td", { colSpan: displayFields.length + 1, className: "px-6 py-4 text-center text-sm text-gray-500", children: ["No documents found.", ' ', _jsx("button", { type: "button", onClick: onCreate, className: "text-indigo-600 hover:text-indigo-500", children: "Create the first one" }), "."] }) })) : (documents.map((doc) => (_jsxs("tr", { className: "hover:bg-gray-50", children: [displayFields.map((field) => (_jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-900", children: renderFieldValue(field.name ? doc[field.name] : undefined, field) }, field.name))), _jsxs("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2", children: [_jsx("button", { type: "button", onClick: () => onEdit(doc), className: "text-indigo-600 hover:text-indigo-900 disabled:opacity-50 disabled:cursor-not-allowed", disabled: deleting !== null, children: "Edit" }), _jsx("button", { type: "button", onClick: () => onDelete(doc), className: "text-red-600 hover:text-red-900 disabled:opacity-50 disabled:cursor-not-allowed", disabled: deleting !== null, children: deleting === doc.id ? 'Deleting...' : 'Delete' })] })] }, doc.id)))) })] }) }), totalPages > 1 && (_jsxs("div", { className: "bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6", children: [_jsxs("div", { className: "flex-1 flex justify-between sm:hidden", children: [_jsx("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: page <= 1, className: "relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Previous" }), _jsx("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: page >= totalPages, className: "ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Next" })] }), _jsxs("div", { className: "hidden sm:flex-1 sm:flex sm:items-center sm:justify-between", children: [_jsx("div", { children: _jsxs("p", { className: "text-sm text-gray-700", children: ["Showing page ", _jsx("span", { className: "font-medium", children: page }), " of", ' ', _jsx("span", { className: "font-medium", children: totalPages })] }) }), _jsx("div", { children: _jsxs("nav", { className: "relative z-0 inline-flex rounded-md shadow-sm -space-x-px", children: [_jsx("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: page <= 1, className: "relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Previous" }), _jsx("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: page >= totalPages, className: "relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Next" })] }) })] })] }))] }));
85
+ const colCount = displayFields.length + 1 + (hasBulk ? 1 : 0);
86
+ return (_jsxs("div", { className: "bg-white shadow overflow-hidden sm:rounded-md", children: [_jsxs("div", { className: "px-4 py-5 sm:px-6 flex justify-between items-center", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg leading-6 font-medium text-gray-900 capitalize", children: collection.slug }), _jsxs("p", { className: "mt-1 max-w-2xl text-sm text-gray-500", children: [totalDocs, " ", totalDocs === 1 ? 'document' : 'documents'] })] }), _jsxs("button", { type: "button", onClick: onCreate, className: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", children: [_jsxs("svg", { className: "-ml-1 mr-2 h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", role: "img", focusable: "false", children: [_jsx("title", { children: "Create New" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" })] }), "Create New"] })] }), hasBulk && selectedIds.size > 0 && (_jsxs("div", { className: "bg-indigo-50 border-y border-indigo-100 px-4 py-2 flex items-center gap-3", children: [_jsxs("span", { className: "text-sm font-medium text-indigo-700", children: [selectedIds.size, " selected"] }), onBulkDelete && (_jsx("button", { type: "button", onClick: () => void handleBulkAction('delete'), disabled: bulkLoading, className: "inline-flex items-center px-3 py-1 text-xs font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 disabled:opacity-50", children: "Delete" })), onBulkPublish && (_jsx("button", { type: "button", onClick: () => void handleBulkAction('publish'), disabled: bulkLoading, className: "inline-flex items-center px-3 py-1 text-xs font-medium rounded-md text-green-700 bg-green-100 hover:bg-green-200 disabled:opacity-50", children: "Publish" })), _jsx("button", { type: "button", onClick: () => setSelectedIds(new Set()), className: "ml-auto text-xs text-gray-500 hover:text-gray-700", children: "Clear selection" })] })), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [_jsx("thead", { className: "bg-gray-50", children: _jsxs("tr", { children: [hasBulk && (_jsx("th", { scope: "col", className: "w-10 px-3 py-3", children: _jsx("input", { type: "checkbox", checked: allSelected, ref: (el) => {
87
+ if (el)
88
+ el.indeterminate = someSelected;
89
+ }, onChange: toggleAll, className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500", "aria-label": "Select all" }) })), displayFields.map((field) => (_jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: getFieldLabel(field) }, field.name))), _jsx("th", { scope: "col", className: "relative px-6 py-3", children: _jsx("span", { className: "sr-only", children: "Actions" }) })] }) }), _jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: documents.length === 0 ? (_jsx("tr", { children: _jsxs("td", { colSpan: colCount, className: "px-6 py-4 text-center text-sm text-gray-500", children: ["No documents found.", ' ', _jsx("button", { type: "button", onClick: onCreate, className: "text-indigo-600 hover:text-indigo-500", children: "Create the first one" }), "."] }) })) : (documents.map((doc) => {
90
+ const docId = String(doc.id);
91
+ return (_jsxs("tr", { className: `hover:bg-gray-50 ${selectedIds.has(docId) ? 'bg-indigo-50' : ''}`, children: [hasBulk && (_jsx("td", { className: "w-10 px-3 py-4", children: _jsx("input", { type: "checkbox", checked: selectedIds.has(docId), onChange: () => toggleOne(docId), className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500", "aria-label": `Select ${docId}` }) })), displayFields.map((field) => (_jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-900", children: renderFieldValue(field.name ? doc[field.name] : undefined, field) }, field.name))), _jsxs("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2", children: [_jsx("button", { type: "button", onClick: () => onEdit(doc), className: "text-indigo-600 hover:text-indigo-900 disabled:opacity-50 disabled:cursor-not-allowed", disabled: deleting !== null, children: "Edit" }), _jsx("button", { type: "button", onClick: () => onDelete(doc), className: "text-red-600 hover:text-red-900 disabled:opacity-50 disabled:cursor-not-allowed", disabled: deleting !== null, children: deleting === docId ? 'Deleting...' : 'Delete' })] })] }, docId));
92
+ })) })] }) }), totalPages > 1 && (_jsxs("div", { className: "bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6", children: [_jsxs("div", { className: "flex-1 flex justify-between sm:hidden", children: [_jsx("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: page <= 1, className: "relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Previous" }), _jsx("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: page >= totalPages, className: "ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Next" })] }), _jsxs("div", { className: "hidden sm:flex-1 sm:flex sm:items-center sm:justify-between", children: [_jsx("div", { children: _jsxs("p", { className: "text-sm text-gray-700", children: ["Showing page ", _jsx("span", { className: "font-medium", children: page }), " of", ' ', _jsx("span", { className: "font-medium", children: totalPages })] }) }), _jsx("div", { children: _jsxs("nav", { className: "relative z-0 inline-flex rounded-md shadow-sm -space-x-px", children: [_jsx("button", { type: "button", onClick: () => onPageChange(page - 1), disabled: page <= 1, className: "relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Previous" }), _jsx("button", { type: "button", onClick: () => onPageChange(page + 1), disabled: page >= totalPages, className: "relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: "Next" })] }) })] })] }))] }));
44
93
  }
45
94
  function renderFieldValue(value, field) {
46
95
  if (value === null || value === undefined) {
@@ -2,7 +2,7 @@
2
2
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { lazy, Suspense, useCallback, useState } from 'react';
4
4
  // Lazy-loaded so Lexical (~1.2MB) is only bundled for edit pages with richText
5
- // fieldslist/dashboard pages skip the entire Lexical chunk.
5
+ // fields - list/dashboard pages skip the entire Lexical chunk.
6
6
  const RichTextEditor = lazy(() => import('../../richtext/RichTextEditor.js').then((m) => ({ default: m.RichTextEditor })));
7
7
  function getFieldLabel(field) {
8
8
  const { label } = field;
@@ -158,7 +158,7 @@ function PointFieldRenderer({ field, value, onChange, }) {
158
158
  return (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { children: [_jsx("label", { htmlFor: `${field.name}-lat`, className: "block text-xs font-medium text-gray-600", children: "Latitude" }), _jsx("input", { type: "number", id: `${field.name}-lat`, value: point.lat, onChange: (e) => onChange({ ...point, lat: Number(e.target.value) }), className: baseClasses, step: "any", min: -90, max: 90 })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: `${field.name}-lng`, className: "block text-xs font-medium text-gray-600", children: "Longitude" }), _jsx("input", { type: "number", id: `${field.name}-lng`, value: point.lng, onChange: (e) => onChange({ ...point, lng: Number(e.target.value) }), className: baseClasses, step: "any", min: -180, max: 180 })] })] }));
159
159
  }
160
160
  // ---------------------------------------------------------------------------
161
- // Main FieldInputrenders the appropriate control for each field type
161
+ // Main FieldInput - renders the appropriate control for each field type
162
162
  // ---------------------------------------------------------------------------
163
163
  function FieldInput({ field, value, onChange }) {
164
164
  const baseClasses = 'mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm';
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  export function RootPage({ config }) {
3
3
  const collections = (config.collections || []);
4
4
  const globals = (config.globals || []);
5
- return (_jsxs("div", { className: "min-h-screen bg-gray-50", children: [_jsx("header", { className: "bg-white shadow-sm border-b", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: _jsxs("div", { className: "flex justify-between items-center py-4", children: [_jsx("div", { className: "flex items-center", children: _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "RevealUI Admin" }) }), _jsx("div", { className: "flex items-center space-x-4", children: _jsx("span", { className: "text-sm text-gray-500", children: "v0.1.0" }) })] }) }) }), _jsx("main", { className: "max-w-7xl mx-auto py-6 sm:px-6 lg:px-8", children: _jsx("div", { className: "px-4 py-6 sm:px-0", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: [_jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("title", { children: "Collections icon" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Collections" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: collections.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: collections.length > 0 ? (_jsx("ul", { className: "space-y-1", children: collections.map((collection) => (_jsxs("li", { className: "text-gray-600", children: [_jsx("span", { className: "font-medium", children: collection.slug }), _jsxs("span", { className: "ml-2 text-xs text-gray-400", children: ["(", collection.fields?.length || 0, " fields)"] })] }, collection.slug))) })) : (_jsx("p", { className: "text-gray-500", children: "No collections configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("title", { children: "Globals icon" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Globals" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: globals.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: globals.length > 0 ? (_jsx("ul", { className: "space-y-1", children: globals.map((global) => (_jsxs("li", { className: "text-gray-600", children: [_jsx("span", { className: "font-medium", children: global.slug }), _jsxs("span", { className: "ml-2 text-xs text-gray-400", children: ["(", global.fields?.length || 0, " fields)"] })] }, global.slug))) })) : (_jsx("p", { className: "text-gray-500", children: "No globals configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx("div", { className: "h-8 w-8 bg-green-100 rounded-full flex items-center justify-center", children: _jsxs("svg", { className: "h-5 w-5 text-green-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("title", { children: "System operational" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" })] }) }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "System Status" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: "Operational" })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsxs("div", { className: "text-sm text-gray-600", children: ["RevealUI CMS is running successfully with ", collections.length, " collections and", ' ', globals.length, " globals configured."] }) })] })] }) }) })] }));
5
+ return (_jsxs("div", { className: "min-h-screen bg-gray-50", children: [_jsx("header", { className: "bg-white shadow-sm border-b", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: _jsxs("div", { className: "flex justify-between items-center py-4", children: [_jsx("div", { className: "flex items-center", children: _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "RevealUI Admin" }) }), _jsx("div", { className: "flex items-center space-x-4", children: _jsx("span", { className: "text-sm text-gray-500", children: "v0.1.0" }) })] }) }) }), _jsx("main", { className: "max-w-7xl mx-auto py-6 sm:px-6 lg:px-8", children: _jsx("div", { className: "px-4 py-6 sm:px-0", children: _jsxs("div", { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: [_jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("title", { children: "Collections icon" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Collections" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: collections.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: collections.length > 0 ? (_jsx("ul", { className: "space-y-1", children: collections.map((collection) => (_jsxs("li", { className: "text-gray-600", children: [_jsx("span", { className: "font-medium", children: collection.slug }), _jsxs("span", { className: "ml-2 text-xs text-gray-400", children: ["(", collection.fields?.length || 0, " fields)"] })] }, collection.slug))) })) : (_jsx("p", { className: "text-gray-500", children: "No collections configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsxs("svg", { className: "h-8 w-8 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("title", { children: "Globals icon" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 100 4m0-4v2m0-6V4" })] }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "Globals" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: globals.length })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsx("div", { className: "text-sm", children: globals.length > 0 ? (_jsx("ul", { className: "space-y-1", children: globals.map((global) => (_jsxs("li", { className: "text-gray-600", children: [_jsx("span", { className: "font-medium", children: global.slug }), _jsxs("span", { className: "ml-2 text-xs text-gray-400", children: ["(", global.fields?.length || 0, " fields)"] })] }, global.slug))) })) : (_jsx("p", { className: "text-gray-500", children: "No globals configured" })) }) })] }), _jsxs("div", { className: "bg-white overflow-hidden shadow rounded-lg", children: [_jsx("div", { className: "p-5", children: _jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx("div", { className: "h-8 w-8 bg-green-100 rounded-full flex items-center justify-center", children: _jsxs("svg", { className: "h-5 w-5 text-green-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: [_jsx("title", { children: "System operational" }), _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" })] }) }) }), _jsx("div", { className: "ml-5 w-0 flex-1", children: _jsxs("dl", { children: [_jsx("dt", { className: "text-sm font-medium text-gray-500 truncate", children: "System Status" }), _jsx("dd", { className: "text-lg font-medium text-gray-900", children: "Operational" })] }) })] }) }), _jsx("div", { className: "bg-gray-50 px-5 py-3", children: _jsxs("div", { className: "text-sm text-gray-600", children: ["RevealUI admin is running successfully with ", collections.length, " collections and", ' ', globals.length, " globals configured."] }) })] })] }) }) })] }));
6
6
  }
7
7
  export function NotFoundPage() {
8
8
  return (_jsxs("div", { children: [_jsx("h1", { children: "404 - Page Not Found" }), _jsx("p", { children: "The requested admin page could not be found." })] }));
@@ -68,7 +68,7 @@ export interface APIClientOptions {
68
68
  baseURL?: string;
69
69
  }
70
70
  /**
71
- * API Client class for making authenticated requests to RevealUI CMS API
71
+ * API Client class for making authenticated requests to RevealUI admin API
72
72
  */
73
73
  export declare class APIClient {
74
74
  private baseURL;
@@ -97,6 +97,22 @@ export declare class APIClient {
97
97
  * Delete a document
98
98
  */
99
99
  delete(options: DeleteOptions): Promise<void>;
100
+ /**
101
+ * Batch delete documents
102
+ */
103
+ batchDelete(options: {
104
+ collection: string;
105
+ ids: string[];
106
+ }): Promise<void>;
107
+ /**
108
+ * Batch update documents (e.g., bulk publish)
109
+ */
110
+ batchUpdate(options: {
111
+ collection: string;
112
+ items: Array<{
113
+ id: string;
114
+ } & Record<string, unknown>>;
115
+ }): Promise<void>;
100
116
  /**
101
117
  * Find a global by slug
102
118
  */
@@ -1 +1 @@
1
- {"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../../../../src/client/admin/utils/apiClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,cAAc;IAC7C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,oBAAY,YAAY;IACtB,OAAO,YAAY;IACnB,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,QAAQ,cAAc;IACtB,MAAM,WAAW;CAClB;AAED,qBAAa,QAAS,SAAQ,KAAK;IAExB,IAAI,EAAE,YAAY;IAElB,MAAM,CAAC,EAAE,MAAM;IACf,KAAK,CAAC,EAAE,MAAM;gBAHd,IAAI,EAAE,YAAY,EACzB,OAAO,EAAE,MAAM,EACR,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,KAAK,CAAC,EAAE,MAAM,YAAA;CAKxB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA+BD;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE,gBAAqB;IAW1C;;OAEG;YACW,OAAO;IAqFrB;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAuBtD;;OAEG;IACG,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAUvE;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAY7D;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAY7D;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IASnD;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;IAerE;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;CAe1E;AAGD,eAAO,MAAM,SAAS,WAAkB,CAAC"}
1
+ {"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../../../../src/client/admin/utils/apiClient.ts"],"names":[],"mappings":"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"}
@@ -45,7 +45,7 @@ const getErrorField = (payload) => {
45
45
  return typeof firstError?.field === 'string' ? firstError.field : undefined;
46
46
  };
47
47
  /**
48
- * API Client class for making authenticated requests to RevealUI CMS API
48
+ * API Client class for making authenticated requests to RevealUI admin API
49
49
  */
50
50
  export class APIClient {
51
51
  baseURL;
@@ -182,6 +182,30 @@ export class APIClient {
182
182
  method: 'DELETE',
183
183
  });
184
184
  }
185
+ /**
186
+ * Batch delete documents
187
+ */
188
+ async batchDelete(options) {
189
+ await this.request('/api/batch/delete', {
190
+ method: 'POST',
191
+ body: JSON.stringify({
192
+ collection: options.collection,
193
+ items: options.ids.map((id) => ({ id })),
194
+ }),
195
+ });
196
+ }
197
+ /**
198
+ * Batch update documents (e.g., bulk publish)
199
+ */
200
+ async batchUpdate(options) {
201
+ await this.request('/api/batch/update', {
202
+ method: 'POST',
203
+ body: JSON.stringify({
204
+ collection: options.collection,
205
+ items: options.items,
206
+ }),
207
+ });
208
+ }
185
209
  /**
186
210
  * Find a global by slug
187
211
  */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * RevealUI Client-Side React Hooks
3
3
  *
4
- * Client-side React hooks and HOCs for RevealUI CMS
4
+ * Client-side React hooks and HOCs for RevealUI admin
5
5
  */
6
6
  import type React from 'react';
7
7
  import type { RevealUIContext } from '../types/index.js';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * RevealUI Client-Side React Hooks
3
3
  *
4
- * Client-side React hooks and HOCs for RevealUI CMS
4
+ * Client-side React hooks and HOCs for RevealUI admin
5
5
  */
6
6
  'use client';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"PastePlugin.d.ts","sourceRoot":"","sources":["../../../../src/client/richtext/plugins/PastePlugin.tsx"],"names":[],"mappings":"AAcA,wBAAgB,WAAW,IAAI,IAAI,CAiClC"}
1
+ {"version":3,"file":"PastePlugin.d.ts","sourceRoot":"","sources":["../../../../src/client/richtext/plugins/PastePlugin.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,WAAW,IAAI,IAAI,CAkClC"}