nextjs-cms 0.9.6 → 0.9.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.
- package/LICENSE +21 -21
- package/README.md +290 -290
- package/dist/api/index.d.ts +3 -3
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/lib/serverActions.d.ts +3 -3
- package/dist/api/lib/serverActions.d.ts.map +1 -1
- package/dist/api/lib/serverActions.js +6 -0
- package/dist/api/root.d.ts +6 -6
- package/dist/api/root.d.ts.map +1 -1
- package/dist/api/routers/config.d.ts.map +1 -1
- package/dist/api/routers/hasItemsSection.d.ts.map +1 -1
- package/dist/api/routers/navigation.d.ts +3 -3
- package/dist/api/routers/simpleSection.d.ts.map +1 -1
- package/dist/cli/lib/db-config.js +10 -10
- package/dist/core/db/table-checker/MysqlTable.js +8 -8
- package/dist/core/factories/FieldFactory.d.ts.map +1 -1
- package/dist/core/factories/FieldFactory.js +5 -1
- package/dist/core/factories/section-factory-with-esbuild.js +9 -9
- package/dist/core/factories/section-factory-with-jiti.js +9 -9
- package/dist/core/fields/date-range.d.ts +119 -0
- package/dist/core/fields/date-range.d.ts.map +1 -0
- package/dist/core/fields/date-range.js +175 -0
- package/dist/core/fields/date.d.ts +15 -3
- package/dist/core/fields/date.d.ts.map +1 -1
- package/dist/core/fields/date.js +34 -17
- package/dist/core/fields/dateRange.d.ts +133 -94
- package/dist/core/fields/dateRange.d.ts.map +1 -1
- package/dist/core/fields/dateRange.js +39 -8
- package/dist/core/fields/index.d.ts +3 -3
- package/dist/core/fields/index.d.ts.map +1 -1
- package/dist/core/fields/index.js +1 -1
- package/dist/core/sections/category.d.ts +4 -4
- package/dist/core/sections/hasItems.d.ts +64 -64
- package/dist/core/sections/section.d.ts +3 -3
- package/dist/core/sections/simple.d.ts +4 -4
- package/dist/core/submit/submit.d.ts.map +1 -1
- package/dist/core/submit/submit.js +3 -1
- package/dist/translations/base/en.d.ts +3 -0
- package/dist/translations/base/en.d.ts.map +1 -1
- package/dist/translations/base/en.js +3 -0
- package/dist/translations/client.d.ts +40 -4
- package/dist/translations/client.d.ts.map +1 -1
- package/dist/translations/server.d.ts +40 -4
- package/dist/translations/server.d.ts.map +1 -1
- package/dist/utils/date-bounds.d.ts +10 -0
- package/dist/utils/date-bounds.d.ts.map +1 -0
- package/dist/utils/date-bounds.js +36 -0
- package/dist/utils/dateBounds.d.ts +10 -0
- package/dist/utils/dateBounds.d.ts.map +1 -0
- package/dist/utils/dateBounds.js +36 -0
- package/dist/validators/date-range.d.ts +11 -0
- package/dist/validators/date-range.d.ts.map +1 -0
- package/dist/validators/date-range.js +38 -0
- package/dist/validators/date.d.ts.map +1 -1
- package/dist/validators/date.js +23 -1
- package/dist/validators/dateRange.d.ts.map +1 -1
- package/dist/validators/dateRange.js +23 -1
- package/dist/validators/index.d.ts +1 -1
- package/dist/validators/index.d.ts.map +1 -1
- package/dist/validators/index.js +1 -1
- package/package.json +1 -1
- package/dist/translations/locale-cookie.d.ts +0 -24
- package/dist/translations/locale-cookie.d.ts.map +0 -1
- package/dist/translations/locale-cookie.js +0 -44
- package/dist/translations/locale-utils.d.ts +0 -8
- package/dist/translations/locale-utils.d.ts.map +0 -1
- package/dist/translations/locale-utils.js +0 -11
- package/dist/translations/localization.d.ts +0 -40
- package/dist/translations/localization.d.ts.map +0 -1
- package/dist/translations/localization.js +0 -48
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simpleSection.d.ts","sourceRoot":"","sources":["../../../src/api/routers/simpleSection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAOxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"simpleSection.d.ts","sourceRoot":"","sources":["../../../src/api/routers/simpleSection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAOxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBA2Du4a,CAAC;;;;;uBAAuE,CAAC;;;;;;uBAA6G,CAAC;;;;;;;;;;;;;;;GAD5lb,CAAA"}
|
|
@@ -140,16 +140,16 @@ export async function dbConfigSetup(options) {
|
|
|
140
140
|
/**
|
|
141
141
|
* Prepare new environment variables block
|
|
142
142
|
*/
|
|
143
|
-
const newEnvBlock = `
|
|
144
|
-
|
|
145
|
-
####
|
|
146
|
-
# generated by the init script
|
|
147
|
-
####
|
|
148
|
-
DB_HOST=${dbHost}
|
|
149
|
-
DB_PORT=${dbPort}
|
|
150
|
-
DB_NAME=${dbName}
|
|
151
|
-
DB_USER=${dbUser}
|
|
152
|
-
DB_PASSWORD='${dbPassword}'
|
|
143
|
+
const newEnvBlock = `
|
|
144
|
+
|
|
145
|
+
####
|
|
146
|
+
# generated by the init script
|
|
147
|
+
####
|
|
148
|
+
DB_HOST=${dbHost}
|
|
149
|
+
DB_PORT=${dbPort}
|
|
150
|
+
DB_NAME=${dbName}
|
|
151
|
+
DB_USER=${dbUser}
|
|
152
|
+
DB_PASSWORD='${dbPassword}'
|
|
153
153
|
`;
|
|
154
154
|
/**
|
|
155
155
|
* Append the new credentials to the .env file
|
|
@@ -22,10 +22,10 @@ export class MysqlTableChecker extends DbTableChecker {
|
|
|
22
22
|
return _tables;
|
|
23
23
|
}
|
|
24
24
|
static async getColumns(tableName) {
|
|
25
|
-
const statement = sql `
|
|
26
|
-
SELECT COLUMN_NAME
|
|
27
|
-
FROM information_schema.COLUMNS c
|
|
28
|
-
inner join information_schema.TABLES t ON t.TABLE_NAME = c.TABLE_NAME
|
|
25
|
+
const statement = sql `
|
|
26
|
+
SELECT COLUMN_NAME
|
|
27
|
+
FROM information_schema.COLUMNS c
|
|
28
|
+
inner join information_schema.TABLES t ON t.TABLE_NAME = c.TABLE_NAME
|
|
29
29
|
WHERE t.TABLE_NAME = ${tableName}`;
|
|
30
30
|
const _cols = [];
|
|
31
31
|
const _res = await db.execute(statement);
|
|
@@ -82,10 +82,10 @@ export class MysqlTableChecker extends DbTableChecker {
|
|
|
82
82
|
fullTextKeys.push({ name: key, columns: _fullTextKeyMap[key] });
|
|
83
83
|
}
|
|
84
84
|
// Foreign keys should be retrieved from information_schema
|
|
85
|
-
const [foreignKeys] = await db.execute(sql `
|
|
86
|
-
SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
|
87
|
-
FROM information_schema.KEY_COLUMN_USAGE
|
|
88
|
-
WHERE TABLE_NAME = ${table} AND CONSTRAINT_NAME != 'PRIMARY' AND REFERENCED_TABLE_NAME IS NOT NULL;
|
|
85
|
+
const [foreignKeys] = await db.execute(sql `
|
|
86
|
+
SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
|
87
|
+
FROM information_schema.KEY_COLUMN_USAGE
|
|
88
|
+
WHERE TABLE_NAME = ${table} AND CONSTRAINT_NAME != 'PRIMARY' AND REFERENCED_TABLE_NAME IS NOT NULL;
|
|
89
89
|
`);
|
|
90
90
|
return { primaryKeys, uniqueKeys, indexKeys, foreignKeys, fullTextKeys };
|
|
91
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldFactory.d.ts","sourceRoot":"","sources":["../../../src/core/factories/FieldFactory.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AASlD,KAAK,eAAe,GACd;IACI,IAAI,EAAE,KAAK,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,KAAK,CAAA;CACjB,GACD;IACI,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1B,CAAA;AAEP,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,aAAa,CAAa;IAClC,SAAS,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,WAAW,EAAE,MAAM,CAAA;IAC7B,OAAO,CAAC,YAAY,CAA4B;IAGhD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAY;IAEjD;;OAEG;gBACS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,eAAe;IASnE;;OAEG;IACU,UAAU;YAUT,aAAa;YASb,qBAAqB;IAqInC;;;;OAIG;YACW,qBAAqB;IAsDnC;;;;OAIG;YACW,cAAc;IAuB5B;;;;;OAKG;YACW,YAAY;
|
|
1
|
+
{"version":3,"file":"FieldFactory.d.ts","sourceRoot":"","sources":["../../../src/core/factories/FieldFactory.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AASlD,KAAK,eAAe,GACd;IACI,IAAI,EAAE,KAAK,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,KAAK,CAAA;CACjB,GACD;IACI,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1B,CAAA;AAEP,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,aAAa,CAAa;IAClC,SAAS,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,WAAW,EAAE,MAAM,CAAA;IAC7B,OAAO,CAAC,YAAY,CAA4B;IAGhD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAY;IAEjD;;OAEG;gBACS,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,eAAe;IASnE;;OAEG;IACU,UAAU;YAUT,aAAa;YASb,qBAAqB;IAqInC;;;;OAIG;YACW,qBAAqB;IAsDnC;;;;OAIG;YACW,cAAc;IAuB5B;;;;;OAKG;YACW,YAAY;IAsB1B,OAAO,CAAC,kBAAkB;IAa1B;;OAEG;IACU,cAAc;IAqB3B;;;OAGG;IACH,OAAO,CAAC,UAAU;IA4ElB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;IACU,gBAAgB;;;;;;;;;;;;;;;;IAiF7B,OAAO,CAAC,0BAA0B;IA0DlC,IAAI,WAAW,IAAI,OAAO,GAAG,SAAS,CAErC;IACD,IAAI,YAAY,IAAI,MAAM,CAEzB;IACD,IAAI,KAAK,IAAI,OAAO,CAEnB;CACJ"}
|
|
@@ -2,7 +2,7 @@ import { sql } from 'drizzle-orm';
|
|
|
2
2
|
import { db } from '../../db/client.js';
|
|
3
3
|
import { SectionFactory } from './SectionFactory.js';
|
|
4
4
|
import { SimpleSection, HasItemsSection, CategorySection } from '../sections/index.js';
|
|
5
|
-
import { checkboxField, SelectField, SelectMultipleField, TagsField } from '../fields/index.js';
|
|
5
|
+
import { checkboxField, DateRangeField, SelectField, SelectMultipleField, TagsField } from '../fields/index.js';
|
|
6
6
|
import { is } from '../helpers/index.js';
|
|
7
7
|
import { cloneDeep } from 'lodash-es';
|
|
8
8
|
import chalk from 'chalk';
|
|
@@ -256,6 +256,10 @@ export class FieldFactory {
|
|
|
256
256
|
// Build the field to initialize it (e.g., fetch select options from DB)
|
|
257
257
|
await field.build();
|
|
258
258
|
if (this.type === 'edit') {
|
|
259
|
+
if (is(field, DateRangeField)) {
|
|
260
|
+
field.setRangeValues(this._values[field.startName], this._values[field.endName]);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
259
263
|
/**
|
|
260
264
|
* If it's an edit operation, get the value from the database and set it
|
|
261
265
|
*/
|
|
@@ -456,15 +456,15 @@ export class SectionFactory {
|
|
|
456
456
|
importErr.message.includes('Cannot find module') &&
|
|
457
457
|
importErr.message.includes('.section')) {
|
|
458
458
|
this.sectionProcessingErrors[file] ??= [];
|
|
459
|
-
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
460
|
-
|
|
461
|
-
Sections MUST use extensionless relative imports:
|
|
462
|
-
|
|
463
|
-
✅ import exampleSection from './example.section'
|
|
464
|
-
❌ import exampleSection from './example.section.ts'
|
|
465
|
-
❌ import exampleSection from './example.section.js'
|
|
466
|
-
|
|
467
|
-
This file is bundled with esbuild, so Node never resolves the import directly.
|
|
459
|
+
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
460
|
+
|
|
461
|
+
Sections MUST use extensionless relative imports:
|
|
462
|
+
|
|
463
|
+
✅ import exampleSection from './example.section'
|
|
464
|
+
❌ import exampleSection from './example.section.ts'
|
|
465
|
+
❌ import exampleSection from './example.section.js'
|
|
466
|
+
|
|
467
|
+
This file is bundled with esbuild, so Node never resolves the import directly.
|
|
468
468
|
If you added an extension manually, remove it.`);
|
|
469
469
|
this.errorCount++;
|
|
470
470
|
continue;
|
|
@@ -412,15 +412,15 @@ export class SectionFactory {
|
|
|
412
412
|
importErr.message.includes('Cannot find module') &&
|
|
413
413
|
importErr.message.includes('.section')) {
|
|
414
414
|
this.sectionProcessingErrors[file] ??= [];
|
|
415
|
-
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
416
|
-
|
|
417
|
-
Sections MUST use extensionless relative imports:
|
|
418
|
-
|
|
419
|
-
✅ import exampleSection from './example.section'
|
|
420
|
-
❌ import exampleSection from './example.section.ts'
|
|
421
|
-
❌ import exampleSection from './example.section.js'
|
|
422
|
-
|
|
423
|
-
This file is bundled with jiti, so Node never resolves the import directly.
|
|
415
|
+
this.sectionProcessingErrors[file].push(`❌ Invalid section import detected.
|
|
416
|
+
|
|
417
|
+
Sections MUST use extensionless relative imports:
|
|
418
|
+
|
|
419
|
+
✅ import exampleSection from './example.section'
|
|
420
|
+
❌ import exampleSection from './example.section.ts'
|
|
421
|
+
❌ import exampleSection from './example.section.js'
|
|
422
|
+
|
|
423
|
+
This file is bundled with jiti, so Node never resolves the import directly.
|
|
424
424
|
If you added an extension manually, remove it.`);
|
|
425
425
|
this.errorCount++;
|
|
426
426
|
continue;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { BaseFieldConfig, FieldClientConfig } from './field.js';
|
|
2
|
+
import { Field } from './field.js';
|
|
3
|
+
import { entityKind } from '../helpers/index.js';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
import { SerializedDateBoundValue, type DateBoundValue } from '../../utils/date-bounds.js';
|
|
6
|
+
declare const configSchema: z.ZodObject<{
|
|
7
|
+
startName: z.ZodString;
|
|
8
|
+
endName: z.ZodString;
|
|
9
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
10
|
+
date: "date";
|
|
11
|
+
datetime: "datetime";
|
|
12
|
+
}>>;
|
|
13
|
+
minDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
14
|
+
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
15
|
+
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
16
|
+
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
type Config = z.infer<typeof configSchema>;
|
|
19
|
+
/**
|
|
20
|
+
* DateRangeField stores two DB columns: startName and endName.
|
|
21
|
+
*
|
|
22
|
+
* Because Field expects a single `name`, we pass `startName` as `name` to the
|
|
23
|
+
* super constructor (used for label generation / identity only).
|
|
24
|
+
* The field opts out of the normal single-column SQL path via hasSqlNameAndValue()=false
|
|
25
|
+
* and instead exposes setValues() / getSqlNamesAndValues() which submit.ts calls
|
|
26
|
+
* via an is(field, DateRangeField) guard — the same pattern used for SelectField.
|
|
27
|
+
*/
|
|
28
|
+
export declare class DateRangeField extends Field<'date_range', Config> {
|
|
29
|
+
static readonly [entityKind]: string;
|
|
30
|
+
readonly startName: string;
|
|
31
|
+
readonly endName: string;
|
|
32
|
+
readonly format: 'date' | 'datetime';
|
|
33
|
+
readonly minDate: DateBoundValue | undefined;
|
|
34
|
+
readonly maxDate: DateBoundValue | undefined;
|
|
35
|
+
private startValue;
|
|
36
|
+
private endValue;
|
|
37
|
+
constructor(config: Omit<BaseFieldConfig<Config>, 'name'> & Config);
|
|
38
|
+
hasSqlNameAndValue(): boolean;
|
|
39
|
+
setRangeValues(startValue: any, endValue: any): void;
|
|
40
|
+
getSqlNamesAndValues(): Record<string, string | null>;
|
|
41
|
+
getValue(): {
|
|
42
|
+
from: string | null;
|
|
43
|
+
to: string | null;
|
|
44
|
+
};
|
|
45
|
+
exportForClient(): FieldClientConfig & {
|
|
46
|
+
type: 'date_range';
|
|
47
|
+
startName: string;
|
|
48
|
+
endName: string;
|
|
49
|
+
format: 'date' | 'datetime';
|
|
50
|
+
minDate?: SerializedDateBoundValue | undefined;
|
|
51
|
+
maxDate?: SerializedDateBoundValue | undefined;
|
|
52
|
+
startValue?: string | null;
|
|
53
|
+
endValue?: string | null;
|
|
54
|
+
};
|
|
55
|
+
checkRequired(): void;
|
|
56
|
+
prepareForSubmission(): Promise<void>;
|
|
57
|
+
private parseDate;
|
|
58
|
+
private formatDate;
|
|
59
|
+
private validateDateBounds;
|
|
60
|
+
}
|
|
61
|
+
export type DateRangeFieldClientConfig = ReturnType<DateRangeField['exportForClient']>;
|
|
62
|
+
declare const optionsSchema: z.ZodObject<{
|
|
63
|
+
startName: z.ZodString;
|
|
64
|
+
endName: z.ZodString;
|
|
65
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
66
|
+
date: "date";
|
|
67
|
+
datetime: "datetime";
|
|
68
|
+
}>>;
|
|
69
|
+
minDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
70
|
+
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
71
|
+
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
72
|
+
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
73
|
+
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
74
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
75
|
+
defaultValue: z.ZodOptional<z.ZodAny>;
|
|
76
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
77
|
+
conditionalRules: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").ConditionalRule, import("../types/index.js").ConditionalRule>>>;
|
|
78
|
+
adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
|
|
79
|
+
localized: z.ZodOptional<z.ZodBoolean>;
|
|
80
|
+
}, z.core.$strict>;
|
|
81
|
+
declare const dateRangeFieldConfigSchema: z.ZodObject<{
|
|
82
|
+
/**
|
|
83
|
+
* Auto-computed as `${startName}|${endName}`.
|
|
84
|
+
* Satisfies the `FieldConfig` union constraint that every config has a `name` property.
|
|
85
|
+
* Never set manually — use `startName` and `endName` instead.
|
|
86
|
+
*/
|
|
87
|
+
name: z.ZodString;
|
|
88
|
+
type: z.ZodLiteral<"date_range">;
|
|
89
|
+
build: z.ZodFunction<z.core.$ZodFunctionArgs, z.ZodCustom<DateRangeField, DateRangeField>>;
|
|
90
|
+
startName: z.ZodString;
|
|
91
|
+
endName: z.ZodString;
|
|
92
|
+
format: z.ZodOptional<z.ZodEnum<{
|
|
93
|
+
date: "date";
|
|
94
|
+
datetime: "datetime";
|
|
95
|
+
}>>;
|
|
96
|
+
minDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
97
|
+
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
98
|
+
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
99
|
+
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
100
|
+
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
101
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
102
|
+
defaultValue: z.ZodOptional<z.ZodAny>;
|
|
103
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
104
|
+
conditionalRules: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").ConditionalRule, import("../types/index.js").ConditionalRule>>>;
|
|
105
|
+
adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
|
|
106
|
+
localized: z.ZodOptional<z.ZodBoolean>;
|
|
107
|
+
}, z.core.$strict>;
|
|
108
|
+
export type DateRangeFieldConfig = z.infer<typeof dateRangeFieldConfigSchema>;
|
|
109
|
+
/**
|
|
110
|
+
* Helper to create a date range field configuration.
|
|
111
|
+
* Uses `startName` and `endName` instead of the usual `name`.
|
|
112
|
+
* The `name` property is auto-computed as `${startName}|${endName}`.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* dateRangeField({ startName: 'event_start', endName: 'event_end', label: 'Event Dates' })
|
|
116
|
+
*/
|
|
117
|
+
export declare function dateRangeField(field: z.infer<typeof optionsSchema>): DateRangeFieldConfig;
|
|
118
|
+
export {};
|
|
119
|
+
//# sourceMappingURL=date-range.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range.d.ts","sourceRoot":"","sources":["../../../src/core/fields/date-range.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,KAAK,EAAyB,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAKH,wBAAwB,EACxB,KAAK,cAAc,EACtB,MAAM,4BAA4B,CAAA;AAEnC,QAAA,MAAM,YAAY;;;;;;;;;;;kBAQhB,CAAA;AAEF,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAG1C;;;;;;;;GAQG;AACH,qBAAa,cAAe,SAAQ,KAAK,CAAC,YAAY,EAAE,MAAM,CAAC;IAC3D,gBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAmB;IAEhE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;IACpC,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAA;IAE5C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,QAAQ,CAAqB;gBAEzB,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM;IAoBzD,kBAAkB,IAAI,OAAO;IAItC,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI;IAOpD,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IASrD,QAAQ,IAAI;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IAO7C,eAAe,IAAI,iBAAiB,GAAG;QAC5C,IAAI,EAAE,YAAY,CAAA;QAClB,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;QAC3B,OAAO,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAA;QAC9C,OAAO,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAA;QAC9C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B;IAaD,aAAa,IAAI,IAAI;IAOf,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3C,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,kBAAkB;CAqB7B;AAED,MAAM,MAAM,0BAA0B,GAAG,UAAU,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAItF,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;kBAGjB,CAAA;AAEF,QAAA,MAAM,0BAA0B;IAE5B;;;;OAIG;;;;;;;;;;;;;;;;;;;;;kBAOL,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAA;AAE7E;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAAG,oBAAoB,CAczF"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Field, baseFieldConfigSchema } from './field.js';
|
|
2
|
+
import { entityKind } from '../helpers/index.js';
|
|
3
|
+
import * as z from 'zod';
|
|
4
|
+
import getString from '../../translations/index.js';
|
|
5
|
+
import { dateBoundSchema, formatDateForField, resolveDateBoundValue, serializeDateBoundValue, } from '../../utils/date-bounds.js';
|
|
6
|
+
const configSchema = z.strictObject({
|
|
7
|
+
startName: z.string().describe('The DB column name for the start date'),
|
|
8
|
+
endName: z.string().describe('The DB column name for the end date'),
|
|
9
|
+
format: z.enum(['date', 'datetime']).optional().describe('The format of the date columns'),
|
|
10
|
+
minDate: dateBoundSchema.optional().describe('Minimum allowed date. Accepts a Date or "now"'),
|
|
11
|
+
maxDate: dateBoundSchema.optional().describe('Maximum allowed date. Accepts a Date or "now"'),
|
|
12
|
+
defaultStartValue: z.date().optional().describe('Default value for the start date'),
|
|
13
|
+
defaultEndValue: z.date().optional().describe('Default value for the end date'),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* DateRangeField stores two DB columns: startName and endName.
|
|
17
|
+
*
|
|
18
|
+
* Because Field expects a single `name`, we pass `startName` as `name` to the
|
|
19
|
+
* super constructor (used for label generation / identity only).
|
|
20
|
+
* The field opts out of the normal single-column SQL path via hasSqlNameAndValue()=false
|
|
21
|
+
* and instead exposes setValues() / getSqlNamesAndValues() which submit.ts calls
|
|
22
|
+
* via an is(field, DateRangeField) guard — the same pattern used for SelectField.
|
|
23
|
+
*/
|
|
24
|
+
export class DateRangeField extends Field {
|
|
25
|
+
static [entityKind] = 'DateRangeField';
|
|
26
|
+
startName;
|
|
27
|
+
endName;
|
|
28
|
+
format;
|
|
29
|
+
minDate;
|
|
30
|
+
maxDate;
|
|
31
|
+
startValue;
|
|
32
|
+
endValue;
|
|
33
|
+
constructor(config) {
|
|
34
|
+
// Pass startName as `name` so the base class label-generation / identity works
|
|
35
|
+
super({ ...config, name: config.startName }, 'date_range');
|
|
36
|
+
this.startName = config.startName;
|
|
37
|
+
this.endName = config.endName;
|
|
38
|
+
this.format = config.format ?? 'date';
|
|
39
|
+
this.minDate = config.minDate;
|
|
40
|
+
this.maxDate = config.maxDate;
|
|
41
|
+
this.startValue = config.defaultStartValue ?? undefined;
|
|
42
|
+
this.endValue = config.defaultEndValue ?? undefined;
|
|
43
|
+
const minDate = resolveDateBoundValue(this.minDate);
|
|
44
|
+
const maxDate = resolveDateBoundValue(this.maxDate);
|
|
45
|
+
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
|
|
46
|
+
throw new Error(getString('fieldDateBoundsMismatch', this.language, { field: this.getLocalizedLabel() }));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ─── Opt out of single-column SQL path ────────────────────────────────────
|
|
50
|
+
hasSqlNameAndValue() {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
setRangeValues(startValue, endValue) {
|
|
54
|
+
this.startValue = startValue;
|
|
55
|
+
this.endValue = endValue;
|
|
56
|
+
}
|
|
57
|
+
// ─── Dual-column SQL output (called by submit.ts) ─────────────────────────
|
|
58
|
+
getSqlNamesAndValues() {
|
|
59
|
+
return {
|
|
60
|
+
[this.startName]: this.formatDate(this.startValue),
|
|
61
|
+
[this.endName]: this.formatDate(this.endValue),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ─── Standard Field interface ─────────────────────────────────────────────
|
|
65
|
+
getValue() {
|
|
66
|
+
return {
|
|
67
|
+
from: this.formatDate(this.startValue),
|
|
68
|
+
to: this.formatDate(this.endValue),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
exportForClient() {
|
|
72
|
+
return {
|
|
73
|
+
...super.exportForClient(),
|
|
74
|
+
startName: this.startName,
|
|
75
|
+
endName: this.endName,
|
|
76
|
+
format: this.format,
|
|
77
|
+
minDate: serializeDateBoundValue(this.minDate),
|
|
78
|
+
maxDate: serializeDateBoundValue(this.maxDate),
|
|
79
|
+
startValue: this.formatDate(this.startValue),
|
|
80
|
+
endValue: this.formatDate(this.endValue),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
checkRequired() {
|
|
84
|
+
if (!this.required)
|
|
85
|
+
return;
|
|
86
|
+
if (!this.parseDate(this.startValue) || !this.parseDate(this.endValue)) {
|
|
87
|
+
throw new Error(getString('fieldIsRequired', this.language, { field: this.getLocalizedLabel() }));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async prepareForSubmission() {
|
|
91
|
+
const start = this.parseDate(this.startValue);
|
|
92
|
+
const end = this.parseDate(this.endValue);
|
|
93
|
+
if (this.required && (!start || !end)) {
|
|
94
|
+
throw new Error(getString('invalidDatePleaseProvideValid', this.language, { field: this.getLocalizedLabel() }));
|
|
95
|
+
}
|
|
96
|
+
if (start) {
|
|
97
|
+
this.validateDateBounds(start);
|
|
98
|
+
this.startValue = start.toISOString();
|
|
99
|
+
}
|
|
100
|
+
if (end) {
|
|
101
|
+
this.validateDateBounds(end);
|
|
102
|
+
this.endValue = end.toISOString();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
106
|
+
parseDate(value) {
|
|
107
|
+
if (value === undefined || value === null || value === '')
|
|
108
|
+
return null;
|
|
109
|
+
const date = new Date(value);
|
|
110
|
+
return isNaN(date.getTime()) ? null : date;
|
|
111
|
+
}
|
|
112
|
+
formatDate(value) {
|
|
113
|
+
const date = this.parseDate(value);
|
|
114
|
+
if (!date)
|
|
115
|
+
return null;
|
|
116
|
+
return formatDateForField(date, this.format);
|
|
117
|
+
}
|
|
118
|
+
validateDateBounds(date) {
|
|
119
|
+
const minDate = resolveDateBoundValue(this.minDate);
|
|
120
|
+
if (minDate && date.getTime() < minDate.getTime()) {
|
|
121
|
+
throw new Error(getString('fieldDateMinValueError', this.language, {
|
|
122
|
+
field: this.getLocalizedLabel(),
|
|
123
|
+
min: formatDateForField(minDate, this.format),
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
const maxDate = resolveDateBoundValue(this.maxDate);
|
|
127
|
+
if (maxDate && date.getTime() > maxDate.getTime()) {
|
|
128
|
+
throw new Error(getString('fieldDateMaxValueError', this.language, {
|
|
129
|
+
field: this.getLocalizedLabel(),
|
|
130
|
+
max: formatDateForField(maxDate, this.format),
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ─── Config schema & factory ──────────────────────────────────────────────────
|
|
136
|
+
const optionsSchema = z.strictObject({
|
|
137
|
+
...baseFieldConfigSchema.omit({ name: true }).shape,
|
|
138
|
+
...configSchema.shape,
|
|
139
|
+
});
|
|
140
|
+
const dateRangeFieldConfigSchema = z.strictObject({
|
|
141
|
+
...optionsSchema.shape,
|
|
142
|
+
/**
|
|
143
|
+
* Auto-computed as `${startName}|${endName}`.
|
|
144
|
+
* Satisfies the `FieldConfig` union constraint that every config has a `name` property.
|
|
145
|
+
* Never set manually — use `startName` and `endName` instead.
|
|
146
|
+
*/
|
|
147
|
+
name: z.string(),
|
|
148
|
+
type: z.literal('date_range').describe('The type of the field'),
|
|
149
|
+
build: z
|
|
150
|
+
.function()
|
|
151
|
+
.output(z.instanceof(DateRangeField))
|
|
152
|
+
.describe('Build a DateRangeField instance from this config'),
|
|
153
|
+
});
|
|
154
|
+
/**
|
|
155
|
+
* Helper to create a date range field configuration.
|
|
156
|
+
* Uses `startName` and `endName` instead of the usual `name`.
|
|
157
|
+
* The `name` property is auto-computed as `${startName}|${endName}`.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* dateRangeField({ startName: 'event_start', endName: 'event_end', label: 'Event Dates' })
|
|
161
|
+
*/
|
|
162
|
+
export function dateRangeField(field) {
|
|
163
|
+
const result = optionsSchema.safeParse(field);
|
|
164
|
+
if (!result.success) {
|
|
165
|
+
throw new Error(`[DateRangeField: ${field.startName}/${field.endName}]: ${z.prettifyError(result.error)}`);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
...field,
|
|
169
|
+
name: `${field.startName}|${field.endName}`,
|
|
170
|
+
type: 'date_range',
|
|
171
|
+
build() {
|
|
172
|
+
return new DateRangeField(field);
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -2,18 +2,21 @@ import type { BaseFieldConfig } from './field.js';
|
|
|
2
2
|
import { Field } from './field.js';
|
|
3
3
|
import { entityKind } from '../helpers/index.js';
|
|
4
4
|
import * as z from 'zod';
|
|
5
|
+
import { type DateBoundValue } from '../../utils/date-bounds.js';
|
|
5
6
|
declare const configSchema: z.ZodObject<{
|
|
6
7
|
format: z.ZodOptional<z.ZodEnum<{
|
|
7
8
|
date: "date";
|
|
8
9
|
datetime: "datetime";
|
|
9
10
|
timestamp: "timestamp";
|
|
10
11
|
}>>;
|
|
12
|
+
minDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
13
|
+
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
11
14
|
/**
|
|
12
15
|
* The default value of the field.
|
|
13
16
|
* If set, the field will be pre-populated with this value when the form is loaded.
|
|
14
17
|
* If `adminGenerated` is not true, the field will be included when submitting the form with this value.
|
|
15
18
|
*/
|
|
16
|
-
defaultValue: z.ZodOptional<z.
|
|
19
|
+
defaultValue: z.ZodOptional<z.ZodDate>;
|
|
17
20
|
}, z.core.$strict>;
|
|
18
21
|
type Config = z.infer<typeof configSchema>;
|
|
19
22
|
export declare class DateField extends Field<'date', Config> {
|
|
@@ -24,6 +27,8 @@ export declare class DateField extends Field<'date', Config> {
|
|
|
24
27
|
* 2- time format should be added as well.
|
|
25
28
|
*/
|
|
26
29
|
readonly format: 'date' | 'datetime' | 'timestamp';
|
|
30
|
+
readonly minDate: DateBoundValue | undefined;
|
|
31
|
+
readonly maxDate: DateBoundValue | undefined;
|
|
27
32
|
private readonly _defaultValue;
|
|
28
33
|
constructor(config: BaseFieldConfig<Config>);
|
|
29
34
|
/**
|
|
@@ -38,6 +43,8 @@ export declare class DateField extends Field<'date', Config> {
|
|
|
38
43
|
getValue(): string | null;
|
|
39
44
|
exportForClient(): {
|
|
40
45
|
format: "date" | "datetime" | "timestamp";
|
|
46
|
+
minDate: string | undefined;
|
|
47
|
+
maxDate: string | undefined;
|
|
41
48
|
type: "date";
|
|
42
49
|
name: string;
|
|
43
50
|
label: string;
|
|
@@ -55,6 +62,7 @@ export declare class DateField extends Field<'date', Config> {
|
|
|
55
62
|
* Normalizes the value to ISO string format for storage
|
|
56
63
|
*/
|
|
57
64
|
prepareForSubmission(): Promise<void>;
|
|
65
|
+
private validateDateBounds;
|
|
58
66
|
}
|
|
59
67
|
export type DateFieldClientConfig = ReturnType<DateField['exportForClient']>;
|
|
60
68
|
declare const optionsSchema: z.ZodObject<{
|
|
@@ -63,12 +71,14 @@ declare const optionsSchema: z.ZodObject<{
|
|
|
63
71
|
datetime: "datetime";
|
|
64
72
|
timestamp: "timestamp";
|
|
65
73
|
}>>;
|
|
74
|
+
minDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
75
|
+
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
66
76
|
/**
|
|
67
77
|
* The default value of the field.
|
|
68
78
|
* If set, the field will be pre-populated with this value when the form is loaded.
|
|
69
79
|
* If `adminGenerated` is not true, the field will be included when submitting the form with this value.
|
|
70
80
|
*/
|
|
71
|
-
defaultValue: z.ZodOptional<z.
|
|
81
|
+
defaultValue: z.ZodOptional<z.ZodDate>;
|
|
72
82
|
name: z.ZodString;
|
|
73
83
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
74
84
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -85,12 +95,14 @@ declare const dateFieldConfigSchema: z.ZodObject<{
|
|
|
85
95
|
datetime: "datetime";
|
|
86
96
|
timestamp: "timestamp";
|
|
87
97
|
}>>;
|
|
98
|
+
minDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
99
|
+
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
88
100
|
/**
|
|
89
101
|
* The default value of the field.
|
|
90
102
|
* If set, the field will be pre-populated with this value when the form is loaded.
|
|
91
103
|
* If `adminGenerated` is not true, the field will be included when submitting the form with this value.
|
|
92
104
|
*/
|
|
93
|
-
defaultValue: z.ZodOptional<z.
|
|
105
|
+
defaultValue: z.ZodOptional<z.ZodDate>;
|
|
94
106
|
name: z.ZodString;
|
|
95
107
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
96
108
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../../src/core/fields/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EAAE,KAAK,EAAyB,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../../src/core/fields/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EAAE,KAAK,EAAyB,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,EAKH,KAAK,cAAc,EACtB,MAAM,4BAA4B,CAAA;AAEnC,QAAA,MAAM,YAAY;;;;;;;;IAId;;;;OAIG;;kBAEL,CAAA;AAEF,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAE1C,qBAAa,SAAU,SAAQ,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC;IAChD,gBAAyB,CAAC,UAAU,CAAC,EAAE,MAAM,CAAc;IAE3D;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,CAAA;IAClD,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAA;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;gBACpC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC;IAkB3C;;;OAGG;IACH,OAAO,CAAC,SAAS;IAQjB;;;OAGG;IACH,QAAQ,IAAI,MAAM,GAAG,IAAI;IAShB,eAAe;;;;;;;;;;;;;;IASxB,aAAa;IAYJ,kBAAkB,IAAI,OAAO;IAOtC;;;OAGG;IACG,oBAAoB;IAa1B,OAAO,CAAC,kBAAkB;CAqB7B;AAED,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAE5E,QAAA,MAAM,aAAa;;;;;;;;IAvIf;;;;OAIG;;;;;;;;;kBAsIL,CAAA;AAEF,QAAA,MAAM,qBAAqB;;;;;;;;;;IA5IvB;;;;OAIG;;;;;;;;;kBA4IL,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAEnE;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAAG,eAAe,CAmB/E"}
|
package/dist/core/fields/date.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { Field, baseFieldConfigSchema } from './field.js';
|
|
2
2
|
import { entityKind } from '../helpers/index.js';
|
|
3
3
|
import * as z from 'zod';
|
|
4
|
-
import dayjs from 'dayjs';
|
|
5
4
|
import getString from '../../translations/index.js';
|
|
5
|
+
import { dateBoundSchema, formatDateForField, resolveDateBoundValue, serializeDateBoundValue, } from '../../utils/date-bounds.js';
|
|
6
6
|
const configSchema = z.strictObject({
|
|
7
7
|
format: z.enum(['date', 'datetime', 'timestamp']).optional().describe('The format of the date field'),
|
|
8
|
+
minDate: dateBoundSchema.optional().describe('Minimum allowed date. Accepts a Date or "now"'),
|
|
9
|
+
maxDate: dateBoundSchema.optional().describe('Maximum allowed date. Accepts a Date or "now"'),
|
|
8
10
|
/**
|
|
9
11
|
* The default value of the field.
|
|
10
12
|
* If set, the field will be pre-populated with this value when the form is loaded.
|
|
11
13
|
* If `adminGenerated` is not true, the field will be included when submitting the form with this value.
|
|
12
14
|
*/
|
|
13
|
-
defaultValue: z.
|
|
15
|
+
defaultValue: z.date().optional().describe('The default value of the field'),
|
|
14
16
|
});
|
|
15
17
|
export class DateField extends Field {
|
|
16
18
|
static [entityKind] = 'DateField';
|
|
@@ -20,15 +22,24 @@ export class DateField extends Field {
|
|
|
20
22
|
* 2- time format should be added as well.
|
|
21
23
|
*/
|
|
22
24
|
format;
|
|
25
|
+
minDate;
|
|
26
|
+
maxDate;
|
|
23
27
|
_defaultValue;
|
|
24
28
|
constructor(config) {
|
|
25
29
|
super(config, 'date');
|
|
26
30
|
this.format = config.format || 'date';
|
|
31
|
+
this.minDate = config.minDate;
|
|
32
|
+
this.maxDate = config.maxDate;
|
|
27
33
|
/**
|
|
28
|
-
* Set the default date
|
|
34
|
+
* Set the default date when provided
|
|
29
35
|
*/
|
|
30
36
|
this.value = config.defaultValue ?? undefined;
|
|
31
37
|
this._defaultValue = config.defaultValue;
|
|
38
|
+
const minDate = resolveDateBoundValue(this.minDate);
|
|
39
|
+
const maxDate = resolveDateBoundValue(this.maxDate);
|
|
40
|
+
if (minDate && maxDate && minDate.getTime() > maxDate.getTime()) {
|
|
41
|
+
throw new Error(getString('fieldDateBoundsMismatch', this.language, { field: this.getLocalizedLabel() }));
|
|
42
|
+
}
|
|
32
43
|
}
|
|
33
44
|
/**
|
|
34
45
|
* Parse the raw value into a Date object
|
|
@@ -50,25 +61,14 @@ export class DateField extends Field {
|
|
|
50
61
|
if (!date) {
|
|
51
62
|
return null;
|
|
52
63
|
}
|
|
53
|
-
|
|
54
|
-
case 'timestamp':
|
|
55
|
-
return dayjs(date).format('YYYY-MM-DD HH:mm:ss.SSS');
|
|
56
|
-
case 'datetime':
|
|
57
|
-
/**
|
|
58
|
-
* Return the date in ISO string format
|
|
59
|
-
* Example: 2025-12-18T12:00:00.000Z
|
|
60
|
-
*/
|
|
61
|
-
return dayjs(date).format('YYYY-MM-DD HH:mm:ss');
|
|
62
|
-
case 'date':
|
|
63
|
-
default:
|
|
64
|
-
// Return YYYY-MM-DD format
|
|
65
|
-
return dayjs(date).format('YYYY-MM-DD');
|
|
66
|
-
}
|
|
64
|
+
return formatDateForField(date, this.format);
|
|
67
65
|
}
|
|
68
66
|
exportForClient() {
|
|
69
67
|
return {
|
|
70
68
|
...super.exportForClient(),
|
|
71
69
|
format: this.format,
|
|
70
|
+
minDate: serializeDateBoundValue(this.minDate),
|
|
71
|
+
maxDate: serializeDateBoundValue(this.maxDate),
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
checkRequired() {
|
|
@@ -95,6 +95,7 @@ export class DateField extends Field {
|
|
|
95
95
|
async prepareForSubmission() {
|
|
96
96
|
const date = this.parseDate();
|
|
97
97
|
if (date) {
|
|
98
|
+
this.validateDateBounds(date);
|
|
98
99
|
this.value = date.toISOString();
|
|
99
100
|
}
|
|
100
101
|
else if (this.required) {
|
|
@@ -102,6 +103,22 @@ export class DateField extends Field {
|
|
|
102
103
|
}
|
|
103
104
|
// If not required and no valid date, keep value as-is (null/undefined)
|
|
104
105
|
}
|
|
106
|
+
validateDateBounds(date) {
|
|
107
|
+
const minDate = resolveDateBoundValue(this.minDate);
|
|
108
|
+
if (minDate && date.getTime() < minDate.getTime()) {
|
|
109
|
+
throw new Error(getString('fieldDateMinValueError', this.language, {
|
|
110
|
+
field: this.getLocalizedLabel(),
|
|
111
|
+
min: formatDateForField(minDate, this.format),
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
const maxDate = resolveDateBoundValue(this.maxDate);
|
|
115
|
+
if (maxDate && date.getTime() > maxDate.getTime()) {
|
|
116
|
+
throw new Error(getString('fieldDateMaxValueError', this.language, {
|
|
117
|
+
field: this.getLocalizedLabel(),
|
|
118
|
+
max: formatDateForField(maxDate, this.format),
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
105
122
|
}
|
|
106
123
|
const optionsSchema = z.strictObject({
|
|
107
124
|
...baseFieldConfigSchema.shape,
|