nextjs-cms 0.5.9 → 0.5.10

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 (258) hide show
  1. package/dist/api/axios/axiosInstance.d.ts +1 -1
  2. package/dist/api/axios/axiosInstance.js +8 -8
  3. package/dist/api/index.d.ts +855 -855
  4. package/dist/api/index.d.ts.map +1 -1
  5. package/dist/api/index.js +12 -12
  6. package/dist/api/lib/serverActions.d.ts +239 -239
  7. package/dist/api/lib/serverActions.d.ts.map +1 -1
  8. package/dist/api/lib/serverActions.js +834 -834
  9. package/dist/api/root.d.ts +828 -828
  10. package/dist/api/root.js +30 -30
  11. package/dist/api/routers/accountSettings.d.ts +60 -60
  12. package/dist/api/routers/accountSettings.js +108 -108
  13. package/dist/api/routers/admins.d.ts +105 -105
  14. package/dist/api/routers/admins.js +219 -219
  15. package/dist/api/routers/auth.d.ts +47 -47
  16. package/dist/api/routers/auth.js +25 -25
  17. package/dist/api/routers/categorySection.d.ts +103 -103
  18. package/dist/api/routers/categorySection.js +38 -38
  19. package/dist/api/routers/cmsSettings.d.ts +48 -48
  20. package/dist/api/routers/cmsSettings.js +51 -51
  21. package/dist/api/routers/cpanel.d.ts +83 -83
  22. package/dist/api/routers/cpanel.js +216 -216
  23. package/dist/api/routers/files.d.ts +47 -47
  24. package/dist/api/routers/files.js +23 -23
  25. package/dist/api/routers/gallery.d.ts +35 -35
  26. package/dist/api/routers/gallery.js +62 -62
  27. package/dist/api/routers/googleAnalytics.d.ts +30 -30
  28. package/dist/api/routers/googleAnalytics.js +7 -7
  29. package/dist/api/routers/hasItemsSection.d.ts +139 -139
  30. package/dist/api/routers/hasItemsSection.js +34 -34
  31. package/dist/api/routers/navigation.d.ts +51 -51
  32. package/dist/api/routers/navigation.js +11 -11
  33. package/dist/api/routers/simpleSection.d.ts +57 -57
  34. package/dist/api/routers/simpleSection.js +12 -12
  35. package/dist/api/trpc.d.ts +106 -106
  36. package/dist/api/trpc.js +72 -72
  37. package/dist/auth/axios/axiosInstance.d.ts +1 -1
  38. package/dist/auth/axios/axiosInstance.js +8 -8
  39. package/dist/auth/csrf.d.ts +29 -29
  40. package/dist/auth/csrf.js +76 -76
  41. package/dist/auth/hooks/index.d.ts +3 -3
  42. package/dist/auth/hooks/index.d.ts.map +1 -1
  43. package/dist/auth/hooks/index.js +3 -3
  44. package/dist/auth/hooks/useAxiosPrivate.d.ts +4 -4
  45. package/dist/auth/hooks/useAxiosPrivate.js +74 -74
  46. package/dist/auth/hooks/useRefreshToken.d.ts +6 -6
  47. package/dist/auth/hooks/useRefreshToken.js +79 -79
  48. package/dist/auth/index.d.ts +22 -22
  49. package/dist/auth/index.js +44 -44
  50. package/dist/auth/jwt.d.ts +5 -5
  51. package/dist/auth/jwt.js +25 -25
  52. package/dist/auth/lib/actions.d.ts +32 -32
  53. package/dist/auth/lib/actions.d.ts.map +1 -1
  54. package/dist/auth/lib/actions.js +209 -209
  55. package/dist/auth/lib/client.d.ts +3 -3
  56. package/dist/auth/lib/client.js +46 -46
  57. package/dist/auth/lib/index.d.ts +2 -2
  58. package/dist/auth/lib/index.d.ts.map +1 -1
  59. package/dist/auth/lib/index.js +2 -2
  60. package/dist/auth/react.d.ts +105 -105
  61. package/dist/auth/react.d.ts.map +1 -1
  62. package/dist/auth/react.js +347 -347
  63. package/dist/auth/trpc.d.ts +5 -5
  64. package/dist/auth/trpc.d.ts.map +1 -1
  65. package/dist/auth/trpc.js +81 -81
  66. package/dist/core/config/config-loader.d.ts +91 -91
  67. package/dist/core/config/config-loader.js +230 -230
  68. package/dist/core/config/index.d.ts +2 -2
  69. package/dist/core/config/index.d.ts.map +1 -1
  70. package/dist/core/config/index.js +1 -1
  71. package/dist/core/config/loader.d.ts +1 -1
  72. package/dist/core/config/loader.js +42 -42
  73. package/dist/core/db/index.d.ts +1 -1
  74. package/dist/core/db/index.d.ts.map +1 -1
  75. package/dist/core/db/index.js +1 -1
  76. package/dist/core/db/table-checker/DbTable.d.ts +5 -5
  77. package/dist/core/db/table-checker/DbTable.js +5 -5
  78. package/dist/core/db/table-checker/MysqlTable.d.ts +33 -33
  79. package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -1
  80. package/dist/core/db/table-checker/MysqlTable.js +94 -94
  81. package/dist/core/db/table-checker/index.d.ts +1 -1
  82. package/dist/core/db/table-checker/index.d.ts.map +1 -1
  83. package/dist/core/db/table-checker/index.js +1 -1
  84. package/dist/core/factories/FieldFactory.d.ts +123 -123
  85. package/dist/core/factories/FieldFactory.d.ts.map +1 -1
  86. package/dist/core/factories/FieldFactory.js +411 -411
  87. package/dist/core/factories/SectionFactory.d.ts +109 -109
  88. package/dist/core/factories/SectionFactory.d.ts.map +1 -1
  89. package/dist/core/factories/SectionFactory.js +415 -415
  90. package/dist/core/factories/index.d.ts +2 -2
  91. package/dist/core/factories/index.d.ts.map +1 -1
  92. package/dist/core/factories/index.js +2 -2
  93. package/dist/core/fields/checkbox.d.ts +62 -62
  94. package/dist/core/fields/checkbox.d.ts.map +1 -1
  95. package/dist/core/fields/checkbox.js +62 -62
  96. package/dist/core/fields/color.d.ts +83 -83
  97. package/dist/core/fields/color.d.ts.map +1 -1
  98. package/dist/core/fields/color.js +91 -91
  99. package/dist/core/fields/date.d.ts +99 -99
  100. package/dist/core/fields/date.d.ts.map +1 -1
  101. package/dist/core/fields/date.js +108 -108
  102. package/dist/core/fields/document.d.ts +179 -179
  103. package/dist/core/fields/document.d.ts.map +1 -1
  104. package/dist/core/fields/document.js +277 -277
  105. package/dist/core/fields/field-group.d.ts +17 -17
  106. package/dist/core/fields/field-group.d.ts.map +1 -1
  107. package/dist/core/fields/field-group.js +6 -6
  108. package/dist/core/fields/field.d.ts +125 -125
  109. package/dist/core/fields/field.d.ts.map +1 -1
  110. package/dist/core/fields/field.js +148 -148
  111. package/dist/core/fields/fileField.d.ts +14 -14
  112. package/dist/core/fields/fileField.d.ts.map +1 -1
  113. package/dist/core/fields/fileField.js +5 -5
  114. package/dist/core/fields/index.d.ts +64 -64
  115. package/dist/core/fields/index.d.ts.map +1 -1
  116. package/dist/core/fields/index.js +18 -18
  117. package/dist/core/fields/map.d.ts +166 -166
  118. package/dist/core/fields/map.d.ts.map +1 -1
  119. package/dist/core/fields/map.js +152 -152
  120. package/dist/core/fields/number.d.ts +185 -185
  121. package/dist/core/fields/number.d.ts.map +1 -1
  122. package/dist/core/fields/number.js +241 -241
  123. package/dist/core/fields/password.d.ts +108 -108
  124. package/dist/core/fields/password.d.ts.map +1 -1
  125. package/dist/core/fields/password.js +133 -133
  126. package/dist/core/fields/photo.d.ts +288 -288
  127. package/dist/core/fields/photo.d.ts.map +1 -1
  128. package/dist/core/fields/photo.js +410 -410
  129. package/dist/core/fields/richText.d.ts +294 -294
  130. package/dist/core/fields/richText.d.ts.map +1 -1
  131. package/dist/core/fields/richText.js +338 -338
  132. package/dist/core/fields/select.d.ts +365 -365
  133. package/dist/core/fields/select.d.ts.map +1 -1
  134. package/dist/core/fields/select.js +499 -499
  135. package/dist/core/fields/selectMultiple.d.ts +235 -235
  136. package/dist/core/fields/selectMultiple.d.ts.map +1 -1
  137. package/dist/core/fields/selectMultiple.js +417 -417
  138. package/dist/core/fields/tags.d.ts +130 -130
  139. package/dist/core/fields/tags.d.ts.map +1 -1
  140. package/dist/core/fields/tags.js +105 -105
  141. package/dist/core/fields/text.d.ts +135 -135
  142. package/dist/core/fields/text.d.ts.map +1 -1
  143. package/dist/core/fields/text.js +157 -157
  144. package/dist/core/fields/textArea.d.ts +106 -106
  145. package/dist/core/fields/textArea.d.ts.map +1 -1
  146. package/dist/core/fields/textArea.js +126 -126
  147. package/dist/core/fields/video.d.ts +147 -147
  148. package/dist/core/fields/video.d.ts.map +1 -1
  149. package/dist/core/fields/video.js +248 -248
  150. package/dist/core/helpers/entity.d.ts +7 -7
  151. package/dist/core/helpers/entity.js +27 -27
  152. package/dist/core/helpers/index.d.ts +4 -4
  153. package/dist/core/helpers/index.d.ts.map +1 -1
  154. package/dist/core/helpers/index.js +3 -3
  155. package/dist/core/index.d.ts +7 -7
  156. package/dist/core/index.d.ts.map +1 -1
  157. package/dist/core/index.js +7 -7
  158. package/dist/core/sections/category.d.ts +282 -282
  159. package/dist/core/sections/category.d.ts.map +1 -1
  160. package/dist/core/sections/category.js +147 -147
  161. package/dist/core/sections/hasItems.d.ts +631 -631
  162. package/dist/core/sections/hasItems.d.ts.map +1 -1
  163. package/dist/core/sections/hasItems.js +144 -144
  164. package/dist/core/sections/index.d.ts +4 -4
  165. package/dist/core/sections/index.d.ts.map +1 -1
  166. package/dist/core/sections/index.js +4 -4
  167. package/dist/core/sections/section.d.ts +225 -225
  168. package/dist/core/sections/section.d.ts.map +1 -1
  169. package/dist/core/sections/section.js +341 -341
  170. package/dist/core/sections/simple.d.ts +98 -98
  171. package/dist/core/sections/simple.d.ts.map +1 -1
  172. package/dist/core/sections/simple.js +95 -95
  173. package/dist/core/security/dom.d.ts +10 -10
  174. package/dist/core/security/dom.js +92 -92
  175. package/dist/core/submit/ItemEditSubmit.d.ts +75 -75
  176. package/dist/core/submit/ItemEditSubmit.js +186 -186
  177. package/dist/core/submit/NewItemSubmit.d.ts +13 -13
  178. package/dist/core/submit/NewItemSubmit.js +93 -93
  179. package/dist/core/submit/SimpleSectionSubmit.d.ts +12 -12
  180. package/dist/core/submit/SimpleSectionSubmit.js +93 -93
  181. package/dist/core/submit/index.d.ts +4 -4
  182. package/dist/core/submit/index.js +4 -4
  183. package/dist/core/submit/submit.d.ts +115 -115
  184. package/dist/core/submit/submit.js +479 -479
  185. package/dist/core/types/index.d.ts +279 -279
  186. package/dist/core/types/index.d.ts.map +1 -1
  187. package/dist/core/types/index.js +1 -1
  188. package/dist/db/client.d.ts +8 -8
  189. package/dist/db/client.d.ts.map +1 -1
  190. package/dist/db/client.js +19 -19
  191. package/dist/db/config.d.ts +5 -5
  192. package/dist/db/config.js +22 -22
  193. package/dist/db/drizzle.config.d.ts +5 -5
  194. package/dist/db/drizzle.config.js +18 -18
  195. package/dist/db/index.d.ts +2 -2
  196. package/dist/db/index.js +3 -3
  197. package/dist/db/schema.d.ts +638 -638
  198. package/dist/db/schema.js +73 -73
  199. package/dist/index.d.ts +7 -7
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js +7 -7
  202. package/dist/translations/index.d.ts +2 -2
  203. package/dist/translations/index.js +15 -15
  204. package/dist/utils/CpanelApi.d.ts +24 -24
  205. package/dist/utils/CpanelApi.js +64 -64
  206. package/dist/utils/constants.d.ts +13 -13
  207. package/dist/utils/constants.js +61 -61
  208. package/dist/utils/index.d.ts +4 -4
  209. package/dist/utils/index.d.ts.map +1 -1
  210. package/dist/utils/index.js +4 -4
  211. package/dist/utils/utils.d.ts +59 -59
  212. package/dist/utils/utils.js +132 -132
  213. package/dist/validators/checkbox.d.ts +3 -3
  214. package/dist/validators/checkbox.d.ts.map +1 -1
  215. package/dist/validators/checkbox.js +12 -12
  216. package/dist/validators/color.d.ts +3 -3
  217. package/dist/validators/color.d.ts.map +1 -1
  218. package/dist/validators/color.js +7 -7
  219. package/dist/validators/date.d.ts +3 -3
  220. package/dist/validators/date.d.ts.map +1 -1
  221. package/dist/validators/date.js +5 -5
  222. package/dist/validators/document.d.ts +3 -3
  223. package/dist/validators/document.d.ts.map +1 -1
  224. package/dist/validators/document.js +57 -57
  225. package/dist/validators/index.d.ts +14 -14
  226. package/dist/validators/index.d.ts.map +1 -1
  227. package/dist/validators/index.js +14 -14
  228. package/dist/validators/map.d.ts +3 -3
  229. package/dist/validators/map.d.ts.map +1 -1
  230. package/dist/validators/map.js +5 -5
  231. package/dist/validators/number.d.ts +3 -3
  232. package/dist/validators/number.d.ts.map +1 -1
  233. package/dist/validators/number.js +20 -20
  234. package/dist/validators/password.d.ts +3 -3
  235. package/dist/validators/password.d.ts.map +1 -1
  236. package/dist/validators/password.js +11 -11
  237. package/dist/validators/photo.d.ts +3 -3
  238. package/dist/validators/photo.d.ts.map +1 -1
  239. package/dist/validators/photo.js +100 -100
  240. package/dist/validators/richText.d.ts +3 -3
  241. package/dist/validators/richText.d.ts.map +1 -1
  242. package/dist/validators/richText.js +8 -8
  243. package/dist/validators/select-multiple.d.ts +9 -9
  244. package/dist/validators/select-multiple.d.ts.map +1 -1
  245. package/dist/validators/select-multiple.js +20 -20
  246. package/dist/validators/select.d.ts +3 -3
  247. package/dist/validators/select.d.ts.map +1 -1
  248. package/dist/validators/select.js +5 -5
  249. package/dist/validators/text.d.ts +3 -3
  250. package/dist/validators/text.d.ts.map +1 -1
  251. package/dist/validators/text.js +7 -7
  252. package/dist/validators/textarea.d.ts +3 -3
  253. package/dist/validators/textarea.d.ts.map +1 -1
  254. package/dist/validators/textarea.js +7 -7
  255. package/dist/validators/video.d.ts +3 -3
  256. package/dist/validators/video.d.ts.map +1 -1
  257. package/dist/validators/video.js +57 -57
  258. package/package.json +2 -3
@@ -1,417 +1,417 @@
1
- import { Field, baseFieldConfigSchema } from "./field.js";
2
- import { entityKind } from "../helpers.js";
3
- import { selectFieldDestinationDbSchema, selectOptionSchema } from "./select.js";
4
- import { MysqlTableChecker } from "../db.js";
5
- import { db } from "../../db/client.js";
6
- import { sql } from 'drizzle-orm';
7
- import chalk from 'chalk';
8
- import * as z from 'zod';
9
- /*export type SelectMultipleValue = {
10
- value: string | number
11
- label: string
12
- }*/
13
- const selectMultipleSharedExtrasSchema = z.strictObject({
14
- /**
15
- * A destination table where the select values will be saved
16
- * When this is set, the value of the field will only be saved in the destination table as one row per each value
17
- */
18
- destinationDb: selectFieldDestinationDbSchema.optional(),
19
- checkValuesExist: z.boolean().optional().describe('Validate submitted values exist in the options'),
20
- });
21
- const selectMultipleFieldDbConfigSchema = selectMultipleSharedExtrasSchema.extend({
22
- options: z.never().optional(),
23
- db: z
24
- .strictObject({
25
- table: z.string().min(1).describe('Database table to read select options from'),
26
- identifier: z.string().min(1).describe('Identifier column for the option'),
27
- label: z.string().min(1).describe('Label column for the option'),
28
- orderBy: z.string().optional().describe('Optional order by column'),
29
- })
30
- .describe('Database configuration for select options'),
31
- section: z.never().optional(),
32
- });
33
- const selectMultipleFieldSectionConfigSchema = selectMultipleSharedExtrasSchema.extend({
34
- options: z.array(selectOptionSchema).optional(),
35
- db: z.never().optional(),
36
- section: z
37
- .custom()
38
- .describe('Section to derive select options from'),
39
- });
40
- const selectMultipleFieldStaticConfigSchema = selectMultipleSharedExtrasSchema.extend({
41
- options: z.array(selectOptionSchema).min(1).describe('List of static select options'),
42
- db: z.never().optional(),
43
- section: z.never().optional(),
44
- });
45
- const selectMultipleFieldConfigSchema = z.union([
46
- selectMultipleFieldDbConfigSchema,
47
- selectMultipleFieldSectionConfigSchema,
48
- selectMultipleFieldStaticConfigSchema,
49
- ]);
50
- export class SelectMultipleField extends Field {
51
- static [entityKind] = 'SelectMultipleField';
52
- checkValuesExist;
53
- optionsType;
54
- db;
55
- /*protected*/ options;
56
- destinationDb;
57
- value = undefined;
58
- _itemIdentifier;
59
- constructor(config) {
60
- super(config, 'select_multiple');
61
- this.checkValuesExist = config.checkValuesExist ?? true;
62
- this.destinationDb = config.destinationDb;
63
- /**
64
- * If options are static, set them
65
- */
66
- if (config.options && config.options?.length > 0) {
67
- this.options = config.options;
68
- this.optionsType = 'static';
69
- this.db = null;
70
- }
71
- else if (config.db) {
72
- /**
73
- * Else, check if the db config is set
74
- */
75
- this.db = config.db;
76
- this.optionsType = 'db';
77
- }
78
- else if (config.section) {
79
- /**
80
- * Else, check if the section config is set
81
- */
82
- /**
83
- * Runtime validation: Ensure the section is a HasItemsSection or CategorySection
84
- * TypeScript ensures type safety at compile time, but runtime data (from JSON/APIs)
85
- * can violate types, so we validate here for safety.
86
- */
87
- const sectionType = config.section.type;
88
- if (sectionType !== 'has_items' && sectionType !== 'category') {
89
- const message = `[SelectMultipleField: ${this.label}]: Section must be a HasItemsSection or CategorySection, but got type: ${sectionType ?? 'undefined'}`;
90
- console.error(chalk.red.bold(message));
91
- console.error(chalk.yellow('Please make sure the section is a HasItemsSection or CategorySection'));
92
- throw new Error(message);
93
- }
94
- /**
95
- * Build the section and set the options type to section
96
- */
97
- let section = config.section.build();
98
- this.optionsType = 'section';
99
- this.db = {
100
- table: section.db.table,
101
- identifier: section.db.identifier.name,
102
- label: section.headingField.name,
103
- orderBy: section.db.orderByField ? section.db.orderByField.name : section.db.identifier.name,
104
- };
105
- /**
106
- * Set the section instance to undefined to avoid memory leaks
107
- */
108
- section = undefined;
109
- }
110
- else {
111
- throw new Error('Select field requires either db, section or an options array.');
112
- }
113
- }
114
- static getEntityKind() {
115
- return SelectMultipleField[entityKind];
116
- }
117
- hasSqlNameAndValue() {
118
- return this.destinationDb === undefined;
119
- }
120
- /**
121
- * Get the value of the field
122
- */
123
- getValue() {
124
- return this.value;
125
- }
126
- /**
127
- * Get the value of the field when submitting to the database
128
- */
129
- getSubmitValue() {
130
- if (!this.hasSqlNameAndValue())
131
- return;
132
- /**
133
- * Set the value as a comma separated string of the values
134
- */
135
- return this.value?.map((value) => value.value).join(',');
136
- }
137
- /**
138
- * Set the value of the field
139
- * @param value can be a string of comma-separated values coming from the db table,
140
- * or an array of `SelectOption` objects coming from the client
141
- */
142
- setValue(value) {
143
- if (typeof value === 'string') {
144
- /**
145
- * Value can be an encoded JSON string sent from the client,
146
- * try parsing the value as a JSON string
147
- */
148
- let decoded;
149
- try {
150
- decoded = JSON.parse(value);
151
- }
152
- catch (error) {
153
- decoded = undefined;
154
- }
155
- if (!Array.isArray(decoded)) {
156
- /**
157
- * If the decoded value is not an array (which means it should be a string coming from the db table),
158
- * split the values by comma and set the value as an array of objects
159
- */
160
- const values = value.split(',');
161
- values.map((value) => {
162
- /**
163
- * Check if the value exists in the options and return the value and text
164
- */
165
- const option = this.options?.find((option) => option.value.toString() === value.toString());
166
- if (option) {
167
- if (!this.value) {
168
- this.value = [];
169
- }
170
- this.value.push(option);
171
- }
172
- });
173
- }
174
- else {
175
- /**
176
- * If the decoded value is an array, set the value as the decoded value
177
- */
178
- this.value = decoded;
179
- }
180
- }
181
- else {
182
- this.value = value;
183
- }
184
- }
185
- exportForClient() {
186
- return {
187
- ...super.exportForClient(),
188
- options: this.options,
189
- value: this.value,
190
- };
191
- }
192
- async postSubmit(itemId) {
193
- if (!this.destinationDb)
194
- return;
195
- this._itemIdentifier = itemId;
196
- try {
197
- /**
198
- * Delete the existing values
199
- */
200
- await db.execute(sql `DELETE FROM \`${sql.raw(this.destinationDb.table)}\` WHERE \`${sql.raw(this.destinationDb.itemIdentifier)}\` = ${this._itemIdentifier}`);
201
- /**
202
- * Prepare the sql statement
203
- */
204
- const sqlChunks = [];
205
- sqlChunks.push(sql `INSERT INTO \`${sql.raw(this.destinationDb.table)}\` (\`${sql.raw(this.destinationDb.selectIdentifier)}\`, \`${sql.raw(this.destinationDb.itemIdentifier)}\`) VALUES `);
206
- /**
207
- * Loop through the values and add them to the sql statement values
208
- */
209
- if (this.value && this.value.length > 0) {
210
- const count = this.value.length;
211
- this.value.forEach((value, index) => {
212
- sqlChunks.push(sql `(${value.value}, ${this._itemIdentifier})`);
213
- if (index < count - 1) {
214
- /**
215
- * Add a comma to separate the values in the sql statement
216
- */
217
- sqlChunks.push(sql `, `);
218
- }
219
- });
220
- }
221
- /**
222
- * Join the sql chunks
223
- */
224
- const finalSql = sql.join(sqlChunks);
225
- /**
226
- * Execute the sql statement
227
- */
228
- await db.execute(finalSql);
229
- }
230
- catch (error) {
231
- throw new Error(`${this.label}: Error saving values to the destination table`);
232
- }
233
- }
234
- async postSubmitRollback() {
235
- if (!this.destinationDb)
236
- return;
237
- if (!this._itemIdentifier) {
238
- throw new Error(`${this.label}: Item identifier is not set. Make sure to set the item identifier by calling postSubmit() before calling postSubmitRollback()`);
239
- }
240
- try {
241
- await db.execute(sql `DELETE FROM \`${sql.raw(this.destinationDb.table)}\` WHERE \`${sql.raw(this.destinationDb.itemIdentifier)}\` = ${this._itemIdentifier}`);
242
- }
243
- catch (error) {
244
- throw new Error(`${this.label}: Error deleting values from the destination table`);
245
- }
246
- }
247
- /**
248
- * Build the field to be used in a form
249
- */
250
- async build() {
251
- if (this.destinationDb) {
252
- this.checkDestinationDBConfig();
253
- await this.checkDestinationDBTableAndColumns();
254
- }
255
- /**
256
- * If options are already set, return
257
- */
258
- if (this.options && this.optionsType === 'static')
259
- return;
260
- /**
261
- * Initialize the select db and options
262
- */
263
- this.checkDBConfig();
264
- await this.checkDBTableAndColumns();
265
- /**
266
- * Fetch the options
267
- */
268
- await this.fetchOptions();
269
- }
270
- async fetchOptions() {
271
- /**
272
- * Let's get the options for the select input
273
- */
274
- let selectStatement = `select * from \`${this.db.table}\` ORDER BY \`${this.db.orderBy ?? this.db.identifier}\` DESC`;
275
- /**
276
- * Get the options from the table
277
- */
278
- const selectOptionsRows = await db.execute(sql.raw(selectStatement));
279
- const rows = selectOptionsRows[0];
280
- const inputSelectTableIdentifierField = this.db.identifier;
281
- const inputSelectTableHeadingField = this.db.label;
282
- /**
283
- * Set the options
284
- */
285
- this.options = rows.map((row) => ({
286
- value: row[inputSelectTableIdentifierField],
287
- label: row[inputSelectTableHeadingField],
288
- }));
289
- }
290
- /**
291
- * Check if table and columns exist
292
- * @private
293
- */
294
- async checkDBTableAndColumns() {
295
- const columns = await MysqlTableChecker.getColumns(this.db.table);
296
- if (!columns.includes(this.db.identifier) || !columns.includes(this.db.label)) {
297
- throw new Error(`Field ${this.label}: Table ${this.db.table} and/or columns: ${this.db.identifier}, ${this.db.label} do not exist in the database!`);
298
- }
299
- }
300
- /**
301
- * Check if destination table and columns exist
302
- * @private
303
- */
304
- async checkDestinationDBTableAndColumns() {
305
- if (!this.destinationDb) {
306
- throw new Error(`Field ${this.label}: Destination table, select identifier and item identifier are required`);
307
- }
308
- const columns = await MysqlTableChecker.getColumns(this.destinationDb.table);
309
- if (!columns.includes(this.destinationDb.selectIdentifier) ||
310
- !columns.includes(this.destinationDb.itemIdentifier)) {
311
- throw new Error(`Field ${this.label}: Table ${this.destinationDb.table} and/or columns: ${this.destinationDb.selectIdentifier}, ${this.destinationDb.itemIdentifier} do not exist in the database!`);
312
- }
313
- }
314
- /**
315
- * Check the db config is set
316
- * @private
317
- */
318
- checkDBConfig() {
319
- if (this.db.table.trim().length === 0 ||
320
- this.db.identifier.trim().length === 0 ||
321
- this.db.label.trim().length === 0) {
322
- throw new Error(`Field ${this.label}: Select table, identifier and label are required`);
323
- }
324
- }
325
- /**
326
- * Check the destination db config is set
327
- * @private
328
- */
329
- checkDestinationDBConfig() {
330
- if (this.destinationDb?.table.trim().length === 0 ||
331
- this.destinationDb?.selectIdentifier.trim().length === 0 ||
332
- this.destinationDb?.itemIdentifier.trim().length === 0) {
333
- throw new Error(`Field ${this.label}: Destination table, select identifier and item identifier are required`);
334
- }
335
- }
336
- checkRequired() {
337
- /**
338
- * Check if the field is required
339
- */
340
- if (this.required) {
341
- if (!this.value || this.value.length === 0) {
342
- throw new Error(`Field ${this.label} is required`);
343
- }
344
- }
345
- }
346
- /**
347
- * Prepare the field for submission
348
- */
349
- async prepareForSubmission() {
350
- if (this.optionsType !== 'static') {
351
- this.checkDBConfig();
352
- await this.checkDBTableAndColumns();
353
- /**
354
- * Fetch the options
355
- */
356
- await this.fetchOptions();
357
- }
358
- if (this.destinationDb) {
359
- this.checkDestinationDBConfig();
360
- await this.checkDestinationDBTableAndColumns();
361
- }
362
- if (this.checkValuesExist && this.value && this.value.length > 0) {
363
- /**
364
- * Check if the values exist in the table
365
- * This is now safe because we have verified the table and columns exist in the database
366
- */
367
- this.value.forEach((value) => {
368
- if (!this.options?.find((option) => option.value.toString() === value.value.toString())) {
369
- throw new Error(`Field ${this.label}: Option ${value.label} does not exist in the options`);
370
- }
371
- });
372
- }
373
- }
374
- }
375
- const selectMultipleFieldDbOptionsSchema = z.strictObject({
376
- ...baseFieldConfigSchema.shape,
377
- ...selectMultipleFieldDbConfigSchema.shape,
378
- });
379
- const selectMultipleFieldSectionOptionsSchema = z.strictObject({
380
- ...baseFieldConfigSchema.shape,
381
- ...selectMultipleFieldSectionConfigSchema.shape,
382
- });
383
- const selectMultipleFieldStaticOptionsSchema = z.strictObject({
384
- ...baseFieldConfigSchema.shape,
385
- ...selectMultipleFieldStaticConfigSchema.shape,
386
- });
387
- const selectMultipleFieldOptionsSchema = z.union([
388
- selectMultipleFieldDbOptionsSchema,
389
- selectMultipleFieldSectionOptionsSchema,
390
- selectMultipleFieldStaticOptionsSchema,
391
- ]);
392
- const selectMultipleFieldHelperConfigSchema = z.intersection(selectMultipleFieldOptionsSchema, z.strictObject({
393
- type: z.literal('select_multiple').describe('The type of the field'),
394
- build: z
395
- .function()
396
- .output(z.instanceof(SelectMultipleField))
397
- .describe('Build a SelectMultipleField instance from this config'),
398
- }));
399
- /**
400
- * Helper function to create a multiple select field configuration
401
- * Returns a config object with a build() method that can be serialized and used anywhere.
402
- */
403
- export function selectMultipleField(field) {
404
- const result = selectMultipleFieldOptionsSchema.safeParse(field);
405
- if (!result.success) {
406
- throw new Error(`[Field: ${field.name}]: ${z.prettifyError(result.error)}`);
407
- }
408
- const parsedField = result.data;
409
- const config = {
410
- ...parsedField,
411
- type: 'select_multiple',
412
- build() {
413
- return new SelectMultipleField(parsedField);
414
- },
415
- };
416
- return config;
417
- }
1
+ import { Field, baseFieldConfigSchema } from './field.js';
2
+ import { entityKind } from '../helpers/index.js';
3
+ import { selectFieldDestinationDbSchema, selectOptionSchema } from './select.js';
4
+ import { MysqlTableChecker } from '../db/index.js';
5
+ import { db } from '../../db/client.js';
6
+ import { sql } from 'drizzle-orm';
7
+ import chalk from 'chalk';
8
+ import * as z from 'zod';
9
+ /*export type SelectMultipleValue = {
10
+ value: string | number
11
+ label: string
12
+ }*/
13
+ const selectMultipleSharedExtrasSchema = z.strictObject({
14
+ /**
15
+ * A destination table where the select values will be saved
16
+ * When this is set, the value of the field will only be saved in the destination table as one row per each value
17
+ */
18
+ destinationDb: selectFieldDestinationDbSchema.optional(),
19
+ checkValuesExist: z.boolean().optional().describe('Validate submitted values exist in the options'),
20
+ });
21
+ const selectMultipleFieldDbConfigSchema = selectMultipleSharedExtrasSchema.extend({
22
+ options: z.never().optional(),
23
+ db: z
24
+ .strictObject({
25
+ table: z.string().min(1).describe('Database table to read select options from'),
26
+ identifier: z.string().min(1).describe('Identifier column for the option'),
27
+ label: z.string().min(1).describe('Label column for the option'),
28
+ orderBy: z.string().optional().describe('Optional order by column'),
29
+ })
30
+ .describe('Database configuration for select options'),
31
+ section: z.never().optional(),
32
+ });
33
+ const selectMultipleFieldSectionConfigSchema = selectMultipleSharedExtrasSchema.extend({
34
+ options: z.array(selectOptionSchema).optional(),
35
+ db: z.never().optional(),
36
+ section: z
37
+ .custom()
38
+ .describe('Section to derive select options from'),
39
+ });
40
+ const selectMultipleFieldStaticConfigSchema = selectMultipleSharedExtrasSchema.extend({
41
+ options: z.array(selectOptionSchema).min(1).describe('List of static select options'),
42
+ db: z.never().optional(),
43
+ section: z.never().optional(),
44
+ });
45
+ const selectMultipleFieldConfigSchema = z.union([
46
+ selectMultipleFieldDbConfigSchema,
47
+ selectMultipleFieldSectionConfigSchema,
48
+ selectMultipleFieldStaticConfigSchema,
49
+ ]);
50
+ export class SelectMultipleField extends Field {
51
+ static [entityKind] = 'SelectMultipleField';
52
+ checkValuesExist;
53
+ optionsType;
54
+ db;
55
+ /*protected*/ options;
56
+ destinationDb;
57
+ value = undefined;
58
+ _itemIdentifier;
59
+ constructor(config) {
60
+ super(config, 'select_multiple');
61
+ this.checkValuesExist = config.checkValuesExist ?? true;
62
+ this.destinationDb = config.destinationDb;
63
+ /**
64
+ * If options are static, set them
65
+ */
66
+ if (config.options && config.options?.length > 0) {
67
+ this.options = config.options;
68
+ this.optionsType = 'static';
69
+ this.db = null;
70
+ }
71
+ else if (config.db) {
72
+ /**
73
+ * Else, check if the db config is set
74
+ */
75
+ this.db = config.db;
76
+ this.optionsType = 'db';
77
+ }
78
+ else if (config.section) {
79
+ /**
80
+ * Else, check if the section config is set
81
+ */
82
+ /**
83
+ * Runtime validation: Ensure the section is a HasItemsSection or CategorySection
84
+ * TypeScript ensures type safety at compile time, but runtime data (from JSON/APIs)
85
+ * can violate types, so we validate here for safety.
86
+ */
87
+ const sectionType = config.section.type;
88
+ if (sectionType !== 'has_items' && sectionType !== 'category') {
89
+ const message = `[SelectMultipleField: ${this.label}]: Section must be a HasItemsSection or CategorySection, but got type: ${sectionType ?? 'undefined'}`;
90
+ console.error(chalk.red.bold(message));
91
+ console.error(chalk.yellow('Please make sure the section is a HasItemsSection or CategorySection'));
92
+ throw new Error(message);
93
+ }
94
+ /**
95
+ * Build the section and set the options type to section
96
+ */
97
+ let section = config.section.build();
98
+ this.optionsType = 'section';
99
+ this.db = {
100
+ table: section.db.table,
101
+ identifier: section.db.identifier.name,
102
+ label: section.headingField.name,
103
+ orderBy: section.db.orderByField ? section.db.orderByField.name : section.db.identifier.name,
104
+ };
105
+ /**
106
+ * Set the section instance to undefined to avoid memory leaks
107
+ */
108
+ section = undefined;
109
+ }
110
+ else {
111
+ throw new Error('Select field requires either db, section or an options array.');
112
+ }
113
+ }
114
+ static getEntityKind() {
115
+ return SelectMultipleField[entityKind];
116
+ }
117
+ hasSqlNameAndValue() {
118
+ return this.destinationDb === undefined;
119
+ }
120
+ /**
121
+ * Get the value of the field
122
+ */
123
+ getValue() {
124
+ return this.value;
125
+ }
126
+ /**
127
+ * Get the value of the field when submitting to the database
128
+ */
129
+ getSubmitValue() {
130
+ if (!this.hasSqlNameAndValue())
131
+ return;
132
+ /**
133
+ * Set the value as a comma separated string of the values
134
+ */
135
+ return this.value?.map((value) => value.value).join(',');
136
+ }
137
+ /**
138
+ * Set the value of the field
139
+ * @param value can be a string of comma-separated values coming from the db table,
140
+ * or an array of `SelectOption` objects coming from the client
141
+ */
142
+ setValue(value) {
143
+ if (typeof value === 'string') {
144
+ /**
145
+ * Value can be an encoded JSON string sent from the client,
146
+ * try parsing the value as a JSON string
147
+ */
148
+ let decoded;
149
+ try {
150
+ decoded = JSON.parse(value);
151
+ }
152
+ catch (error) {
153
+ decoded = undefined;
154
+ }
155
+ if (!Array.isArray(decoded)) {
156
+ /**
157
+ * If the decoded value is not an array (which means it should be a string coming from the db table),
158
+ * split the values by comma and set the value as an array of objects
159
+ */
160
+ const values = value.split(',');
161
+ values.map((value) => {
162
+ /**
163
+ * Check if the value exists in the options and return the value and text
164
+ */
165
+ const option = this.options?.find((option) => option.value.toString() === value.toString());
166
+ if (option) {
167
+ if (!this.value) {
168
+ this.value = [];
169
+ }
170
+ this.value.push(option);
171
+ }
172
+ });
173
+ }
174
+ else {
175
+ /**
176
+ * If the decoded value is an array, set the value as the decoded value
177
+ */
178
+ this.value = decoded;
179
+ }
180
+ }
181
+ else {
182
+ this.value = value;
183
+ }
184
+ }
185
+ exportForClient() {
186
+ return {
187
+ ...super.exportForClient(),
188
+ options: this.options,
189
+ value: this.value,
190
+ };
191
+ }
192
+ async postSubmit(itemId) {
193
+ if (!this.destinationDb)
194
+ return;
195
+ this._itemIdentifier = itemId;
196
+ try {
197
+ /**
198
+ * Delete the existing values
199
+ */
200
+ await db.execute(sql `DELETE FROM \`${sql.raw(this.destinationDb.table)}\` WHERE \`${sql.raw(this.destinationDb.itemIdentifier)}\` = ${this._itemIdentifier}`);
201
+ /**
202
+ * Prepare the sql statement
203
+ */
204
+ const sqlChunks = [];
205
+ sqlChunks.push(sql `INSERT INTO \`${sql.raw(this.destinationDb.table)}\` (\`${sql.raw(this.destinationDb.selectIdentifier)}\`, \`${sql.raw(this.destinationDb.itemIdentifier)}\`) VALUES `);
206
+ /**
207
+ * Loop through the values and add them to the sql statement values
208
+ */
209
+ if (this.value && this.value.length > 0) {
210
+ const count = this.value.length;
211
+ this.value.forEach((value, index) => {
212
+ sqlChunks.push(sql `(${value.value}, ${this._itemIdentifier})`);
213
+ if (index < count - 1) {
214
+ /**
215
+ * Add a comma to separate the values in the sql statement
216
+ */
217
+ sqlChunks.push(sql `, `);
218
+ }
219
+ });
220
+ }
221
+ /**
222
+ * Join the sql chunks
223
+ */
224
+ const finalSql = sql.join(sqlChunks);
225
+ /**
226
+ * Execute the sql statement
227
+ */
228
+ await db.execute(finalSql);
229
+ }
230
+ catch (error) {
231
+ throw new Error(`${this.label}: Error saving values to the destination table`);
232
+ }
233
+ }
234
+ async postSubmitRollback() {
235
+ if (!this.destinationDb)
236
+ return;
237
+ if (!this._itemIdentifier) {
238
+ throw new Error(`${this.label}: Item identifier is not set. Make sure to set the item identifier by calling postSubmit() before calling postSubmitRollback()`);
239
+ }
240
+ try {
241
+ await db.execute(sql `DELETE FROM \`${sql.raw(this.destinationDb.table)}\` WHERE \`${sql.raw(this.destinationDb.itemIdentifier)}\` = ${this._itemIdentifier}`);
242
+ }
243
+ catch (error) {
244
+ throw new Error(`${this.label}: Error deleting values from the destination table`);
245
+ }
246
+ }
247
+ /**
248
+ * Build the field to be used in a form
249
+ */
250
+ async build() {
251
+ if (this.destinationDb) {
252
+ this.checkDestinationDBConfig();
253
+ await this.checkDestinationDBTableAndColumns();
254
+ }
255
+ /**
256
+ * If options are already set, return
257
+ */
258
+ if (this.options && this.optionsType === 'static')
259
+ return;
260
+ /**
261
+ * Initialize the select db and options
262
+ */
263
+ this.checkDBConfig();
264
+ await this.checkDBTableAndColumns();
265
+ /**
266
+ * Fetch the options
267
+ */
268
+ await this.fetchOptions();
269
+ }
270
+ async fetchOptions() {
271
+ /**
272
+ * Let's get the options for the select input
273
+ */
274
+ let selectStatement = `select * from \`${this.db.table}\` ORDER BY \`${this.db.orderBy ?? this.db.identifier}\` DESC`;
275
+ /**
276
+ * Get the options from the table
277
+ */
278
+ const selectOptionsRows = await db.execute(sql.raw(selectStatement));
279
+ const rows = selectOptionsRows[0];
280
+ const inputSelectTableIdentifierField = this.db.identifier;
281
+ const inputSelectTableHeadingField = this.db.label;
282
+ /**
283
+ * Set the options
284
+ */
285
+ this.options = rows.map((row) => ({
286
+ value: row[inputSelectTableIdentifierField],
287
+ label: row[inputSelectTableHeadingField],
288
+ }));
289
+ }
290
+ /**
291
+ * Check if table and columns exist
292
+ * @private
293
+ */
294
+ async checkDBTableAndColumns() {
295
+ const columns = await MysqlTableChecker.getColumns(this.db.table);
296
+ if (!columns.includes(this.db.identifier) || !columns.includes(this.db.label)) {
297
+ throw new Error(`Field ${this.label}: Table ${this.db.table} and/or columns: ${this.db.identifier}, ${this.db.label} do not exist in the database!`);
298
+ }
299
+ }
300
+ /**
301
+ * Check if destination table and columns exist
302
+ * @private
303
+ */
304
+ async checkDestinationDBTableAndColumns() {
305
+ if (!this.destinationDb) {
306
+ throw new Error(`Field ${this.label}: Destination table, select identifier and item identifier are required`);
307
+ }
308
+ const columns = await MysqlTableChecker.getColumns(this.destinationDb.table);
309
+ if (!columns.includes(this.destinationDb.selectIdentifier) ||
310
+ !columns.includes(this.destinationDb.itemIdentifier)) {
311
+ throw new Error(`Field ${this.label}: Table ${this.destinationDb.table} and/or columns: ${this.destinationDb.selectIdentifier}, ${this.destinationDb.itemIdentifier} do not exist in the database!`);
312
+ }
313
+ }
314
+ /**
315
+ * Check the db config is set
316
+ * @private
317
+ */
318
+ checkDBConfig() {
319
+ if (this.db.table.trim().length === 0 ||
320
+ this.db.identifier.trim().length === 0 ||
321
+ this.db.label.trim().length === 0) {
322
+ throw new Error(`Field ${this.label}: Select table, identifier and label are required`);
323
+ }
324
+ }
325
+ /**
326
+ * Check the destination db config is set
327
+ * @private
328
+ */
329
+ checkDestinationDBConfig() {
330
+ if (this.destinationDb?.table.trim().length === 0 ||
331
+ this.destinationDb?.selectIdentifier.trim().length === 0 ||
332
+ this.destinationDb?.itemIdentifier.trim().length === 0) {
333
+ throw new Error(`Field ${this.label}: Destination table, select identifier and item identifier are required`);
334
+ }
335
+ }
336
+ checkRequired() {
337
+ /**
338
+ * Check if the field is required
339
+ */
340
+ if (this.required) {
341
+ if (!this.value || this.value.length === 0) {
342
+ throw new Error(`Field ${this.label} is required`);
343
+ }
344
+ }
345
+ }
346
+ /**
347
+ * Prepare the field for submission
348
+ */
349
+ async prepareForSubmission() {
350
+ if (this.optionsType !== 'static') {
351
+ this.checkDBConfig();
352
+ await this.checkDBTableAndColumns();
353
+ /**
354
+ * Fetch the options
355
+ */
356
+ await this.fetchOptions();
357
+ }
358
+ if (this.destinationDb) {
359
+ this.checkDestinationDBConfig();
360
+ await this.checkDestinationDBTableAndColumns();
361
+ }
362
+ if (this.checkValuesExist && this.value && this.value.length > 0) {
363
+ /**
364
+ * Check if the values exist in the table
365
+ * This is now safe because we have verified the table and columns exist in the database
366
+ */
367
+ this.value.forEach((value) => {
368
+ if (!this.options?.find((option) => option.value.toString() === value.value.toString())) {
369
+ throw new Error(`Field ${this.label}: Option ${value.label} does not exist in the options`);
370
+ }
371
+ });
372
+ }
373
+ }
374
+ }
375
+ const selectMultipleFieldDbOptionsSchema = z.strictObject({
376
+ ...baseFieldConfigSchema.shape,
377
+ ...selectMultipleFieldDbConfigSchema.shape,
378
+ });
379
+ const selectMultipleFieldSectionOptionsSchema = z.strictObject({
380
+ ...baseFieldConfigSchema.shape,
381
+ ...selectMultipleFieldSectionConfigSchema.shape,
382
+ });
383
+ const selectMultipleFieldStaticOptionsSchema = z.strictObject({
384
+ ...baseFieldConfigSchema.shape,
385
+ ...selectMultipleFieldStaticConfigSchema.shape,
386
+ });
387
+ const selectMultipleFieldOptionsSchema = z.union([
388
+ selectMultipleFieldDbOptionsSchema,
389
+ selectMultipleFieldSectionOptionsSchema,
390
+ selectMultipleFieldStaticOptionsSchema,
391
+ ]);
392
+ const selectMultipleFieldHelperConfigSchema = z.intersection(selectMultipleFieldOptionsSchema, z.strictObject({
393
+ type: z.literal('select_multiple').describe('The type of the field'),
394
+ build: z
395
+ .function()
396
+ .output(z.instanceof(SelectMultipleField))
397
+ .describe('Build a SelectMultipleField instance from this config'),
398
+ }));
399
+ /**
400
+ * Helper function to create a multiple select field configuration
401
+ * Returns a config object with a build() method that can be serialized and used anywhere.
402
+ */
403
+ export function selectMultipleField(field) {
404
+ const result = selectMultipleFieldOptionsSchema.safeParse(field);
405
+ if (!result.success) {
406
+ throw new Error(`[Field: ${field.name}]: ${z.prettifyError(result.error)}`);
407
+ }
408
+ const parsedField = result.data;
409
+ const config = {
410
+ ...parsedField,
411
+ type: 'select_multiple',
412
+ build() {
413
+ return new SelectMultipleField(parsedField);
414
+ },
415
+ };
416
+ return config;
417
+ }