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-100 #2090

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
chore: view widget name should be unique
nichenqin committed Oct 11, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 8e9ccf844786f6b4a6de5dccbdb0d848c7f366f7
Original file line number Diff line number Diff line change
@@ -3,16 +3,17 @@
import { trpc } from "$lib/trpc/client"
import { cn } from "$lib/utils"
import { createQuery } from "@tanstack/svelte-query"
import { ID_TYPE, type IAggregate } from "@undb/table"
import { ID_TYPE, type IAggregate, type IWidgetDTO } from "@undb/table"
import { derived } from "svelte/store"

const table = getTable()
export let viewId: string | undefined

export let widget: IWidgetDTO
export let aggregate: IAggregate

const getAggregate = createQuery({
queryKey: ["aggregate", $table.id.value],
queryKey: ["aggregate", $table.id.value, widget.id],
queryFn: () => {
if (aggregate.type === "count") {
return trpc.record.aggregate.query({
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import FieldPicker from "../../field-picker/field-picker.svelte"
import AggregateTypePicker from "../aggregate-type-picker.svelte"
import { tick } from "svelte"
import { toast } from "svelte-sonner"

export let viewId: string
export let widget: IWidgetDTO
@@ -41,9 +42,12 @@
async onSuccess(data, variables, context) {
await invalidate(`table:${$table.id.value}`)
await tick()
await client.invalidateQueries({ queryKey: ["aggregate", $table.id.value] })
await client.invalidateQueries({ queryKey: ["aggregate", $table.id.value, widget.id] })
onSuccess()
},
onError(error, variables, context) {
toast.error(error.message)
},
})

const updateViewWidget = (widget: IWidgetDTO) => {
Original file line number Diff line number Diff line change
@@ -6,5 +6,5 @@

<Button on:click={() => toggleModal(VIEW_WIDGET_MODAL)} variant="outline" size="sm">
<PlugIcon class="mr-2 size-4" />
Widget
Widgets
</Button>
Original file line number Diff line number Diff line change
@@ -8,8 +8,11 @@
import { PlusIcon } from "lucide-svelte"
import { createMutation } from "@tanstack/svelte-query"
import { trpc } from "$lib/trpc/client"
import CreateWidgetForm from "../widget/create-widget-form.svelte"
import CreateViewWidgetForm from "../widget/create-view-widget-form.svelte"
import Widget from "../widget/widget.svelte"
import { toast } from "svelte-sonner"
import { onMount } from "svelte"
import Sortable from "sortablejs"

let table = getTable()
export let viewId: Readable<string | undefined>
@@ -19,9 +22,39 @@

const createViewWidgetMutation = createMutation({
mutationFn: trpc.table.view.widget.create.mutate,
onError(error, variables, context) {
toast.error(error.message)
},
})

let open = false

let widgetsContainer: HTMLElement

onMount(() => {
// if (widgetsContainer) {
// new Sortable(widgetsContainer, {
// animation: 150,
// ghostClass: "bg-gray-100",
// onEnd: (evt) => {
// // 在这里处理排序结果
// const newOrder = Array.from(widgetsContainer.children).map((el) => el.id)
// // 调用API更新widget顺序
// updateWidgetsOrder(newOrder)
// },
// })
// }
})

function updateWidgetsOrder(newOrder: string[]) {
console.log(newOrder)
// 调用API更新widget顺序的逻辑
// 例如:
// trpc.table.view.widget.updateOrder.mutate({
// viewId: $viewId,
// order: newOrder
// })
}
</script>

<Sheet.Root portal="body" open={$isModalOpen(VIEW_WIDGET_MODAL)} onOpenChange={() => toggleModal(VIEW_WIDGET_MODAL)}>
@@ -30,29 +63,29 @@
<Sheet.Title>Widgets</Sheet.Title>
<Sheet.Description>View Widgets</Sheet.Description>
</Sheet.Header>
<div class="flex-1 space-y-3 overflow-y-auto">
<div class="flex-1 space-y-3 overflow-y-auto" bind:this={widgetsContainer}>
{#each $widgets as widget}
<Widget {widget} viewId={$view.id.value} />
{/each}
<Popover.Root bind:open>
<Popover.Trigger asChild let:builder>
<Button size="sm" class="w-full" builders={[builder]} variant={$widgets.length ? "outline" : "default"}>
<PlusIcon class="mr-2 size-4" />
Add Widget
</Button>
</Popover.Trigger>
<Popover.Content sameWidth>
{#if $viewId}
<CreateWidgetForm
viewId={$viewId}
onSuccess={() => {
open = false
}}
/>
{/if}
</Popover.Content>
</Popover.Root>
</div>
<Popover.Root bind:open>
<Popover.Trigger asChild let:builder>
<Button size="sm" class="w-full" builders={[builder]} variant={$widgets.length ? "outline" : "default"}>
<PlusIcon class="mr-2 size-4" />
Add Widget
</Button>
</Popover.Trigger>
<Popover.Content sameWidth>
{#if $viewId}
<CreateViewWidgetForm
viewId={$viewId}
onSuccess={() => {
open = false
}}
/>
{/if}
</Popover.Content>
</Popover.Root>
<Sheet.Footer>
<Sheet.Close asChild let:builder>
<Button disabled={$createViewWidgetMutation.isPending} builders={[builder]} type="button">Close</Button>
Original file line number Diff line number Diff line change
@@ -14,18 +14,26 @@
import { trpc } from "$lib/trpc/client"
import { invalidate } from "$app/navigation"
import WidgetTypePicker from "./widget-type-picker.svelte"
import { derived } from "svelte/store"
import { getNextName } from "@undb/utils"
import { toast } from "svelte-sonner"

const table = getTable()
export let viewId: string

let view = derived([table], ([$table]) => $table?.views.getViewById(viewId))
let widgets = derived([view], ([$view]) => $view?.widgets.unwrapOr([]))
let widgetNames = derived([widgets], ([$widgets]) => $widgets.map((w) => w.name))
let name = derived([widgetNames], ([$widgetNames]) => getNextName($widgetNames, "Count"))

export let onSuccess: () => void = () => {}

const form = superForm(
defaults(
{
tableId: $table.id.value,
viewId,
widget: WidgetVO.default().toJSON(),
widget: WidgetVO.default($name).toJSON(),
},
zodClient(createViewWidgetCommand),
),
@@ -54,6 +62,9 @@
await invalidate(`table:${$table.id.value}`)
onSuccess()
},
onError(error, variables, context) {
toast.error(error.message)
},
})
</script>

21 changes: 13 additions & 8 deletions apps/frontend/src/lib/components/blocks/widget/widget.svelte
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import { getTable } from "$lib/store/table.store"
import * as AlertDialog from "$lib/components/ui/alert-dialog"
import { invalidate } from "$app/navigation"
import { toast } from "svelte-sonner"

const table = getTable()

@@ -29,11 +30,14 @@
confirmDelete = false
await invalidate(`table:${$table.id.value}`)
},
onError(error, variables, context) {
toast.error(error.message)
},
})
</script>

<div class="group rounded-sm border">
<div class="flex items-center justify-between p-4">
<div class="flex items-center justify-between px-4 py-2">
<span class="text-sm font-bold">{widget.name}</span>
<div class="hidden items-center gap-2 group-hover:flex">
<Dialog.Root bind:open portal="body">
@@ -54,7 +58,7 @@
<div class="flex h-full flex-1">
<div class="w-3/4 pr-4">
{#if widget.item.type === "aggregate"}
<Aggregate {viewId} aggregate={widget.item.aggregate} class="h-full" />
<Aggregate {widget} {viewId} aggregate={widget.item.aggregate} class="h-full" />
{/if}
</div>
<div class="flex w-1/4 flex-col border-l px-4 py-2">
@@ -92,7 +96,7 @@
</div>
</div>
{#if widget.item.type === "aggregate"}
<Aggregate {viewId} aggregate={widget.item.aggregate} />
<Aggregate {widget} {viewId} aggregate={widget.item.aggregate} />
{/if}
</div>

@@ -106,11 +110,12 @@
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<AlertDialog.Action
asChild
let:builder
>
<Button builders={[builder]} variant="destructive" on:click={() => $deleteViewWidgetMutation.mutate({ tableId: $table.id.value, viewId, id: widget.id })}>
<AlertDialog.Action asChild let:builder>
<Button
builders={[builder]}
variant="destructive"
on:click={() => $deleteViewWidgetMutation.mutate({ tableId: $table.id.value, viewId, id: widget.id })}
>
Delete
</Button>
</AlertDialog.Action>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { None, Option, Some, and } from "@undb/domain"
import { None, Option, Some, and, applyRules } from "@undb/domain"
import { z } from "@undb/zod"
import type { IDuplicateViewDTO, IUpdateViewDTO } from "../../../../dto"
import { ViewWidgetNameShouldBeUnique } from "../../../../rules/view-widget-name-should-be-unique.rule"
import { type TableComositeSpecification } from "../../../../specifications"
import {
WithNewView,
@@ -102,6 +103,8 @@ export abstract class AbstractView {
}

setWidgets(widgets: IWidgetDTO[]) {
const names = widgets.map((w) => w.name)
applyRules(new ViewWidgetNameShouldBeUnique(names))
this.widgets = Some(widgets.map((widget) => new WidgetVO(widget)))
}

@@ -119,7 +122,7 @@ export abstract class AbstractView {
return Some(new WithViewWidgets(this.id, Option(previous), widgets))
}

$deleteWidgetSpec(id: WidgetId): Option<WithViewWidgets> {
$deleteWidgetSpec(id: string): Option<WithViewWidgets> {
const previous = this.widgets.into(null)
const widgets = this.widgets.unwrapOr([]).filter((w) => w.id !== id)
return Some(new WithViewWidgets(this.id, Option(previous), widgets))
21 changes: 21 additions & 0 deletions packages/table/src/rules/view-widget-name-should-be-unique.rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DomainRules, ExceptionBase } from "@undb/domain"

class ViewWidgetNameShouldBeUniqueError extends ExceptionBase {
code = "table:VIEW_WIDGET_NAME_SHOULD_BE_UNIQUE"

constructor() {
super("view widget name should be unique within a view")
}
}

export class ViewWidgetNameShouldBeUnique extends DomainRules<ViewWidgetNameShouldBeUniqueError> {
constructor(private readonly names: string[]) {
super()
}

override err = new ViewWidgetNameShouldBeUniqueError()

override isBroken(): boolean {
return new Set(this.names).size !== this.names.length
}
}