@fullgrainsoftware/portal 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,6 +47,8 @@ export default defineSite({
47
47
  });
48
48
  ```
49
49
 
50
+ A repeatable section whose items are mostly a picture can take photos in bulk from the panel: set `batchUpload: true` (the section has exactly one image field) or `batchUpload: "<image field key>"`. Each picked image becomes one item with every other field empty, so no other field of that section may be `required`.
51
+
50
52
  ## Read content in your pages
51
53
 
52
54
  ```tsx
@@ -18,6 +18,13 @@ export interface ManifestSection {
18
18
  title: string;
19
19
  kind: "repeatable" | "singleton";
20
20
  allowGroupCreation?: boolean;
21
+ /**
22
+ * Batch photo uploads in the panel ("Add photos": one item per picked
23
+ * image, every other field empty). `true` when the section has exactly one
24
+ * image field, else that field's key. Repeatable sections only; no other
25
+ * field may be `required`.
26
+ */
27
+ batchUpload?: true | string;
21
28
  fields: Record<string, ManifestField>;
22
29
  seed?: readonly ManifestSeedItem[];
23
30
  }
@@ -41,6 +48,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
41
48
  title: z.ZodString;
42
49
  kind: z.ZodEnum<["repeatable", "singleton"]>;
43
50
  allowGroupCreation: z.ZodOptional<z.ZodBoolean>;
51
+ batchUpload: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<true>, z.ZodString]>>;
44
52
  fields: z.ZodRecord<z.ZodString, z.ZodObject<{
45
53
  label: z.ZodString;
46
54
  type: z.ZodEnum<["text", "image", "color", "audio", "video"]>;
@@ -90,6 +98,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
90
98
  generateMp4?: boolean | undefined;
91
99
  }>;
92
100
  allowGroupCreation?: boolean | undefined;
101
+ batchUpload?: string | true | undefined;
93
102
  seed?: {
94
103
  values: Record<string, string>;
95
104
  name: string;
@@ -108,6 +117,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
108
117
  generateMp4?: boolean | undefined;
109
118
  }>;
110
119
  allowGroupCreation?: boolean | undefined;
120
+ batchUpload?: string | true | undefined;
111
121
  seed?: {
112
122
  values: Record<string, string>;
113
123
  name: string;
@@ -126,6 +136,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
126
136
  generateMp4?: boolean | undefined;
127
137
  }>;
128
138
  allowGroupCreation?: boolean | undefined;
139
+ batchUpload?: string | true | undefined;
129
140
  seed?: {
130
141
  values: Record<string, string>;
131
142
  name: string;
@@ -144,6 +155,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
144
155
  generateMp4?: boolean | undefined;
145
156
  }>;
146
157
  allowGroupCreation?: boolean | undefined;
158
+ batchUpload?: string | true | undefined;
147
159
  seed?: {
148
160
  values: Record<string, string>;
149
161
  name: string;
@@ -166,6 +178,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
166
178
  generateMp4?: boolean | undefined;
167
179
  }>;
168
180
  allowGroupCreation?: boolean | undefined;
181
+ batchUpload?: string | true | undefined;
169
182
  seed?: {
170
183
  values: Record<string, string>;
171
184
  name: string;
@@ -188,6 +201,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
188
201
  generateMp4?: boolean | undefined;
189
202
  }>;
190
203
  allowGroupCreation?: boolean | undefined;
204
+ batchUpload?: string | true | undefined;
191
205
  seed?: {
192
206
  values: Record<string, string>;
193
207
  name: string;
@@ -213,6 +227,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
213
227
  generateMp4?: boolean | undefined;
214
228
  }>;
215
229
  allowGroupCreation?: boolean | undefined;
230
+ batchUpload?: string | true | undefined;
216
231
  seed?: {
217
232
  values: Record<string, string>;
218
233
  name: string;
@@ -238,6 +253,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
238
253
  generateMp4?: boolean | undefined;
239
254
  }>;
240
255
  allowGroupCreation?: boolean | undefined;
256
+ batchUpload?: string | true | undefined;
241
257
  seed?: {
242
258
  values: Record<string, string>;
243
259
  name: string;
@@ -263,6 +279,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
263
279
  generateMp4?: boolean | undefined;
264
280
  }>;
265
281
  allowGroupCreation?: boolean | undefined;
282
+ batchUpload?: string | true | undefined;
266
283
  seed?: {
267
284
  values: Record<string, string>;
268
285
  name: string;
@@ -288,6 +305,7 @@ export declare const siteManifestSchema: z.ZodEffects<z.ZodObject<{
288
305
  generateMp4?: boolean | undefined;
289
306
  }>;
290
307
  allowGroupCreation?: boolean | undefined;
308
+ batchUpload?: string | true | undefined;
291
309
  seed?: {
292
310
  values: Record<string, string>;
293
311
  name: string;
package/dist/manifest.js CHANGED
@@ -29,6 +29,7 @@ const sectionSchema = z
29
29
  title: z.string().trim().min(1),
30
30
  kind: z.enum(["repeatable", "singleton"]),
31
31
  allowGroupCreation: z.boolean().optional(),
32
+ batchUpload: z.union([z.literal(true), z.string().trim().min(1)]).optional(),
32
33
  fields: z.record(z.string().regex(/^[a-z][a-z0-9-]*$/, "field keys are lower-kebab"), fieldSchema),
33
34
  seed: z.array(seedItemSchema).optional(),
34
35
  })
@@ -64,6 +65,48 @@ const sectionSchema = z
64
65
  }
65
66
  }
66
67
  }
68
+ // Batch uploads: the panel fills ONE image field per picked file and
69
+ // leaves every other field empty, so the target must be an image field
70
+ // of a repeatable section and nothing else may be required.
71
+ if (section.batchUpload !== undefined) {
72
+ if (section.kind !== "repeatable") {
73
+ ctx.addIssue({
74
+ code: z.ZodIssueCode.custom,
75
+ message: `batchUpload on section "${section.title}" needs kind: "repeatable"`,
76
+ });
77
+ }
78
+ const imageKeys = Object.entries(section.fields)
79
+ .filter(([, field]) => field.type === "image")
80
+ .map(([key]) => key);
81
+ let target = null;
82
+ if (section.batchUpload === true) {
83
+ if (imageKeys.length === 1)
84
+ target = imageKeys[0];
85
+ else {
86
+ ctx.addIssue({
87
+ code: z.ZodIssueCode.custom,
88
+ message: `batchUpload: true on section "${section.title}" needs exactly one image field (found ${imageKeys.length}) — name the field key instead`,
89
+ });
90
+ }
91
+ }
92
+ else if (imageKeys.includes(section.batchUpload)) {
93
+ target = section.batchUpload;
94
+ }
95
+ else {
96
+ ctx.addIssue({
97
+ code: z.ZodIssueCode.custom,
98
+ message: `batchUpload on section "${section.title}" names "${section.batchUpload}", which is not an image field of that section`,
99
+ });
100
+ }
101
+ for (const [key, field] of Object.entries(section.fields)) {
102
+ if (key !== target && field.required) {
103
+ ctx.addIssue({
104
+ code: z.ZodIssueCode.custom,
105
+ message: `batchUpload on section "${section.title}" can't fill required field "${key}" — a batch-made item leaves every other field empty`,
106
+ });
107
+ }
108
+ }
109
+ }
67
110
  });
68
111
  const pageSchema = z
69
112
  .object({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fullgrainsoftware/portal",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Fullgrain Portal site SDK: manifest, typed content reader, sync CLI, preview bridge, commerce, inquiries and bookings clients",
5
5
  "repository": {
6
6
  "type": "git",