Skip to content

Commit

Permalink
Merge pull request #1275 from undb-xyz/feature/gantt
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin authored Jul 6, 2023
2 parents d87c685 + 3e7b27e commit e79a264
Show file tree
Hide file tree
Showing 56 changed files with 1,792 additions and 12,798 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
- 🔧 Deploy with variant adapters
- 🎮 Developer friendly. Provide openapi / webhooks / realtime subscriptions / sdk(soon) / erd preview and more
- :sparkles: Multiple built-in field types and variants
- :city_sunset: Different types of views, including grid, kanban, tree, calendar and more
- :city_sunset: Different types of views, including grid, kanban, gantt, tree, calendar and more

## 📚 Tech Stack

Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/core/table/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { SetFieldSortCommandHandler } from './set-field-sort.command.handler.js'
import { SetFieldVisibilityCommandHandler } from './set-field-visibility.command.handler.js'
import { SetFieldWidthCommandHandler } from './set-field-width.command.handler.js'
import { SetFiltersCommandHandler } from './set-filters.command.handler.js'
import { SetGanttFieldCommandHandler } from './set-gantt-field.command.handler.js'
import { SetKanbanFieldCommandHandler } from './set-kanban-field.command.handler.js'
import { SetPinnedFieldsCommandHandler } from './set-pinned-fields.command.handler.js'
import { SetRowHeightCommandHandler } from './set-row-height.command.handler.js'
Expand Down Expand Up @@ -86,4 +87,5 @@ export const commandHandlers = [
DeleteWidgetCommandHandler,
DuplicateFieldCommandHandler,
ExportGridCommandHandler,
SetGanttFieldCommandHandler,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ICommandHandler } from '@nestjs/cqrs'
import { CommandHandler } from '@nestjs/cqrs'
import { type ITableRepository } from '@undb/core'
import { SetGanttFieldCommandHandler as DomainHandler, SetGanttFieldCommand } from '@undb/cqrs'
import { InjectTableRepository } from '../adapters/index.js'

@CommandHandler(SetGanttFieldCommand)
export class SetGanttFieldCommandHandler extends DomainHandler implements ICommandHandler<SetGanttFieldCommand> {
constructor(
@InjectTableRepository()
protected readonly repo: ITableRepository,
) {
super(repo)
}
}
1 change: 1 addition & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"svelte-check": "^3.4.4",
"svelte-copy": "^1.4.1",
"svelte-dnd-action": "^0.9.22",
"svelte-gantt": "4.0.9-beta",
"svelte-grid": "^5.1.1",
"svelte-i18next": "^2.0.0",
"svelte-jsoneditor": "^0.17.8",
Expand Down
7 changes: 7 additions & 0 deletions apps/frontend/src/app.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

.root,
#root,
#docs-root {
--primary-color: #fff;
--secondary-color: #000;
}
77 changes: 77 additions & 0 deletions apps/frontend/src/lib/gantt/GanttConfig.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts">
import { getTable, getView } from '$lib/store/table'
import { Button, Hr, Radio } from 'flowbite-svelte'
import FieldIcon from '$lib/field/FieldIcon.svelte'
import { trpc } from '$lib/trpc/client'
import { writable } from 'svelte/store'
import { configViewModal, createFieldInitial, createFieldModal } from '$lib/store/modal'
import { t } from '$lib/i18n'
import { invalidate } from '$app/navigation'
import { FieldId } from '@undb/core'
const table = getTable()
const view = getView()
$: ganttFields = $table.schema.ganttFields
const ganttField = writable($view.ganttFieldIdString)
const setField = trpc().table.view.gantt.setField.mutation({
async onSuccess(data, variables, context) {
await invalidate(`table:${$table.id.value}`)
$view.ganttFieldIdString = $ganttField
configViewModal.close()
},
})
const onChange = async () => {
if (ganttField) {
$setField.mutate({
tableId: $table.id.value,
viewId: $view.id.value,
field: $ganttField,
})
}
}
</script>

<div class="flex flex-col space-y-2">
{#each ganttFields as field}
<Radio bind:group={$ganttField} name="ganttFieldId" value={field.id.value} on:change={onChange} class="space-x-1">
<FieldIcon type={field.type} />
<span>{field.name.value}</span>
</Radio>
{/each}
</div>

{#if ganttFields.length}
<div class="my-4">
<Hr>
<span class="text-gray-400 text-sm font-normal">{$t('or', { ns: 'common' })}</span>
</Hr>
</div>
{/if}

<div class="flex flex-col justify-center gap-2">
<Button
size="xs"
color="light"
class="flex gap-2"
on:click={() => {
const id = FieldId.createId()
$createFieldInitial = {
id,
type: 'date-range',
}

createFieldModal.open(async () => {
$setField.mutate({
tableId: $table.id.value,
viewId: $view.id.value,
field: id,
})
})
}}
>
<i class="ti ti-plus" />
<span>{$t('Create New Date Range Field')}</span>
<FieldIcon type="select" />
</Button>
</div>
23 changes: 23 additions & 0 deletions apps/frontend/src/lib/gantt/GanttIndex.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import { getTable, getView } from '$lib/store/table'
import { Card } from 'flowbite-svelte'
import type { DateRangeField } from '@undb/core'
import GanttConfig from './GanttConfig.svelte'
import GanttView from './GanttView.svelte'
const table = getTable()
const view = getView()
$: fieldId = $view.ganttFieldIdString
$: field = fieldId ? ($table.schema.getFieldById(fieldId).into() as DateRangeField | undefined) : undefined
</script>

{#if field}
<GanttView {field} />
{:else}
<div class="flex items-center justify-center h-screen w-full bg-gray-100 dark:bg-slate-800/80">
<Card class="flex-1">
<GanttConfig />
</Card>
</div>
{/if}
9 changes: 9 additions & 0 deletions apps/frontend/src/lib/gantt/GanttToolbar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script lang="ts">
import ShareViewButton from '$lib/share/ShareViewButton.svelte'
import CreateRecordButton from '$lib/table/CreateRecordButton.svelte'
import FilterMenu from '$lib/table/FilterMenu.svelte'
</script>

<CreateRecordButton />
<FilterMenu />
<ShareViewButton />
180 changes: 180 additions & 0 deletions apps/frontend/src/lib/gantt/GanttView.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<script lang="ts">
import { SvelteGantt, SvelteGanttDependencies, SvelteGanttTable } from 'svelte-gantt'
import type { SvelteGanttComponent, SvelteGanttOptions } from 'svelte-gantt/types/gantt'
import { addDays, endOfWeek, startOfWeek, subDays } from 'date-fns'
import { currentRecordId, getTable, listRecordFn, readonly, recordHash } from '$lib/store/table'
import { RecordFactory, type DateRangeField } from '@undb/core'
import type { RowModel } from 'svelte-gantt/types/core/row'
import type { TaskModel } from 'svelte-gantt/types/core/task'
import { onMount } from 'svelte'
import { t } from '$lib/i18n'
import { trpc } from '$lib/trpc/client'
const table = getTable()
export let field: DateRangeField
let currentStart = startOfWeek(new Date())
let currentEnd = endOfWeek(new Date())
const previous = () => {
currentStart = subDays(currentStart, 7)
currentEnd = subDays(currentEnd, 7)
}
const next = () => {
currentStart = addDays(currentStart, 7)
currentEnd = addDays(currentEnd, 7)
}
$: listRecords = $listRecordFn(
[
{
path: field.id.value,
type: field.type,
operator: '$between',
value: [currentStart.toISOString(), currentEnd.toISOString()],
},
],
{
queryHash: $recordHash + '_gantt',
},
)
const updateRecord = trpc().record.update.mutation({
async onSuccess(data, variables, context) {
await $listRecords.refetch()
},
})
$: records = RecordFactory.fromQueryRecords($listRecords?.data?.records ?? [], $table.schema.toIdMap()) ?? []
$: rows = records.map<RowModel>((r) => ({
id: r.id.value,
label: r.getDisplayFieldsValue($table),
height: 52,
classes: 'bg-gray-100 dark:!bg-gray-300 dark:text-white',
}))
$: tasks = records.map<TaskModel>((r) => {
const value = r.valuesJSON?.[field.id.value]
const [from, to] = value
const fromTimeStamp = new Date(from).getTime()
const toTimeStampe = new Date(to).getTime()
return {
id: r.id.value as any as number,
resourceId: r.id.value as any as number,
label: r.getDisplayFieldsValue($table),
from: fromTimeStamp,
to: toTimeStampe,
classes: '!bg-blue-400 hover:!bg-blue-500',
enableDragging: !$readonly,
}
})
$: options = {
rows,
tasks,
dependencies: [],
timeRanges: [],
columnOffset: 15,
magnetOffset: 15,
rowHeight: 52,
rowPadding: 6,
headers: [{ unit: 'day', format: 'MMMM Do' }],
fitWidth: true,
minWidth: 800,
from: currentStart.getTime(),
to: currentEnd.getTime(),
tableHeaders: [{ title: $t('Label', { ns: 'common' }), property: 'label', width: 140 }],
tableWidth: 240,
ganttTableModules: [SvelteGanttTable],
ganttBodyModules: [SvelteGanttDependencies],
} satisfies SvelteGanttOptions
let ele: HTMLElement | undefined
let gantt: SvelteGanttComponent
onMount(() => {
if (ele) {
gantt = new SvelteGantt({ target: ele, props: options })
// @ts-expect-error
gantt.api.tasks.on.dblclicked((event, b) => {
const [model] = event
if (!model) return
const recordId = model.id
$currentRecordId = recordId
})
// @ts-expect-error
gantt.api.tasks.on.changed((event) => {
const [model] = event
if (!model) return
if (model.sourceRow.model.id !== model.targetRow.model.id) return
const task = model.task
const recordId = task.model.resourceId
const newFrom = new Date(task.model.from)
const newTo = new Date(task.model.to)
$updateRecord.mutate({
tableId: $table.id.value,
id: recordId,
values: {
[field.id.value]: [newFrom.toISOString(), newTo.toISOString()],
},
})
})
}
})
$: if (gantt) gantt.$set(options)
</script>

<div class="w-full">
<div class="p-2 text-gray-500">
<div class="flex justify-end gap-2">
<button
on:click={previous}
class="p-1 hover:bg-gray-100 w-6 h-6 inline-flex items-center justify-center transition"
>
<i class="ti ti-chevron-left" />
</button>
<button on:click={next} class="p-1 hover:bg-gray-100 w-6 h-6 inline-flex items-center justify-center transition">
<i class="ti ti-chevron-right" />
</button>
</div>
</div>
<div class="border-t" bind:this={ele} id="undb-gantt" />
</div>

<style>
#undb-gantt {
flex-grow: 1;
overflow: auto;
}
#undb-gantt :global(.sg-hover) {
background-color: #00000008;
}
#undb-gantt :global(.sg-hover .sg-table-body-cell) {
background-color: #00000008;
}
:global(.dark .sg-gantt .column-header-cell) {
color: white;
}
:global(.dark .sg-gantt .column-header-cell:hover) {
color: #374151;
background-color: #f7f7f7;
}
:global(.dark .sg-gantt .sg-table-body-cell) {
color: white;
background-color: #374151;
border: none;
}
:global(.dark .sg-gantt .sg-table-header-cell) {
color: white;
background-color: #374151;
}
</style>
2 changes: 1 addition & 1 deletion apps/frontend/src/lib/store/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export const listRecordFn: Readable<
'share.view',
() => (filter?: IRootFilter, options?: ListRecordQueryOptions) =>
trpc().share.viewRecords.query(
{ viewId: $view.id.value },
{ viewId: $view.id.value, q: $q, filter },
{ refetchOnMount: false, refetchOnWindowFocus: true, queryHash: $recordHash, ...options },
),
)
Expand Down
2 changes: 2 additions & 0 deletions apps/frontend/src/lib/table/TableIndex.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import CalendarIndex from '$lib/calendar/CalendarIndex.svelte'
import DashboardIndex from '$lib/dashboard/DashboardIndex.svelte'
import TreeIndex from '$lib/tree/TreeIndex.svelte'
import GanttIndex from '$lib/gantt/GanttIndex.svelte'
const view = getView()
Expand All @@ -15,6 +16,7 @@
const map: Partial<Record<IViewDisplayType, ComponentType>> = {
grid: TableView,
kanban: KanbanIndex,
gantt: GanttIndex,
tree: TreeIndex,
calendar: CalendarIndex,
dashboard: DashboardIndex,
Expand Down
6 changes: 3 additions & 3 deletions apps/frontend/src/lib/table/TableView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -452,14 +452,14 @@
}
:global(.dark .hovered) {
background-color: #374151;
background-color: var(--primary-color) !important;
}
:global(.dark revogr-data .rgRow.focused-rgRow) {
background-color: #374151 !important;
}
:global(.dark revo-grid[theme=compact] revogr-header .rgHeaderCell.focused-cell){
background-color: #374151 !important
:global(.dark revo-grid[theme='compact'] revogr-header .rgHeaderCell.focused-cell) {
background-color: #374151 !important;
}
</style>
Loading

0 comments on commit e79a264

Please sign in to comment.