Skip to content

Commit

Permalink
Merge pull request #2113 from undb-io/feature/formula
Browse files Browse the repository at this point in the history
feat: add basic formula field
  • Loading branch information
nichenqin authored Oct 24, 2024
2 parents 1d4f168 + fb2d72d commit eb72be2
Show file tree
Hide file tree
Showing 38 changed files with 431 additions and 33 deletions.
1 change: 1 addition & 0 deletions apps/frontend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum FieldType {
date
duration
email
formula
id
json
longText
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import CheckboxControl from "./checkbox-control.svelte"
import UrlControl from "./url-control.svelte"
import RollupField from "../field-value/rollup-field.svelte"
import FormulaField from "../field-value/formula-field.svelte"
import LongTextControl from "./long-text-control.svelte"
import CurrencyControl from "./currency-control.svelte"
import ButtonControl from "./button-control.svelte"
Expand Down Expand Up @@ -55,6 +56,7 @@
date: DateControl,
json: JsonControl,
checkbox: CheckboxControl,
formula: FormulaField,
user: UserControl,
duration: DurationControl,
percentage: PercentageControl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
MousePointerClickIcon,
TimerIcon,
PercentIcon,
SquareFunctionIcon,
} from "lucide-svelte"
export let type: FieldType
Expand Down Expand Up @@ -58,6 +59,7 @@
button: MousePointerClickIcon,
duration: TimerIcon,
percentage: PercentIcon,
formula: SquareFunctionIcon,
}
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import DurationField from "./duration-field.svelte"
import PercentageField from "./percentage-field.svelte"
import ButtonControl from "../field-control/button-control.svelte"
import FormulaField from "./formula-field.svelte"
export let type: FieldType
export let value: any
Expand Down Expand Up @@ -55,6 +56,7 @@
user: UserField,
duration: DurationField,
percentage: PercentageField,
formula: FormulaField,
button: ButtonControl,
}
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import { cn } from "$lib/utils"
export let value: string | undefined = undefined
export let placeholder: string | undefined = undefined
$: v = value || placeholder || ""
</script>

{#if v}
<div class={cn("truncate text-left text-sm", $$restProps.class)}>
{v}
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
import { trpc } from "$lib/trpc/client"
import { cn } from "$lib/utils"
import { createMutation } from "@tanstack/svelte-query"
import type { FormulaField } from "@undb/table"
import { toast } from "svelte-sonner"
import { gridViewStore } from "../grid-view.store"
export let value: string
</script>

<div class={cn("truncate", $$restProps.class)}>
{#if value}
{value}
{/if}
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import ButtonCell from "./editable-cell/button-cell.svelte"
import DurationCell from "./editable-cell/duration-cell.svelte"
import PercentageCell from "./editable-cell/percentage-cell.svelte"
import FormulaCell from "./editable-cell/formula-cell.svelte"
import type { Readable } from "svelte/store"
const table = getTable()
Expand Down Expand Up @@ -69,6 +70,7 @@
user: UserCell,
duration: DurationCell,
percentage: PercentageCell,
formula: FormulaCell,
}
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@
{@const dirty = $tainted && $tainted[field.id.value]}
<Form.Field class="flex gap-2 space-y-0" {form} name={field.id.value}>
<Form.Control let:attrs>
<Form.Label class="text-muted-foreground flex h-4 w-48 shrink-0 items-center justify-between gap-2 pt-4">
<Form.Label
class="text-muted-foreground flex h-full w-48 shrink-0 items-center justify-between gap-2 truncate pt-2"
>
<div class="flex items-center gap-2">
<FieldIcon {field} type={field.type} class="h-4 w-4" />
<span class="flex-1 truncate">{field.name.value}</span>
Expand All @@ -141,7 +143,7 @@
value={values[field.id.value]}
type={field.type}
displayValue={displayValues[field.id.value]}
class="flex min-h-9 items-center text-xs"
class="flex min-h-9 items-center truncate text-xs"
readonly
/>
{:else}
Expand Down
1 change: 1 addition & 0 deletions packages/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export class Graphql {
user
duration
percentage
formula
}
type Field {
Expand Down
1 change: 1 addition & 0 deletions packages/i18n/src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const fieldTypes: Record<FieldType, string> = {
duration: "Duration",
button: "Button",
percentage: "Percentage",
formula: "Formula",
}

const rollupFns: Record<IRollupFn, string> = {
Expand Down
8 changes: 8 additions & 0 deletions packages/i18n/src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ type RootTranslation = {
* P​e​r​c​e​n​t​a​g​e
*/
percentage: string
/**
* F​o​r​m​u​l​a
*/
formula: string
}
rollupFns: {
/**
Expand Down Expand Up @@ -666,6 +670,10 @@ export type TranslationFunctions = {
* Percentage
*/
percentage: () => LocalizedString
/**
* Formula
*/
formula: () => LocalizedString
}
rollupFns: {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
type UrlField,
type UserField,
} from "@undb/table"
import type { FormulaField } from "@undb/table/src/modules/schema/fields/variants/formula-field"
import { getTableName } from "drizzle-orm"
import { sql, type QueryCreator, type SelectExpression } from "kysely"
import type { IRecordQueryBuilder } from "../qb"
Expand Down Expand Up @@ -86,6 +87,7 @@ export class RecordQueryCreatorVisitor implements IFieldVisitor {
button(field: ButtonField): void {}
duration(field: DurationField): void {}
percentage(field: PercentageField): void {}
formula(field: FormulaField): void {}
user(field: UserField): void {
if (field.isMultiple) {
const usersTable = getTableName(users)
Expand Down
4 changes: 4 additions & 0 deletions packages/persistence/src/record/record-reference-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
type UrlField,
type UserField,
} from "@undb/table"
import type { FormulaField } from "@undb/table/src/modules/schema/fields/variants/formula-field"
import type { SelectQueryBuilder } from "kysely"

export class RecordReferenceVisitor implements IFieldVisitor {
Expand Down Expand Up @@ -89,6 +90,9 @@ export class RecordReferenceVisitor implements IFieldVisitor {
reference(field: ReferenceField): void {
this.qb = this.qb.leftJoin(field.id.value, `${this.table.id.value}.${ID_TYPE}`, `${field.id.value}.${ID_TYPE}`)
}
formula(field: FormulaField): void {
throw new Error("Method not implemented.")
}
percentage(field: PercentageField): void {
throw new Error("Method not implemented.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
type UrlField,
type UserField,
} from "@undb/table"
import type { FormulaField } from "@undb/table/src/modules/schema/fields/variants/formula-field"
import { getTableName } from "drizzle-orm"
import { sql, type ExpressionBuilder, type SelectExpression } from "kysely"
import { users } from "../tables"
Expand Down Expand Up @@ -167,6 +168,9 @@ export class RecordSelectFieldVisitor implements IFieldVisitor {
const select = `${field.referenceFieldId}.${field.id.value} as ${field.id.value}`
this.addSelect(select)
}
formula(field: FormulaField): void {
this.addSelect(this.getField(field.id.value))
}
attachment(field: AttachmentField): void {
this.addSelect(this.getField(field.id.value))
}
Expand Down
28 changes: 14 additions & 14 deletions packages/persistence/src/template/template-data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { env } from "@undb/env"
import { templates,type IBaseTemplateDTO,type ITemplateDTO } from "@undb/template"
import { templates, type IBaseTemplateDTO, type ITemplateDTO } from "@undb/template"

function getTemplateImage(folder: string, file: string) {
return env.UNDB_BASE_URL + "/assets/templates/" + folder + "/" + file
Expand Down Expand Up @@ -389,16 +389,16 @@ export const templateData: ITemplateDTO[] = [
},
]

// if (env.NODE_ENV === "development") {
// templateData.unshift({
// id: "test",
// icon: "💼",
// name: "Test",
// categories: ["sales"],
// description: "A template for testing",
// template: {
// type: "base",
// template: templates.test as IBaseTemplateDTO,
// },
// })
// }
if (env.NODE_ENV === "development") {
templateData.unshift({
id: "test",
icon: "💼",
name: "Test",
categories: ["sales"],
description: "A template for testing",
template: {
type: "base",
template: templates.test as IBaseTemplateDTO,
},
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
type UrlField,
type UserField,
} from "@undb/table"
import type { FormulaField } from "@undb/table/src/modules/schema/fields/variants/formula-field"
import { sql } from "kysely"
import { AbstractQBMutationVisitor } from "../abstract-qb.visitor"
import type { IRecordQueryBuilder } from "../qb"
Expand All @@ -50,6 +51,7 @@ export class UnderlyingTableFieldUpdatedVisitor extends AbstractQBMutationVisito
string(field: StringField): void {}
number(field: NumberField): void {}
rating(field: RatingField): void {}
formula(field: FormulaField): void {}
select(field: SelectField): void {
const prev = this.prev as SelectField
const deletedOptions = Options.getDeletedOptions(prev.options, field.options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CreatedByField,
DateField,
DurationField,
FormulaField,
ID_TYPE,
JsonField,
LongTextField,
Expand Down Expand Up @@ -180,4 +181,12 @@ export class UnderlyingTableFieldVisitor<TB extends CreateTableBuilder<any, any>
this.addColumn(c)
}
}
formula(field: FormulaField): void {
const parse = (fn: string): string => {
return fn.replaceAll("{{", "").replaceAll("}}", "")
}
const exp = parse(field.fn)
const c = this.tb.addColumn(field.id.value, "text", (b) => b.generatedAlwaysAs(sql.raw(exp)).stored())
this.addColumn(c)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { ICreatedAtFieldValueVisitor } from "../../schema/fields/variants/c
import type { ICurrencyFieldValueVisitor } from "../../schema/fields/variants/currency-field"
import type { IDurationFieldValueVisitor } from "../../schema/fields/variants/duration-field/duration-field-value.visitor"
import type { IEmailFieldValueVisitor } from "../../schema/fields/variants/email-field"
import type { IFormulaFieldValueVisitor } from "../../schema/fields/variants/formula-field/formula-field-value.visitor"
import type { IIdFieldValueVisitor } from "../../schema/fields/variants/id-field"
import type { IRatingFieldValueVisitor } from "../../schema/fields/variants/rating-field"
import type { ISelectFieldValueVisitor } from "../../schema/fields/variants/select-field"
Expand All @@ -42,4 +43,5 @@ export type IRecordVisitor = IStringFieldValueVisitor &
ICurrencyFieldValueVisitor &
IDurationFieldValueVisitor &
IPercentageFieldValueVisitor &
IFormulaFieldValueVisitor &
ISpecVisitor
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { createCurrencyFieldDTO, createTablesCurrencyFieldDTO } from "../variant
import { createDateFieldDTO, createTablesDateFieldDTO } from "../variants/date-field/date-field.vo"
import { createDurationFieldDTO } from "../variants/duration-field/duration-field.vo"
import { createEmailFieldDTO, createTablesEmailFieldDTO } from "../variants/email-field/email-field.vo"
import { createFormulaFieldDTO, createTablesFormulaFieldDTO } from "../variants/formula-field"
import { createJsonFieldDTO, createTablesJsonFieldDTO } from "../variants/json-field/json-field.vo"
import { createLongTextFieldDTO, createTablesLongTextFieldDTO } from "../variants/long-text-field/long-text-field.vo"
import { createNumberFieldDTO, createTablesNumberFieldDTO } from "../variants/number-field/number-field.vo"
Expand Down Expand Up @@ -43,6 +44,7 @@ export const createFieldDTO = z.discriminatedUnion("type", [
createButtonFieldDTO,
createDurationFieldDTO,
createPercentageFieldDTO,
createFormulaFieldDTO,
])

export const createTablesFieldDTO = z.discriminatedUnion("type", [
Expand All @@ -63,6 +65,7 @@ export const createTablesFieldDTO = z.discriminatedUnion("type", [
createTablesLongTextFieldDTO,
createTablesUserFieldDTO,
createTablesPercentageFieldDTO,
createTablesFormulaFieldDTO,
])

export type ICreateFieldDTO = z.infer<typeof createFieldDTO>
2 changes: 2 additions & 0 deletions packages/table/src/modules/schema/fields/dto/field.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createdAtFieldDTO } from "../variants/created-at-field"
import { createdByFieldDTO } from "../variants/created-by-field"
import { createCurrencyFieldDTO, currencyFieldDTO } from "../variants/currency-field"
import { createEmailFieldDTO, emailFieldDTO } from "../variants/email-field"
import { formulaFieldDTO } from "../variants/formula-field/formula-field.vo"
import { idFieldDTO } from "../variants/id-field/id-field.vo"
import { createLongTextFieldDTO, longTextFieldDTO } from "../variants/long-text-field"
import { createNumberFieldDTO, numberFieldDTO } from "../variants/number-field/number-field.vo"
Expand Down Expand Up @@ -55,6 +56,7 @@ export const fieldDTO = z.discriminatedUnion("type", [
buttonFieldDTO,
durationFieldDTO,
percentageFieldDTO,
formulaFieldDTO,
])

export type IFieldDTO = z.infer<typeof fieldDTO>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { updateCurrencyFieldDTO } from "../variants/currency-field"
import { updateDateFieldDTO } from "../variants/date-field/date-field.vo"
import { updateDurationFieldDTO } from "../variants/duration-field/duration-field.vo"
import { updateEmailFieldDTO } from "../variants/email-field"
import { updateFormulaFieldDTO } from "../variants/formula-field/formula-field.vo"
import { updateIdFieldDTO } from "../variants/id-field/id-field.vo"
import { updateJsonFieldDTO } from "../variants/json-field/json-field.vo"
import { updateNumberFieldDTO } from "../variants/number-field/number-field.vo"
Expand Down Expand Up @@ -45,6 +46,7 @@ export const updateFieldDTO = z.discriminatedUnion("type", [
updateButtonFieldDTO,
updateDurationFieldDTO,
updatePercentageFieldDTO,
updateFormulaFieldDTO,
])

export type IUpdateFieldDTO = z.infer<typeof updateFieldDTO>
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CheckboxFieldValue } from "./variants/checkbox-field"
import { CreatedByFieldValue } from "./variants/created-by-field"
import { CurrencyFieldValue } from "./variants/currency-field"
import { EmailFieldValue } from "./variants/email-field"
import { FormulaFieldValue } from "./variants/formula-field/formula-field-value.vo"
import { LongTextFieldValue } from "./variants/long-text-field/long-text-field-value.vo"
import { PercentageFieldValue } from "./variants/percentage-field" // 新增导入
import { RatingFieldValue } from "./variants/rating-field"
Expand Down Expand Up @@ -68,6 +69,7 @@ export class FieldValueFactory {
.with("updatedBy", () => Some(new UpdatedByFieldValue(value as string)))
.with("reference", () => Some(new ReferenceFieldValue(value as string[])))
.with("rollup", () => Some(new RollupFieldValue(value as number | Date)))
.with("formula", () => Some(new FormulaFieldValue(value as any)))
.with("select", () => Some(new SelectFieldValue(SelectFieldValue.parseValue(value, field as SelectField))))
.with("email", () => Some(new EmailFieldValue(value as string)))
.with("url", () => Some(new UrlFieldValue(value as string)))
Expand Down
2 changes: 2 additions & 0 deletions packages/table/src/modules/schema/fields/field.aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { checkboxFieldAggregate } from "./variants/checkbox-field/checkbox-field
import { currencyFieldAggregate } from "./variants/currency-field/currency-field.aggregate"
import { durationFieldAggregate } from "./variants/duration-field/duration-field.aggregate"
import { emailFieldAggregate } from "./variants/email-field/email-field.aggregate"
import { formulaFieldAggregate } from "./variants/formula-field/formula-field.aggregate"
import { jsonFieldAggregate } from "./variants/json-field/json-field.aggregate"
import { longTextFieldAggregate } from "./variants/long-text-field/long-text-field.aggregate"
import { percentageFieldAggregate } from "./variants/percentage-field/percentage-field.aggregate"
Expand All @@ -30,5 +31,6 @@ export const fieldAggregate = stringFieldAggregate
.or(currencyFieldAggregate)
.or(durationFieldAggregate)
.or(percentageFieldAggregate)
.or(formulaFieldAggregate)

export type IFieldAggregate = z.infer<typeof fieldAggregate>
Loading

0 comments on commit eb72be2

Please sign in to comment.