Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release version v1.0.0-48 #1970

Merged
merged 21 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# Changelog


## v1.0.0-48


### 🩹 Fixes

- Update field ux ([9eda222](https://github.com/undb-io/undb/commit/9eda222))

### ❤️ Contributors

- Nichenqin ([@nichenqin](http://github.com/nichenqin))

## v1.0.0-47

## v1.0.0-46
Expand Down
27 changes: 25 additions & 2 deletions apps/backend/src/modules/openapi/record.openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {
CreateRecordsCommand,
DeleteRecordCommand,
DuplicateRecordCommand,
TriggerRecordButtonCommand,
UpdateRecordCommand,
} from "@undb/commands"
import { CommandBus, QueryBus } from "@undb/cqrs"
import { inject, singleton } from "@undb/di"
import { type ICommandBus, type IQueryBus, type PaginatedDTO } from "@undb/domain"
import { Option, type ICommandBus, type IQueryBus, type PaginatedDTO } from "@undb/domain"
import { injectQueryBuilder, type IQueryBuilder } from "@undb/persistence"
import { GetReadableRecordByIdQuery, GetReadableRecordsQuery } from "@undb/queries"
import { type IRecordReadableValueDTO } from "@undb/table"
import { RecordDO, type IRecordReadableValueDTO } from "@undb/table"
import Elysia, { t } from "elysia"
import { withTransaction } from "../../db"

Expand Down Expand Up @@ -226,6 +227,28 @@ export class RecordOpenApi {
},
},
)
.post(
"/records/:recordId/trigger/:field",
async (ctx) => {
const { baseName, tableName, recordId, field } = ctx.params
return withTransaction(this.qb)(async () => {
const result = (await this.commandBus.execute(
new TriggerRecordButtonCommand({ baseName, tableName, recordId, field }),
)) as Option<RecordDO>
return {
mutated: result.isSome(),
}
})
},
{
params: t.Object({ baseName: t.String(), tableName: t.String(), recordId: t.String(), field: t.String() }),
detail: {
tags: ["Record", "Button"],
summary: "Trigger record button",
description: "Trigger record button",
},
},
)
.post(
"/records/duplicate",
async (ctx) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
<script lang="ts">
import FieldPicker from "../field-picker/field-picker.svelte"
import { FieldIdVo, getIsMutableFieldType, type IButtonFieldOption } from "@undb/table"
import {
FieldIdVo,
getIsFilterableFieldType,
getIsMutableFieldType,
type IButtonFieldOption,
type MaybeConditionGroup,
parseValidViewFilter,
toMaybeConditionGroup,
} from "@undb/table"
import { getTable } from "$lib/store/table.store"
import FieldControl from "../field-control/field-control.svelte"
import { Input } from "$lib/components/ui/input"
import { Label } from "$lib/components/ui/label"
import { Checkbox } from "$lib/components/ui/checkbox"
import { Button } from "$lib/components/ui/button"
import Separator from "$lib/components/ui/separator/separator.svelte"
import { writable } from "svelte/store"
import type { ZodUndefined } from "@undb/zod"
import FiltersEditor from "../filters-editor/filters-editor.svelte"
import { onMount } from "svelte"

const table = getTable()

$: visibleFields = $table.getOrderedVisibleFields()

export let disabled: boolean | undefined
export let option: IButtonFieldOption = {
label: undefined,
disabled: undefined,
action: {
type: "update",
values: [
Expand All @@ -25,6 +40,17 @@
confirm: true,
},
}
const value = writable<MaybeConditionGroup<ZodUndefined> | undefined>()
$: validValue = $value ? parseValidViewFilter($table.schema, $value) : undefined
$: if (validValue) {
option.disabled = validValue
}

onMount(() => {
if (option.disabled) {
value.set(toMaybeConditionGroup(option.disabled))
}
})

$: selectedFields = option.action.values.map((v) => v.field)
$: selectableFields = $table.schema.fields.filter(
Expand All @@ -36,39 +62,52 @@
<Label for="label">Label</Label>
<Input class="w-full" placeholder="Button" id="label" bind:value={option.label} />

<p class="text-xs font-semibold">Update Value when Click Button</p>
{#each option.action.values as value, index}
{@const field = value.field ? $table.schema.getFieldById(new FieldIdVo(value.field)).unwrap() : undefined}
<FieldPicker
class="w-full"
bind:value={value.field}
{disabled}
filter={(f) =>
getIsMutableFieldType(f.type) && f.type !== "attachment" && !option.action.values.some((v) => v.field === f.id)}
/>
{#if field}
<FieldControl
class="text-xs"
placeholder="Value to update..."
bind:value={value.value}
{field}
tableId={$table.id.value}
<div class="space-y-2 rounded-sm border pt-2">
<Label class="pl-4 text-xs font-semibold" for="disabled">Disabled When...</Label>
<FiltersEditor
bind:value={$value}
table={$table}
filter={(field) => visibleFields.some((f) => f.id.value === field.id) && getIsFilterableFieldType(field.type)}
></FiltersEditor>
</div>

<div class="space-y-2 rounded-md border px-4 py-3">
<p class="text-xs font-semibold">Update Value when Click Button</p>
{#each option.action.values as value, index}
{@const field = value.field ? $table.schema.getFieldById(new FieldIdVo(value.field)).unwrap() : undefined}
<FieldPicker
class="w-full"
bind:value={value.field}
{disabled}
filter={(f) =>
getIsMutableFieldType(f.type) &&
f.type !== "attachment" &&
!option.action.values.some((v) => v.field === f.id)}
/>
{#if field}
<FieldControl
class="text-xs"
placeholder="Value to update..."
bind:value={value.value}
{field}
tableId={$table.id.value}
/>
{/if}
{#if index !== option.action.values.length - 1}
<Separator />
{/if}
{/each}
{#if selectableFields.length > 0 && option.action.values.every((v) => v.field)}
<Button
class="text-muted-foreground w-full justify-start text-xs"
on:click={() => {
option.action.values = [...option.action.values, { field: undefined, value: undefined }]
}}
variant="link"
size="sm">+ Add another field to update</Button
>
{/if}
{#if index !== option.action.values.length - 1}
<Separator />
{/if}
{/each}
{#if selectableFields.length > 0 && option.action.values.every((v) => v.field)}
<Button
class="text-muted-foreground w-full text-xs"
on:click={() => {
option.action.values = [...option.action.values, { field: undefined, value: undefined }]
}}
variant="link"
size="sm">+ Add another field to update</Button
>
{/if}
</div>

<div class="flex items-center gap-2">
<Checkbox id="confirm" bind:checked={option.action.confirm} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
<script lang="ts">
import { trpc } from "$lib/trpc/client"
import { createMutation } from "@tanstack/svelte-query"
import { ButtonField } from "@undb/table"
import { ButtonField, FieldIdVo, RecordDO } from "@undb/table"
import { toast } from "svelte-sonner"
import { gridViewStore } from "../grid-view.store"
import { Button } from "$lib/components/ui/button"
import { LoaderCircleIcon } from "lucide-svelte"
import { recordsStore } from "$lib/store/records.store"
import { getTable } from "$lib/store/table.store"
import { objectify } from "radash"
import * as AlertDialog from "$lib/components/ui/alert-dialog"
import FieldPicker from "../../field-picker/field-picker.svelte"
import FieldControl from "../../field-control/field-control.svelte"

export let tableId: string
export let field: ButtonField
export let recordId: string
export let record: RecordDO | undefined

const table = getTable()

const updateCell = createMutation({
mutationKey: ["record", tableId, field.id.value, recordId],
mutationFn: trpc.record.update.mutate,
const trigger = createMutation({
mutationKey: ["record", tableId, field.id.value, recordId, "trigger"],
mutationFn: trpc.record.trigger.mutate,
async onSuccess(data, variables, context) {
gridViewStore.exitEditing()
await recordsStore.invalidateRecord($table, recordId)
Expand All @@ -29,29 +33,66 @@
})

function handleClick() {
const option = field.option.into(undefined)
if (!option) return
const action = option.action
if (!action.values.length) return
if (field.shouldConfirm) {
confirm = true
} else {
handleUpdate()
}
}

$updateCell.mutate({
function handleUpdate() {
$trigger.mutate({
tableId,
id: recordId,
values: objectify(
action.values,
(v) => v.field!,
(v) => v.value ?? null,
),
recordId,
field: field.id.value,
})
}

$: disabled = record ? field.getIsDisabled($table, record) : false

let confirm = false
</script>

<div class={$$restProps.class}>
<Button disabled={$updateCell.isPending} on:click={handleClick} size="xs" variant="outline">
{#if $updateCell.isPending}
<Button disabled={$trigger.isPending || disabled} on:click={handleClick} size="xs" variant="outline">
{#if $trigger.isPending}
<LoaderCircleIcon className="h-3 w-3 animate-spin" />
{:else}
{field.label ?? "Button"}
{/if}
</Button>
</div>

<AlertDialog.Root bind:open={confirm}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Confirm to update</AlertDialog.Title>
<AlertDialog.Description>The following fields will be updated when you click the button.</AlertDialog.Description>
</AlertDialog.Header>

{#if field.option.isSome()}
{#each field.option.unwrap().action.values as value}
{@const field = value.field ? $table.schema.getFieldById(new FieldIdVo(value.field)).unwrap() : undefined}
<div class="flex items-center gap-2">
<FieldPicker value={value.field} disabled />
{#if field}
<FieldControl
class="text-xs"
placeholder="Value to update..."
value={value.value}
{field}
disabled
readonly
tableId={$table.id.value}
/>
{/if}
</div>
{/each}
{/if}

<AlertDialog.Footer>
<AlertDialog.Cancel on:click={() => (confirm = false)}>Cancel</AlertDialog.Cancel>
<AlertDialog.Action on:click={handleUpdate}>Continue</AlertDialog.Action>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<script lang="ts">
import type { Field, FieldType } from "@undb/table"
import type { Field, FieldType, RecordDO } from "@undb/table"
import type { ComponentType } from "svelte"
import StringCell from "./editable-cell/string-cell.svelte"
import NumberCell from "./editable-cell/number-cell.svelte"
import IdField from "../field-value/id-field.svelte"
import DateField from "../field-value/date-field.svelte"
import NumberField from "../field-value/number-field.svelte"
import { cn } from "$lib/utils"
import RollupField from "../field-value/rollup-field.svelte"
import { isEditingCell, isSelectedCell } from "./grid-view.store"
import SelectCell from "./editable-cell/select-cell.svelte"
import { getTable } from "$lib/store/table.store"
Expand All @@ -34,6 +33,7 @@
export let index: number
export let field: Field
export let recordId: string
export let record: RecordDO | undefined
export let readonly: boolean

$: isEditing = $isEditingCell(recordId, field.id.value)
Expand Down Expand Up @@ -74,6 +74,7 @@
{isEditing}
{isSelected}
{recordId}
{record}
readonly={field.isSystem || readonly}
tableId={$table.id.value}
class={cn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
recordId: item.row.original.id,
displayValue,
readonly,
record,
})
},
footer: createRender(GridViewFooter, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@
onSubmit(input) {
validateForm({ update: true })
},
onUpdate(event) {
async onUpdate(event) {
if (!event.form.valid) {
console.log(event.form.errors, event.form.data)
return
}
const data = event.form.data
const field = FieldFactory.fromJSON(data).toJSON()

$updateFieldMutation.mutate({
await $updateFieldMutation.mutateAsync({
tableId: $table.id.value,
field,
})
Expand Down Expand Up @@ -125,11 +125,10 @@
{#if $updateFieldMutation.isPending}
<LoaderCircleIcon class="mr-2 h-5 w-5 animate-spin" />
{:else}
<PencilIcon class="mr-2 h-5 w-5" />
<PencilIcon class="mr-2 h-4 w-4" />
{/if}
Update Field
</Button
>
</Button>
</div>
</form>

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "undb",
"version": "1.0.0-47",
"version": "1.0.0-48",
"private": true,
"scripts": {
"build": "NODE_ENV=production bun --bun turbo build",
Expand Down
Loading
Loading