nextjs-cms 0.6.6 → 0.6.8

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 (113) hide show
  1. package/dist/api/lib/serverActions.js +8 -8
  2. package/dist/api/routers/accountSettings.js +4 -4
  3. package/dist/api/trpc.js +1 -1
  4. package/dist/auth/lib/actions.d.ts.map +1 -1
  5. package/dist/auth/lib/actions.js +6 -5
  6. package/dist/core/config/config-loader.d.ts +4 -4
  7. package/dist/core/config/config-loader.d.ts.map +1 -1
  8. package/dist/core/config/config-loader.js +6 -5
  9. package/dist/core/factories/FieldFactory.d.ts.map +1 -1
  10. package/dist/core/factories/FieldFactory.js +10 -1
  11. package/dist/core/fields/color.d.ts.map +1 -1
  12. package/dist/core/fields/color.js +3 -2
  13. package/dist/core/fields/date.d.ts.map +1 -1
  14. package/dist/core/fields/date.js +3 -2
  15. package/dist/core/fields/document.d.ts.map +1 -1
  16. package/dist/core/fields/document.js +14 -13
  17. package/dist/core/fields/field-group.d.ts +3 -2
  18. package/dist/core/fields/field-group.d.ts.map +1 -1
  19. package/dist/core/fields/field.d.ts +6 -2
  20. package/dist/core/fields/field.d.ts.map +1 -1
  21. package/dist/core/fields/field.js +12 -0
  22. package/dist/core/fields/index.d.ts +1 -1
  23. package/dist/core/fields/index.d.ts.map +1 -1
  24. package/dist/core/fields/map.d.ts.map +1 -1
  25. package/dist/core/fields/map.js +3 -2
  26. package/dist/core/fields/number.d.ts.map +1 -1
  27. package/dist/core/fields/number.js +8 -7
  28. package/dist/core/fields/password.d.ts.map +1 -1
  29. package/dist/core/fields/password.js +5 -4
  30. package/dist/core/fields/photo.d.ts +6 -6
  31. package/dist/core/fields/photo.d.ts.map +1 -1
  32. package/dist/core/fields/photo.js +18 -17
  33. package/dist/core/fields/richText.d.ts +9 -9
  34. package/dist/core/fields/select.js +3 -3
  35. package/dist/core/fields/slug.d.ts +4 -21
  36. package/dist/core/fields/slug.d.ts.map +1 -1
  37. package/dist/core/fields/slug.js +8 -50
  38. package/dist/core/fields/text.d.ts.map +1 -1
  39. package/dist/core/fields/text.js +4 -3
  40. package/dist/core/fields/video.d.ts.map +1 -1
  41. package/dist/core/fields/video.js +13 -12
  42. package/dist/core/sections/category.d.ts +4 -4
  43. package/dist/core/sections/category.js +3 -3
  44. package/dist/core/sections/hasItems.d.ts +10 -10
  45. package/dist/core/sections/section.d.ts +2 -2
  46. package/dist/core/sections/simple.d.ts +4 -4
  47. package/dist/core/submit/ItemEditSubmit.d.ts +6 -0
  48. package/dist/core/submit/ItemEditSubmit.d.ts.map +1 -1
  49. package/dist/core/submit/ItemEditSubmit.js +8 -0
  50. package/dist/core/submit/submit.d.ts +25 -5
  51. package/dist/core/submit/submit.d.ts.map +1 -1
  52. package/dist/core/submit/submit.js +119 -21
  53. package/dist/translations/dictionaries/ar.d.ts +68 -0
  54. package/dist/translations/dictionaries/ar.d.ts.map +1 -1
  55. package/dist/translations/dictionaries/ar.js +81 -0
  56. package/dist/translations/dictionaries/en.d.ts +68 -0
  57. package/dist/translations/dictionaries/en.d.ts.map +1 -1
  58. package/dist/translations/dictionaries/en.js +81 -0
  59. package/dist/translations/index.d.ts +3 -2
  60. package/dist/translations/index.d.ts.map +1 -1
  61. package/dist/translations/index.js +11 -3
  62. package/dist/validators/checkbox.d.ts +1 -1
  63. package/dist/validators/checkbox.d.ts.map +1 -1
  64. package/dist/validators/checkbox.js +7 -3
  65. package/dist/validators/color.d.ts +1 -1
  66. package/dist/validators/color.d.ts.map +1 -1
  67. package/dist/validators/color.js +6 -4
  68. package/dist/validators/date.d.ts +1 -1
  69. package/dist/validators/date.d.ts.map +1 -1
  70. package/dist/validators/date.js +5 -3
  71. package/dist/validators/document.d.ts +1 -1
  72. package/dist/validators/document.d.ts.map +1 -1
  73. package/dist/validators/document.js +10 -8
  74. package/dist/validators/index.d.ts +7 -7
  75. package/dist/validators/index.d.ts.map +1 -1
  76. package/dist/validators/index.js +7 -7
  77. package/dist/validators/map.d.ts +1 -1
  78. package/dist/validators/map.d.ts.map +1 -1
  79. package/dist/validators/map.js +7 -3
  80. package/dist/validators/number.d.ts +1 -1
  81. package/dist/validators/number.d.ts.map +1 -1
  82. package/dist/validators/number.js +19 -10
  83. package/dist/validators/password.d.ts +1 -1
  84. package/dist/validators/password.d.ts.map +1 -1
  85. package/dist/validators/password.js +13 -5
  86. package/dist/validators/photo.d.ts +1 -1
  87. package/dist/validators/photo.d.ts.map +1 -1
  88. package/dist/validators/photo.js +15 -13
  89. package/dist/validators/richText.d.ts +1 -1
  90. package/dist/validators/richText.d.ts.map +1 -1
  91. package/dist/validators/richText.js +14 -6
  92. package/dist/validators/select-multiple.d.ts +1 -1
  93. package/dist/validators/select-multiple.d.ts.map +1 -1
  94. package/dist/validators/select-multiple.js +8 -6
  95. package/dist/validators/select.d.ts +1 -1
  96. package/dist/validators/select.d.ts.map +1 -1
  97. package/dist/validators/select.js +8 -3
  98. package/dist/validators/slug.d.ts +1 -1
  99. package/dist/validators/slug.d.ts.map +1 -1
  100. package/dist/validators/slug.js +14 -8
  101. package/dist/validators/text.d.ts +1 -1
  102. package/dist/validators/text.d.ts.map +1 -1
  103. package/dist/validators/text.js +13 -3
  104. package/dist/validators/textarea.d.ts +1 -1
  105. package/dist/validators/textarea.d.ts.map +1 -1
  106. package/dist/validators/textarea.js +13 -3
  107. package/dist/validators/types.d.ts +7 -0
  108. package/dist/validators/types.d.ts.map +1 -0
  109. package/dist/validators/types.js +0 -0
  110. package/dist/validators/video.d.ts +1 -1
  111. package/dist/validators/video.d.ts.map +1 -1
  112. package/dist/validators/video.js +10 -8
  113. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../../src/core/fields/video.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAIhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,QAAA,MAAM,YAAY;IACd;;OAEG;;;;;;;;IAQH;;;OAGG;;;;;;kBAKL,CAAA;AAEF,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAE1C,qBAAa,UAAW,SAAQ,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IACtD,gBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAe;IAC5D,QAAQ,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;KAAE,CAAA;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAA;IAE7B;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,kBAAkB,CAAU;gBACxB,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI;IA6BxC,eAAe;;kBAzCD,MAAM;kBAAQ,IAAI,GAAG,IAAI;;;;;;;;;;;;;IAkDjD,iBAAiB,CAAC,EACpB,WAAW,EACX,MAAM,EACN,aAAiB,GACpB,EAAE;QACC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;QACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAClC,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBvB;;OAEG;IACG,WAAW;IA+BK,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAenC,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpG;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIX,WAAW,CAAC,KAAK,EAAE,MAAM;IAIvB,QAAQ,CAAC,KAAK,EAAE,GAAG;IAQnB,OAAO,CAAC,IAAI,EAAE,IAAI;IAK3B,aAAa;IAYb;;OAEG;IACG,oBAAoB;CAoD7B;AAED,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAE9E,QAAA,MAAM,aAAa;IA7Pf;;OAEG;;;;;;;;IAQH;;;OAGG;;;;;;;;;;;;;kBAmPL,CAAA;AAEF,QAAA,MAAM,sBAAsB;;;IAlQxB;;OAEG;;;;;;;;IAQH;;;OAGG;;;;;;;;;;;;;kBAyPL,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAAG,gBAAgB,CAmBjF"}
1
+ {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../../src/core/fields/video.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAIhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAGxB,QAAA,MAAM,YAAY;IACd;;OAEG;;;;;;;;IAQH;;;OAGG;;;;;;kBAKL,CAAA;AAEF,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAE1C,qBAAa,UAAW,SAAQ,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IACtD,gBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAe;IAC5D,QAAQ,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;KAAE,CAAA;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAA;IAE7B;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,kBAAkB,CAAU;gBACxB,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI;IA6BxC,eAAe;;kBAzCD,MAAM;kBAAQ,IAAI,GAAG,IAAI;;;;;;;;;;;;;IAkDjD,iBAAiB,CAAC,EACpB,WAAW,EACX,MAAM,EACN,aAAiB,GACpB,EAAE;QACC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;QACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAClC,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBvB;;OAEG;IACG,WAAW;IA2BK,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzC,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAanC,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpG;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIX,WAAW,CAAC,KAAK,EAAE,MAAM;IAIvB,QAAQ,CAAC,KAAK,EAAE,GAAG;IAQnB,OAAO,CAAC,IAAI,EAAE,IAAI;IAK3B,aAAa;IAYb;;OAEG;IACG,oBAAoB;CAoD7B;AAED,MAAM,MAAM,sBAAsB,GAAG,UAAU,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAE9E,QAAA,MAAM,aAAa;IAvPf;;OAEG;;;;;;;;IAQH;;;OAGG;;;;;;;;;;;;;kBA6OL,CAAA;AAEF,QAAA,MAAM,sBAAsB;;;IA5PxB;;OAEG;;;;;;;;IAQH;;;OAGG;;;;;;;;;;;;;kBAmPL,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAErE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAAG,gBAAgB,CAmBjF"}
@@ -6,6 +6,7 @@ import path from 'path';
6
6
  import { FileField } from './fileField.js';
7
7
  import { getCMSConfig } from '../config/index.js';
8
8
  import * as z from 'zod';
9
+ import getString from '../../translations/index.js';
9
10
  const configSchema = z.strictObject({
10
11
  /**
11
12
  * Maximum file size
@@ -75,13 +76,13 @@ export class VideoField extends FileField {
75
76
  }
76
77
  async readChunkFromFile({ arrayBuffer, length, startPosition = 0, }) {
77
78
  if (!arrayBuffer) {
78
- throw new Error('Error reading file');
79
+ throw new Error(getString('errorReadingFile', this.locale));
79
80
  }
80
81
  const buffer = new Uint8Array(arrayBuffer);
81
82
  const start = Math.max(0, startPosition);
82
83
  const end = Math.min(start + length, buffer.length);
83
84
  if (start >= end) {
84
- throw new Error('Error reading file');
85
+ throw new Error(getString('errorReadingFile', this.locale));
85
86
  }
86
87
  return buffer.subarray(start, end);
87
88
  }
@@ -90,10 +91,10 @@ export class VideoField extends FileField {
90
91
  */
91
92
  async writeToFile() {
92
93
  if (!this._folder) {
93
- throw new Error(`${this.label}: Folder is not set. Make sure to set the folder by call postSubmit() before writing the file to disk`);
94
+ throw new Error(getString('videoFolderNotSet', this.locale, { field: this.getLocalizedLabel() }));
94
95
  }
95
96
  if (!this._buffer) {
96
- throw new Error(`${this.label}: Buffer is not set. Make sure to call prepareForSubmission() before writing the file to disk`);
97
+ throw new Error(getString('videoBufferNotSet', this.locale, { field: this.getLocalizedLabel() }));
97
98
  }
98
99
  try {
99
100
  /**
@@ -110,7 +111,7 @@ export class VideoField extends FileField {
110
111
  await fs.promises.writeFile(path.join(uploadsFolder, '.videos', this._folder, this.value), this._buffer);
111
112
  }
112
113
  catch (error) {
113
- throw new Error(`${this.label}: Error writing file to disk ${error.message}`);
114
+ throw new Error(getString('videoWriteError', this.locale, { field: this.getLocalizedLabel() }) + ` ${error.message}`);
114
115
  }
115
116
  }
116
117
  async postSubmit(folder) {
@@ -119,7 +120,7 @@ export class VideoField extends FileField {
119
120
  }
120
121
  async postSubmitRollback() {
121
122
  if (!this._folder) {
122
- throw new Error(`${this.label}: Folder is not set. Make sure to set the folder before writing the file to disk`);
123
+ throw new Error(getString('videoFolderNotSet', this.locale, { field: this.getLocalizedLabel() }));
123
124
  }
124
125
  try {
125
126
  const uploadsFolder = (await getCMSConfig()).media.upload.path;
@@ -127,7 +128,7 @@ export class VideoField extends FileField {
127
128
  await fs.promises.unlink(pathToFile);
128
129
  }
129
130
  catch (error) {
130
- throw new Error(`${this.label}: Error deleting file from disk`);
131
+ throw new Error(getString('fileDeleteError', this.locale, { field: this.getLocalizedLabel() }));
131
132
  }
132
133
  }
133
134
  async cleanupReplacedFile(previousValue, sectionName) {
@@ -165,7 +166,7 @@ export class VideoField extends FileField {
165
166
  * Note: Those values are coming from the browser (not safe)
166
167
  */
167
168
  if ((this.required && !this._file) || !this._file?.type || !this._file?.name || !this._file?.size) {
168
- throw new Error(`Field ${this.label} is required`);
169
+ throw new Error(getString('fieldIsRequired', this.locale, { field: this.getLocalizedLabel() }));
169
170
  }
170
171
  }
171
172
  /**
@@ -180,14 +181,14 @@ export class VideoField extends FileField {
180
181
  const arrayBuffer = await this._file.arrayBuffer();
181
182
  this._buffer = Buffer.from(arrayBuffer);
182
183
  if (!this._buffer.length) {
183
- throw new Error(`Field ${this.label} is required`);
184
+ throw new Error(getString('fieldIsRequired', this.locale, { field: this.getLocalizedLabel() }));
184
185
  }
185
186
  /**
186
187
  * Check extension
187
188
  */
188
189
  const ext = this._file.name.split('.').pop();
189
190
  if (!ext || !this.extensions.includes(ext)) {
190
- throw new Error(`${this.label}: Invalid file type or extension. Allowed extensions: ${this.extensions.join(', ')}`);
191
+ throw new Error(getString('invalidFileTypeOrExtension', this.locale, { field: this.getLocalizedLabel(), extensions: this.extensions.join(', ') }));
191
192
  }
192
193
  /**
193
194
  * Read the first 4100 bytes of the file
@@ -202,14 +203,14 @@ export class VideoField extends FileField {
202
203
  * If the file type is invalid, return an error
203
204
  */
204
205
  if (!fileType) {
205
- throw new Error(`${this.label}: Invalid file type`);
206
+ throw new Error(getString('invalidDocumentFileType', this.locale, { field: this.getLocalizedLabel() }));
206
207
  }
207
208
  /**
208
209
  * Don't just trust the file extension
209
210
  * Check the `fileType.ext` against the allowed extensions
210
211
  */
211
212
  if (!fileType.ext || !this.extensions.includes(fileType.ext)) {
212
- throw new Error(`${this.label}: Invalid file type or extension. Allowed extensions: ${this.extensions.join(', ')}`);
213
+ throw new Error(getString('invalidFileTypeOrExtension', this.locale, { field: this.getLocalizedLabel(), extensions: this.extensions.join(', ') }));
213
214
  }
214
215
  /**
215
216
  * Generate a random name for the file
@@ -119,9 +119,8 @@ declare const optionsSchema: z.ZodObject<{
119
119
  allowRecursiveDelete: z.ZodOptional<z.ZodBoolean>;
120
120
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
121
121
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
122
- name: z.ZodString;
123
- order: z.ZodNumber;
124
122
  icon: z.ZodOptional<z.ZodString>;
123
+ name: z.ZodString;
125
124
  db: z.ZodObject<{
126
125
  table: z.ZodString;
127
126
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -146,6 +145,7 @@ declare const optionsSchema: z.ZodObject<{
146
145
  }, z.core.$strict>>>;
147
146
  }, z.core.$strict>;
148
147
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
148
+ order: z.ZodNumber;
149
149
  gallery: z.ZodOptional<z.ZodObject<{
150
150
  db: z.ZodObject<{
151
151
  tableName: z.ZodString;
@@ -216,9 +216,8 @@ export declare const categorySectionConfigSchema: z.ZodObject<{
216
216
  */
217
217
  allowRecursiveDelete: z.ZodOptional<z.ZodBoolean>;
218
218
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
219
- name: z.ZodString;
220
- order: z.ZodNumber;
221
219
  icon: z.ZodOptional<z.ZodString>;
220
+ name: z.ZodString;
222
221
  db: z.ZodObject<{
223
222
  table: z.ZodString;
224
223
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -243,6 +242,7 @@ export declare const categorySectionConfigSchema: z.ZodObject<{
243
242
  }, z.core.$strict>>>;
244
243
  }, z.core.$strict>;
245
244
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
245
+ order: z.ZodNumber;
246
246
  gallery: z.ZodOptional<z.ZodObject<{
247
247
  db: z.ZodObject<{
248
248
  tableName: z.ZodString;
@@ -57,14 +57,14 @@ export class CategorySection extends Section {
57
57
  if (this.depth > 1) {
58
58
  const levelConfig = numberField({
59
59
  name: 'level',
60
- label: 'Level',
60
+ label: { en: 'Level', ar: 'المستوى' },
61
61
  required: false,
62
62
  order: 0,
63
63
  });
64
64
  if (this.db.identifier.type === 'number') {
65
65
  this._fieldConfigs?.push(numberField({
66
66
  name: 'parent_id',
67
- label: 'Parent ID',
67
+ label: { en: 'Parent ID', ar: 'معرف الأب' },
68
68
  required: false,
69
69
  order: 0,
70
70
  }), levelConfig);
@@ -72,7 +72,7 @@ export class CategorySection extends Section {
72
72
  else if (this.db.identifier.type === 'text') {
73
73
  this._fieldConfigs?.push(textField({
74
74
  name: 'parent_id',
75
- label: 'Parent ID',
75
+ label: { en: 'Parent ID', ar: 'معرف الأب' },
76
76
  required: false,
77
77
  order: 0,
78
78
  }), levelConfig);
@@ -147,7 +147,7 @@ declare const configSchema: z.ZodObject<{
147
147
  size: number;
148
148
  unit: "kb" | "mb";
149
149
  } | undefined;
150
- fileType?: ("jpeg" | "jpg" | "png" | "webp")[] | undefined;
150
+ fileType?: ("webp" | "jpg" | "jpeg" | "png")[] | undefined;
151
151
  removeExtension?: boolean | undefined;
152
152
  label?: string | Record<string, string> | undefined;
153
153
  required?: boolean | undefined;
@@ -188,7 +188,7 @@ declare const configSchema: z.ZodObject<{
188
188
  size: number;
189
189
  unit: "kb" | "mb";
190
190
  } | undefined;
191
- fileType?: ("jpeg" | "jpg" | "png" | "webp")[] | undefined;
191
+ fileType?: ("webp" | "jpg" | "jpeg" | "png")[] | undefined;
192
192
  removeExtension?: boolean | undefined;
193
193
  label?: string | Record<string, string> | undefined;
194
194
  required?: boolean | undefined;
@@ -365,7 +365,7 @@ declare const optionsSchema: z.ZodObject<{
365
365
  size: number;
366
366
  unit: "kb" | "mb";
367
367
  } | undefined;
368
- fileType?: ("jpeg" | "jpg" | "png" | "webp")[] | undefined;
368
+ fileType?: ("webp" | "jpg" | "jpeg" | "png")[] | undefined;
369
369
  removeExtension?: boolean | undefined;
370
370
  label?: string | Record<string, string> | undefined;
371
371
  required?: boolean | undefined;
@@ -406,7 +406,7 @@ declare const optionsSchema: z.ZodObject<{
406
406
  size: number;
407
407
  unit: "kb" | "mb";
408
408
  } | undefined;
409
- fileType?: ("jpeg" | "jpg" | "png" | "webp")[] | undefined;
409
+ fileType?: ("webp" | "jpg" | "jpeg" | "png")[] | undefined;
410
410
  removeExtension?: boolean | undefined;
411
411
  label?: string | Record<string, string> | undefined;
412
412
  required?: boolean | undefined;
@@ -418,9 +418,8 @@ declare const optionsSchema: z.ZodObject<{
418
418
  generateQR: z.ZodOptional<z.ZodBoolean>;
419
419
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
420
420
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
421
- name: z.ZodString;
422
- order: z.ZodNumber;
423
421
  icon: z.ZodOptional<z.ZodString>;
422
+ name: z.ZodString;
424
423
  db: z.ZodObject<{
425
424
  table: z.ZodString;
426
425
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -445,6 +444,7 @@ declare const optionsSchema: z.ZodObject<{
445
444
  }, z.core.$strict>>>;
446
445
  }, z.core.$strict>;
447
446
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
447
+ order: z.ZodNumber;
448
448
  gallery: z.ZodOptional<z.ZodObject<{
449
449
  db: z.ZodObject<{
450
450
  tableName: z.ZodString;
@@ -607,7 +607,7 @@ declare const hasItemsSectionConfigSchema: z.ZodObject<{
607
607
  size: number;
608
608
  unit: "kb" | "mb";
609
609
  } | undefined;
610
- fileType?: ("jpeg" | "jpg" | "png" | "webp")[] | undefined;
610
+ fileType?: ("webp" | "jpg" | "jpeg" | "png")[] | undefined;
611
611
  removeExtension?: boolean | undefined;
612
612
  label?: string | Record<string, string> | undefined;
613
613
  required?: boolean | undefined;
@@ -648,7 +648,7 @@ declare const hasItemsSectionConfigSchema: z.ZodObject<{
648
648
  size: number;
649
649
  unit: "kb" | "mb";
650
650
  } | undefined;
651
- fileType?: ("jpeg" | "jpg" | "png" | "webp")[] | undefined;
651
+ fileType?: ("webp" | "jpg" | "jpeg" | "png")[] | undefined;
652
652
  removeExtension?: boolean | undefined;
653
653
  label?: string | Record<string, string> | undefined;
654
654
  required?: boolean | undefined;
@@ -659,9 +659,8 @@ declare const hasItemsSectionConfigSchema: z.ZodObject<{
659
659
  }>>;
660
660
  generateQR: z.ZodOptional<z.ZodBoolean>;
661
661
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
662
- name: z.ZodString;
663
- order: z.ZodNumber;
664
662
  icon: z.ZodOptional<z.ZodString>;
663
+ name: z.ZodString;
665
664
  db: z.ZodObject<{
666
665
  table: z.ZodString;
667
666
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -686,6 +685,7 @@ declare const hasItemsSectionConfigSchema: z.ZodObject<{
686
685
  }, z.core.$strict>>>;
687
686
  }, z.core.$strict>;
688
687
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
688
+ order: z.ZodNumber;
689
689
  gallery: z.ZodOptional<z.ZodObject<{
690
690
  db: z.ZodObject<{
691
691
  tableName: z.ZodString;
@@ -63,9 +63,8 @@ export declare const baseSectionOptionsSchema: z.ZodObject<{
63
63
  export declare const baseHelperFunctionOptionsSchema: z.ZodObject<{
64
64
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
65
65
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
66
- name: z.ZodString;
67
- order: z.ZodNumber;
68
66
  icon: z.ZodOptional<z.ZodString>;
67
+ name: z.ZodString;
69
68
  db: z.ZodObject<{
70
69
  table: z.ZodString;
71
70
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -90,6 +89,7 @@ export declare const baseHelperFunctionOptionsSchema: z.ZodObject<{
90
89
  }, z.core.$strict>>>;
91
90
  }, z.core.$strict>;
92
91
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<Variant, Variant>>>;
92
+ order: z.ZodNumber;
93
93
  gallery: z.ZodOptional<z.ZodObject<{
94
94
  db: z.ZodObject<{
95
95
  tableName: z.ZodString;
@@ -24,10 +24,10 @@ declare const optionsSchema: z.ZodObject<{
24
24
  }, z.core.$strict>;
25
25
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
26
26
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
27
- name: z.ZodString;
28
- order: z.ZodNumber;
29
27
  icon: z.ZodOptional<z.ZodString>;
28
+ name: z.ZodString;
30
29
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
30
+ order: z.ZodNumber;
31
31
  gallery: z.ZodOptional<z.ZodObject<{
32
32
  db: z.ZodObject<{
33
33
  tableName: z.ZodString;
@@ -55,10 +55,10 @@ declare const simpleSectionConfigSchema: z.ZodObject<{
55
55
  table: z.ZodString;
56
56
  }, z.core.$strict>;
57
57
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
58
- name: z.ZodString;
59
- order: z.ZodNumber;
60
58
  icon: z.ZodOptional<z.ZodString>;
59
+ name: z.ZodString;
61
60
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
61
+ order: z.ZodNumber;
62
62
  gallery: z.ZodOptional<z.ZodObject<{
63
63
  db: z.ZodObject<{
64
64
  tableName: z.ZodString;
@@ -74,6 +74,12 @@ export declare class EditSubmit extends Submit {
74
74
  * Item id value is already assigned in the constructor
75
75
  */
76
76
  assignItemIdValue(): void;
77
+ /**
78
+ * Return the current item's ID for exclusion in uniqueness checks.
79
+ * This ensures that when editing an item, it doesn't conflict with itself.
80
+ * @override
81
+ */
82
+ protected getExcludedItemId(): string;
77
83
  /**
78
84
  * Set field value override
79
85
  * @param field
@@ -1 +1 @@
1
- {"version":3,"file":"ItemEditSubmit.d.ts","sourceRoot":"","sources":["../../../src/core/submit/ItemEditSubmit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAItC,OAAO,KAAK,EAAuB,KAAK,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,OAAO,EAAE,UAAU,EAAM,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAO3E,KAAK,eAAe,GAAG;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,IAAI,CAAA;IACV,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,eAAe,CAAC,EAAE,eAAe,CAAA;CACpC,CAAA;AAED,qBAAa,UAAW,SAAQ,MAAM;IAClC,gBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAe;IAC5D,mBAA4B,OAAO,EAAE,MAAM,CAAA;IAC3C,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAK;cAExB,eAAe,IAAI,YAAY;IAIlD;;OAEG;gBACS,MAAM,EAAE,eAAe;IAKnC;;;OAGG;IACmB,UAAU;IASV,MAAM;YAQd,qBAAqB;cAuBhB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;cAIjD,gBAAgB,IAAI,MAAM,EAAE;IAO/C,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,iBAAiB;IAczB;;;OAGG;YACW,aAAa;YAQb,sBAAsB;IAoBpC;;;;;OAKG;cACsB,kBAAkB;IAI3C;;;;;OAKG;cACsB,cAAc;YAUzB,oBAAoB;IAoBlC;;;OAGG;cACgB,aAAa,IAAI,GAAG,GAAG,SAAS;IAmBnD;;;;OAIG;IACM,aAAa,CAAC,KAAK,EAAE,KAAK;IAmBnC;;;;OAIG;IACY,WAAW,CAAC,KAAK,EAAE,KAAK;IAQvC;;OAEG;IACM,iBAAiB;IAI1B;;;;OAIG;IACM,aAAa,CAAC,KAAK,EAAE,KAAK;CAgBtC"}
1
+ {"version":3,"file":"ItemEditSubmit.d.ts","sourceRoot":"","sources":["../../../src/core/submit/ItemEditSubmit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAItC,OAAO,KAAK,EAAuB,KAAK,EAAE,MAAM,WAAW,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAEjC,OAAO,EAAE,UAAU,EAAM,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAO3E,KAAK,eAAe,GAAG;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,IAAI,CAAA;IACV,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,eAAe,CAAC,EAAE,eAAe,CAAA;CACpC,CAAA;AAED,qBAAa,UAAW,SAAQ,MAAM;IAClC,gBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAe;IAC5D,mBAA4B,OAAO,EAAE,MAAM,CAAA;IAC3C,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAK;cAExB,eAAe,IAAI,YAAY;IAIlD;;OAEG;gBACS,MAAM,EAAE,eAAe;IAKnC;;;OAGG;IACmB,UAAU;IASV,MAAM;YAQd,qBAAqB;cAuBhB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;cAIjD,gBAAgB,IAAI,MAAM,EAAE;IAO/C,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,iBAAiB;IAczB;;;OAGG;YACW,aAAa;YAQb,sBAAsB;IAoBpC;;;;;OAKG;cACsB,kBAAkB;IAI3C;;;;;OAKG;cACsB,cAAc;YAUzB,oBAAoB;IAoBlC;;;OAGG;cACgB,aAAa,IAAI,GAAG,GAAG,SAAS;IAmBnD;;;;OAIG;IACM,aAAa,CAAC,KAAK,EAAE,KAAK;IAmBnC;;;;OAIG;IACY,WAAW,CAAC,KAAK,EAAE,KAAK;IAQvC;;OAEG;IACM,iBAAiB;IAI1B;;;;OAIG;cACgB,iBAAiB,IAAI,MAAM;IAI9C;;;;OAIG;IACM,aAAa,CAAC,KAAK,EAAE,KAAK;CAgBtC"}
@@ -219,6 +219,14 @@ export class EditSubmit extends Submit {
219
219
  assignItemIdValue() {
220
220
  return;
221
221
  }
222
+ /**
223
+ * Return the current item's ID for exclusion in uniqueness checks.
224
+ * This ensures that when editing an item, it doesn't conflict with itself.
225
+ * @override
226
+ */
227
+ getExcludedItemId() {
228
+ return this._itemId;
229
+ }
222
230
  /**
223
231
  * Set field value override
224
232
  * @param field
@@ -109,14 +109,34 @@ export declare abstract class Submit {
109
109
  * @protected
110
110
  */
111
111
  protected handleField(field: Field): Promise<void>;
112
+ protected handleFields(): Promise<void>;
112
113
  /**
113
- * Call prepareForSubmission on a field with appropriate context.
114
- * SlugField requires table name for duplicate checking.
115
- * @param field
114
+ * Get the ID of the item to exclude from uniqueness checks.
115
+ * Returns undefined in the base class (NewSubmit behavior).
116
+ * Override in EditSubmit to return the current item's ID.
116
117
  * @protected
117
118
  */
118
- protected callPrepareForSubmission(field: Field): Promise<void>;
119
- protected handleFields(): Promise<void>;
119
+ protected getExcludedItemId(): number | string | undefined;
120
+ /**
121
+ * Get a user-friendly label for a field config.
122
+ * Resolves localized strings to English.
123
+ * @param fieldConfig - The field configuration
124
+ * @private
125
+ */
126
+ private getFieldLabel;
127
+ /**
128
+ * Check all uniqueness constraints defined in the section.
129
+ * Stops on the first violation.
130
+ * @protected
131
+ */
132
+ protected checkUniqueness(): Promise<void>;
133
+ /**
134
+ * Check a single uniqueness constraint.
135
+ * Handles both single-column and composite (multi-column) constraints.
136
+ * @param constraint - The constraint to check
137
+ * @private
138
+ */
139
+ private checkUniqueConstraint;
120
140
  /**
121
141
  * Execute the sql query.
122
142
  * @param sqlQuery
@@ -1 +1 @@
1
- {"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../../src/core/submit/submit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"submit.d.ts","sourceRoot":"","sources":["../../../src/core/submit/submit.ts"],"names":[],"mappings":"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"}
@@ -2,10 +2,13 @@ import { sql } from 'drizzle-orm';
2
2
  import { db } from '../../db/client';
3
3
  import { SectionFactory } from '../factories';
4
4
  import { entityKind, is } from '../helpers';
5
- import { NumberField, PhotoField, SelectField, SlugField, TextField } from '../fields';
5
+ import { NumberField, PhotoField, SelectField, TextField } from '../fields';
6
6
  import { MysqlTableChecker } from '../db';
7
7
  import { recordLog } from '../../logging/index.js';
8
8
  import getString from '../../translations';
9
+ import { resolveLocalizedString } from '../../translations/localization.js';
10
+ import { resolveLocale } from '../../translations/locale-utils.js';
11
+ import { getCMSConfig } from '../config/index.js';
9
12
  export class Submit {
10
13
  static [entityKind] = 'Submit';
11
14
  user;
@@ -177,6 +180,14 @@ export class Submit {
177
180
  * Build the fields from the field configs
178
181
  */
179
182
  this._sectionInfo.buildFields();
183
+ /**
184
+ * Set locale for fields so validation errors are localized
185
+ */
186
+ const cmsConfig = await getCMSConfig();
187
+ const locale = resolveLocale(user.locale, cmsConfig.i18n.supportedLanguages, cmsConfig.i18n.fallbackLanguage);
188
+ for (const field of this._sectionInfo.fields) {
189
+ field.setLocale(locale, cmsConfig.i18n.fallbackLanguage);
190
+ }
180
191
  }
181
192
  /**
182
193
  * Perform post submit operations
@@ -352,7 +363,7 @@ export class Submit {
352
363
  * Prepare the field for submission,
353
364
  * this will throw an error if the checks fail
354
365
  */
355
- await this.callPrepareForSubmission(field);
366
+ await field.prepareForSubmission();
356
367
  }
357
368
  catch (e) {
358
369
  this._error = true;
@@ -368,22 +379,6 @@ export class Submit {
368
379
  */
369
380
  this.assignItemIdValue(field);
370
381
  }
371
- /**
372
- * Call prepareForSubmission on a field with appropriate context.
373
- * SlugField requires table name for duplicate checking.
374
- * @param field
375
- * @protected
376
- */
377
- async callPrepareForSubmission(field) {
378
- if (is(field, SlugField)) {
379
- await field.prepareForSubmission({
380
- tableName: this._sectionInfo.db.table,
381
- });
382
- }
383
- else {
384
- await field.prepareForSubmission();
385
- }
386
- }
387
382
  async handleFields() {
388
383
  if (this._error)
389
384
  return;
@@ -395,6 +390,99 @@ export class Submit {
395
390
  }
396
391
  }
397
392
  }
393
+ /**
394
+ * Get the ID of the item to exclude from uniqueness checks.
395
+ * Returns undefined in the base class (NewSubmit behavior).
396
+ * Override in EditSubmit to return the current item's ID.
397
+ * @protected
398
+ */
399
+ getExcludedItemId() {
400
+ return undefined;
401
+ }
402
+ /**
403
+ * Get a user-friendly label for a field config.
404
+ * Resolves localized strings to English.
405
+ * @param fieldConfig - The field configuration
406
+ * @private
407
+ */
408
+ getFieldLabel(fieldConfig) {
409
+ const label = fieldConfig.label;
410
+ if (!label) {
411
+ return fieldConfig.name
412
+ .replace(/_/g, ' ')
413
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
414
+ .replace(/\b\w/g, (char) => char.toUpperCase());
415
+ }
416
+ return resolveLocalizedString(label, 'en', 'en');
417
+ }
418
+ /**
419
+ * Check all uniqueness constraints defined in the section.
420
+ * Stops on the first violation.
421
+ * @protected
422
+ */
423
+ async checkUniqueness() {
424
+ if (this._error)
425
+ return;
426
+ if (!this._sectionInfo?.db.unique?.length)
427
+ return;
428
+ for (const constraint of this._sectionInfo.db.unique) {
429
+ await this.checkUniqueConstraint(constraint);
430
+ if (this._error)
431
+ return;
432
+ }
433
+ }
434
+ /**
435
+ * Check a single uniqueness constraint.
436
+ * Handles both single-column and composite (multi-column) constraints.
437
+ * @param constraint - The constraint to check
438
+ * @private
439
+ */
440
+ async checkUniqueConstraint(constraint) {
441
+ const columnNames = constraint.columns.map((col) => col.name);
442
+ // Check if ALL columns have values in sqlNamesAndValues
443
+ // Skip if any column has null/undefined value
444
+ const columnValues = [];
445
+ for (const fieldConfig of constraint.columns) {
446
+ const value = this.sqlNamesAndValues[fieldConfig.name];
447
+ if (value === null || value === undefined) {
448
+ // Skip this constraint if any column is null/undefined
449
+ return;
450
+ }
451
+ columnValues.push({ name: fieldConfig.name, value, fieldConfig });
452
+ }
453
+ // Build the WHERE clause
454
+ const whereConditions = columnValues.map(({ name, value }) => sql `${sql.raw(`\`${name}\``)} = ${value}`);
455
+ // Add exclusion for the current item if editing
456
+ const excludedId = this.getExcludedItemId();
457
+ if (excludedId !== undefined) {
458
+ const identifierName = this._sectionInfo.db.identifier.name;
459
+ whereConditions.push(sql `${sql.raw(`\`${identifierName}\``)} != ${excludedId}`);
460
+ }
461
+ // Combine conditions with AND
462
+ const whereClause = sql.join(whereConditions, sql ` AND `);
463
+ // Execute the query
464
+ const query = sql `SELECT 1 FROM ${sql.raw(`\`${this._sectionInfo.db.table}\``)} WHERE ${whereClause} LIMIT 1`;
465
+ try {
466
+ const [rows] = await db.execute(query);
467
+ if (Array.isArray(rows) && rows.length > 0) {
468
+ // Violation found - build error message
469
+ this._error = true;
470
+ const firstColumn = columnValues[0];
471
+ if (columnValues.length === 1 && firstColumn) {
472
+ const label = this.getFieldLabel(firstColumn.fieldConfig);
473
+ this._errorMessage = getString('recordWithFieldExists', this.user.locale, { field: label });
474
+ }
475
+ else {
476
+ const labels = columnValues.map(({ fieldConfig }) => this.getFieldLabel(fieldConfig));
477
+ this._errorMessage = getString('recordWithCombinationExists', this.user.locale, { fields: labels.join(', ') });
478
+ }
479
+ }
480
+ }
481
+ catch (e) {
482
+ // Log but don't fail on query errors - let the actual insert/update handle it
483
+ console.error('Uniqueness check query failed:', e);
484
+ }
485
+ }
398
486
  /**
399
487
  * Execute the sql query.
400
488
  * @param sqlQuery
@@ -408,7 +496,7 @@ export class Submit {
408
496
  */
409
497
  if (!sqlQuery) {
410
498
  this._error = true;
411
- this._errorMessage = 'SQL query is not defined';
499
+ this._errorMessage = getString('sqlQueryNotDefined', this.user.locale);
412
500
  return;
413
501
  }
414
502
  try {
@@ -438,12 +526,22 @@ export class Submit {
438
526
  * Assign the values to the fields
439
527
  */
440
528
  await this.handleFields();
529
+ /**
530
+ * Check uniqueness constraints
531
+ */
532
+ await this.checkUniqueness();
441
533
  /**
442
534
  * If this is a pre-submit operation, don't submit the form
443
535
  */
444
536
  if (this.preSubmit) {
445
537
  return;
446
538
  }
539
+ /**
540
+ * Return early if there was an error (including uniqueness violations)
541
+ */
542
+ if (this._error) {
543
+ return;
544
+ }
447
545
  /**
448
546
  * Run pre-submit hooks (beforeUpdate)
449
547
  */
@@ -525,7 +623,7 @@ export class Submit {
525
623
  /**
526
624
  * The gallery table is not set up correctly, add it to the notices array
527
625
  */
528
- this.addNotice('Gallery table is not set up correctly, gallery photos were not saved.');
626
+ this.addNotice(getString('galleryTableNotSetUp', this.user.locale));
529
627
  return;
530
628
  }
531
629
  const columns = await MysqlTableChecker.getColumns(gallery.db.tableName);
@@ -535,7 +633,7 @@ export class Submit {
535
633
  /**
536
634
  * The gallery table is not set up correctly, add it to the notices array
537
635
  */
538
- this.addNotice('Gallery table is not set up correctly, gallery photos were not saved.');
636
+ this.addNotice(getString('galleryTableNotSetUp', this.user.locale));
539
637
  return;
540
638
  }
541
639
  /**