diff --git a/packages/i18n/src/i18n/i18n-types.ts b/packages/i18n/src/i18n/i18n-types.ts index 23fd026e4..200b33dbe 100644 --- a/packages/i18n/src/i18n/i18n-types.ts +++ b/packages/i18n/src/i18n/i18n-types.ts @@ -1381,10 +1381,6 @@ type RootTranslation = { 'import': string } field: { - /** - * Y​o​u​ ​h​a​v​e​ ​c​h​a​n​g​e​d​ ​t​h​e​ ​f​i​e​l​d​ ​t​y​p​e​,​ ​d​a​t​a​ ​w​i​l​l​ ​b​e​ ​c​a​s​t​ ​t​o​ ​n​e​w​ ​t​y​p​e​ ​w​h​e​n​ ​p​o​s​s​i​b​l​e​,​ ​b​u​t​ ​m​a​y​ ​b​e​ ​c​l​e​a​r​e​d - */ - typeChanged: string /** * F​i​e​l​d */ @@ -1405,10 +1401,6 @@ type RootTranslation = { * U​p​d​a​t​e​ ​F​i​e​l​d */ update: string - /** - * F​i​e​l​d​ ​h​a​s​ ​b​e​e​n​ ​u​p​d​a​t​e​d​! - */ - updated: string /** * D​e​l​e​t​e​ ​F​i​e​l​d */ @@ -1417,14 +1409,6 @@ type RootTranslation = { * A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​f​i​e​l​d​?​ ​A​l​l​ ​d​a​t​a​ ​a​s​s​o​c​i​a​t​e​d​ ​w​i​t​h​ ​t​h​i​s​ ​f​i​e​l​d​ ​w​i​l​l​ ​b​e​ ​d​e​l​e​t​e​ ​p​e​r​m​i​n​e​n​t​l​y​ ​f​r​o​m​ ​t​a​b​l​e​. */ deleteConfirm: string - /** - * F​i​e​l​d​ ​h​a​s​ ​b​e​e​n​ ​d​e​l​e​t​e​d​! - */ - deleted: string - /** - * F​a​i​l​e​d​ ​t​o​ ​d​e​l​e​t​e​ ​f​i​e​l​d - */ - deleteFailed: string /** * D​u​p​l​i​c​a​t​e​ ​F​i​e​l​d */ @@ -3759,10 +3743,6 @@ export type TranslationFunctions = { 'import': () => LocalizedString } field: { - /** - * You have changed the field type, data will be cast to new type when possible, but may be cleared - */ - typeChanged: () => LocalizedString /** * Field */ @@ -3783,10 +3763,6 @@ export type TranslationFunctions = { * Update Field */ update: () => LocalizedString - /** - * Field has been updated! - */ - updated: () => LocalizedString /** * Delete Field */ @@ -3795,14 +3771,6 @@ export type TranslationFunctions = { * Are you sure you want to delete the following field? All data associated with this field will be delete perminently from table. */ deleteConfirm: () => LocalizedString - /** - * Field has been deleted! - */ - deleted: () => LocalizedString - /** - * Failed to delete field - */ - deleteFailed: () => LocalizedString /** * Duplicate Field */ diff --git a/packages/persistence/src/underlying/conversion/conversion.factory.ts b/packages/persistence/src/underlying/conversion/conversion.factory.ts index 7690b8a07..7bc3210aa 100644 --- a/packages/persistence/src/underlying/conversion/conversion.factory.ts +++ b/packages/persistence/src/underlying/conversion/conversion.factory.ts @@ -5,15 +5,19 @@ import type { IRecordQueryBuilder } from "../../qb" import type { UnderlyingConversionStrategy } from "./conversion.interface" import { NoopConversionStrategy } from "./noop.strategy" import { AnyToCurrencyStrategy } from "./strategies/any-to-currency.strategy" +import { AnyToDateRangeStrategy } from "./strategies/any-to-date-range.strategy.ts" import { AnyToEmailStrategy } from "./strategies/any-to-email.strategy" import { AnyToNumberStrategy } from "./strategies/any-to-number.strategy" import { AnyToTextStrategy } from "./strategies/any-to-text.strategy" import { AnyToUrlStrategy } from "./strategies/any-to-url.strategy" import { ClearValueStrategy } from "./strategies/clear-value.strategy" +import { DateToDateRangeStrategy } from "./strategies/date-to-date-range.strategy" import { NumberToBooleanStrategy } from "./strategies/number-to-boolean.strategy" +import { NumberToDateRangeStrategy } from "./strategies/number-to-date-range.strategy.ts" import { NumberToDateStrategy } from "./strategies/number-to-date.strategy" import { SelectToStringStrategy } from "./strategies/select-to-string.strategy" import { StringToBooleanStrategy } from "./strategies/string-to-boolean.strategy" +import { StringToDateRangeStrategy } from "./strategies/string-to-date-range.strategy" import { StringToDateStrategy } from "./strategies/string-to-date.strategy" import { StringToSelectStrategy } from "./strategies/string-to-select.strategy" import { StringToUserStrategy } from "./strategies/string-to-user.strategy" @@ -47,6 +51,18 @@ export class ConversionFactory { .with({ toType: "email" }, () => new AnyToEmailStrategy(tb, qb, table)) .with({ toType: "url" }, () => new AnyToUrlStrategy(tb, qb, table)) + // date-range + .with({ fromType: "date", toType: "dateRange" }, () => new DateToDateRangeStrategy(tb, qb, table)) + .when( + ({ fromType, toType }) => isTextTypeField(fromType) && toType === "dateRange", + () => new StringToDateRangeStrategy(tb, qb, table), + ) + .when( + ({ fromType, toType }) => fromType === "number" && toType === "dateRange", + () => new NumberToDateRangeStrategy(tb, qb, table), + ) + .with({ toType: "dateRange" }, () => new AnyToDateRangeStrategy(tb, qb, table)) + // user .when( ({ fromType, toType }) => isTextTypeField(fromType) && toType === "user", diff --git a/packages/persistence/src/underlying/conversion/conversion.interface.ts b/packages/persistence/src/underlying/conversion/conversion.interface.ts index bad03cefc..ecdb1e457 100644 --- a/packages/persistence/src/underlying/conversion/conversion.interface.ts +++ b/packages/persistence/src/underlying/conversion/conversion.interface.ts @@ -21,8 +21,12 @@ export abstract class UnderlyingConversionStrategy implements IConversionStrateg abstract convert(field: Field, previousField: Field): void | Promise + generateTempFieldId(name: string) { + return TEMP_FIELD_PREFIX + name + } + tempField(field: Field) { - return TEMP_FIELD_PREFIX + field.id.value + return this.generateTempFieldId(field.id.value) } protected changeType(field: Field, type: ColumnDataType, update: () => CompiledQuery) { diff --git a/packages/persistence/src/underlying/conversion/strategies/any-to-date-range.strategy.ts b/packages/persistence/src/underlying/conversion/strategies/any-to-date-range.strategy.ts new file mode 100644 index 000000000..786a32cf0 --- /dev/null +++ b/packages/persistence/src/underlying/conversion/strategies/any-to-date-range.strategy.ts @@ -0,0 +1,20 @@ +import type { Field } from "@undb/table" +import { getDateRangeFieldName } from "../../underlying-table.util" +import { UnderlyingConversionStrategy } from "../conversion.interface" + +export class AnyToDateRangeStrategy extends UnderlyingConversionStrategy { + convert(field: Field, previousField: Field): void | Promise { + if (field.type !== "dateRange" || previousField.type !== "date") { + return + } + const { start, end } = getDateRangeFieldName(field) + + const fieldId = field.id.value + + const addColumns = [this.tb.addColumn(start, "timestamp").compile(), this.tb.addColumn(end, "timestamp").compile()] + + const dropColumns = this.tb.dropColumn(fieldId).compile() + + this.addSql(...addColumns, dropColumns) + } +} diff --git a/packages/persistence/src/underlying/conversion/strategies/date-to-date-range.strategy.ts b/packages/persistence/src/underlying/conversion/strategies/date-to-date-range.strategy.ts new file mode 100644 index 000000000..f525300ed --- /dev/null +++ b/packages/persistence/src/underlying/conversion/strategies/date-to-date-range.strategy.ts @@ -0,0 +1,28 @@ +import type { Field } from "@undb/table" +import { getDateRangeFieldName } from "../../underlying-table.util" +import { UnderlyingConversionStrategy } from "../conversion.interface" + +export class DateToDateRangeStrategy extends UnderlyingConversionStrategy { + convert(field: Field, previousField: Field): void | Promise { + if (field.type !== "dateRange" || previousField.type !== "date") { + return + } + const { start, end } = getDateRangeFieldName(field) + + const fieldId = field.id.value + + const addColumns = [this.tb.addColumn(start, "timestamp").compile(), this.tb.addColumn(end, "timestamp").compile()] + + const updated = this.qb + .updateTable(this.table.id.value) + .set((eb) => ({ + [start]: eb.ref(fieldId), + [end]: eb.ref(fieldId), + })) + .compile() + + const dropColumns = this.tb.dropColumn(fieldId).compile() + + this.addSql(...addColumns, updated, dropColumns) + } +} diff --git a/packages/persistence/src/underlying/conversion/strategies/number-to-date-range.strategy.ts b/packages/persistence/src/underlying/conversion/strategies/number-to-date-range.strategy.ts new file mode 100644 index 000000000..d90f3ed4e --- /dev/null +++ b/packages/persistence/src/underlying/conversion/strategies/number-to-date-range.strategy.ts @@ -0,0 +1,29 @@ +import type { Field } from "@undb/table" +import { sql } from "kysely" +import { getDateRangeFieldName } from "../../underlying-table.util" +import { UnderlyingConversionStrategy } from "../conversion.interface" + +export class NumberToDateRangeStrategy extends UnderlyingConversionStrategy { + convert(field: Field): void | Promise { + if (field.type !== "dateRange") { + return + } + const { start, end } = getDateRangeFieldName(field) + + const fieldId = field.id.value + + const addColumns = [this.tb.addColumn(start, "timestamp").compile(), this.tb.addColumn(end, "timestamp").compile()] + + const updated = this.qb + .updateTable(this.table.id.value) + .set((eb) => ({ + [start]: sql`datetime(${sql.ref(fieldId)}, 'unixepoch')`, + [end]: sql`datetime(${sql.ref(fieldId)}, 'unixepoch')`, + })) + .compile() + + const dropColumns = this.tb.dropColumn(fieldId).compile() + + this.addSql(...addColumns, updated, dropColumns) + } +} diff --git a/packages/persistence/src/underlying/conversion/strategies/string-to-date-range.strategy.ts b/packages/persistence/src/underlying/conversion/strategies/string-to-date-range.strategy.ts new file mode 100644 index 000000000..3522a02be --- /dev/null +++ b/packages/persistence/src/underlying/conversion/strategies/string-to-date-range.strategy.ts @@ -0,0 +1,29 @@ +import type { Field } from "@undb/table" +import { sql } from "kysely" +import { getDateRangeFieldName } from "../../underlying-table.util" +import { UnderlyingConversionStrategy } from "../conversion.interface" + +export class StringToDateRangeStrategy extends UnderlyingConversionStrategy { + convert(field: Field): void | Promise { + if (field.type !== "dateRange") { + return + } + const { start, end } = getDateRangeFieldName(field) + + const fieldId = field.id.value + + const addColumns = [this.tb.addColumn(start, "timestamp").compile(), this.tb.addColumn(end, "timestamp").compile()] + + const updated = this.qb + .updateTable(this.table.id.value) + .set((eb) => ({ + [start]: sql`DATE(${sql.ref(fieldId)})`, + [end]: sql`DATE(${sql.ref(fieldId)})`, + })) + .compile() + + const dropColumns = this.tb.dropColumn(fieldId).compile() + + this.addSql(...addColumns, updated, dropColumns) + } +} diff --git a/packages/persistence/src/underlying/underlying-table.util.ts b/packages/persistence/src/underlying/underlying-table.util.ts index 3686b1ddd..775e43690 100644 --- a/packages/persistence/src/underlying/underlying-table.util.ts +++ b/packages/persistence/src/underlying/underlying-table.util.ts @@ -17,7 +17,7 @@ export const getDateRangeFieldName = (field: DateRangeField) => { return { start: `${field.id.value}_start`, end: `${field.id.value}_end`, - } + } as const } export function getUnderlyingColumnType(type: FieldType): ColumnDataType { diff --git a/packages/template/src/templates/everything.base.json b/packages/template/src/templates/everything.base.json index 0f6d14d4d..0ced2f4b4 100644 --- a/packages/template/src/templates/everything.base.json +++ b/packages/template/src/templates/everything.base.json @@ -60,11 +60,24 @@ "Long Text": "Hello, world!", "Number": 123, "Currency": 123, - "Percentage": 123, + "Percentage": 0.5, "Duration": 123, "Checkbox": true, "Date": "2024-01-01", "Date Range": ["2024-01-01", "2024-01-02"] + }, + { + "id": "basic_field_types_date_range_1", + "String": "2024-01-01", + "Date": "2024-01-01", + "Date Range": ["2024-01-01", "2024-01-02"] + }, + { + "id": "basic_field_types_number_1", + "String": "123", + "Number": 1720384749, + "Date": "2024-01-01", + "Date Range": ["2024-01-01", "2024-01-02"] } ] },