Skip to content

Commit

Permalink
feat: readonly form ui
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Aug 24, 2024
1 parent 2d42b34 commit 740c500
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 87 deletions.
1 change: 1 addition & 0 deletions apps/frontend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ type ShareTarget {
}

enum ShareTargetType {
base
form
view
}
Expand Down
142 changes: 77 additions & 65 deletions apps/frontend/src/lib/components/blocks/base/share-base-nav.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,79 +25,91 @@
$: viewId = $page.params.viewId
$: baseTables = base.tables.filter((t) => t !== null) ?? []
$: active = !tableId && !viewId
let open: Record<string, boolean> = {}
</script>

<a href={`/s/b/${shareId}`} class={cn("group flex h-8 items-center justify-between pl-4 pr-2 transition-all")}>
<div class={cn("flex h-full flex-1 items-center font-light")}>
<HardDriveIcon class="mr-2 h-4 w-4" />
{base.name}
</div>
</a>
{#each baseTables as table}
{#if table}
{@const active = table.id === tableId}
{@const views = table.views.filter((view) => !view.isDefault)}
<Collapsible.Root bind:open={open[table.id]}>
<div
class={cn(
"group flex h-8 cursor-pointer items-center justify-between rounded-md pl-8 pr-2 transition-all",
active && !viewId ? "bg-primary/90" : "hover:bg-gray-100",
)}
>
<a
href={`/s/b/${shareId}/t/${table.id}`}
<nav class="grid items-start gap-1 px-1.5 text-sm font-medium">
<a
href={`/s/b/${shareId}`}
class={cn(
"group flex h-8 items-center justify-between pl-4 pr-2 transition-all",
active && "bg-primary/90 text-background rounded-md",
)}
>
<div class={cn("flex h-full flex-1 items-center font-light")}>
<HardDriveIcon class="mr-2 h-4 w-4" />
{base.name}
</div>
</a>
{#each baseTables as table}
{#if table}
{@const active = table.id === tableId}
{@const views = table.views.filter((view) => !view.isDefault)}
<Collapsible.Root bind:open={open[table.id]}>
<div
class={cn(
"text-primary flex h-full flex-1 items-center font-light",
active && !viewId && "text-background font-medium",
"group flex h-8 cursor-pointer items-center justify-between rounded-md pl-8 pr-2 transition-all",
active && !viewId ? "bg-primary/90" : "hover:bg-gray-100",
)}
>
<DatabaseIcon class="mr-2 h-4 w-4" />
{table.name}
</a>
<div class="flex items-center gap-2 opacity-0 transition-all group-hover:opacity-100">
{#if views.length > 0}
<Collapsible.Trigger
on:click={(e) => {
e.preventDefault()
e.stopPropagation()
open[table.id] = !open[table.id]
}}
<a
href={`/s/b/${shareId}/t/${table.id}`}
class={cn(
"text-primary flex h-full flex-1 items-center font-light",
active && !viewId && "text-background font-medium",
)}
>
<DatabaseIcon class="mr-2 h-4 w-4" />
{table.name}
</a>
<div class="flex items-center gap-2 opacity-0 transition-all group-hover:opacity-100">
{#if views.length > 0}
<Collapsible.Trigger
on:click={(e) => {
e.preventDefault()
e.stopPropagation()
open[table.id] = !open[table.id]
}}
class={cn(
"flex h-5 w-5 items-center justify-center rounded-md hover:bg-gray-200",
active && !viewId && "hover:bg-primary",
)}
>
<ChevronRightIcon
class={cn(
"text-muted-foreground h-4 w-4 transition-all",
open[table.id] && "rotate-90",
active && "text-background",
)}
/>
</Collapsible.Trigger>
{/if}
</div>
</div>
<Collapsible.Content>
{#each views as view}
{@const active = view.id === viewId}
<div
class={cn(
"flex h-5 w-5 items-center justify-center rounded-md hover:bg-gray-200",
active && !viewId && "hover:bg-primary",
"group flex h-8 items-center justify-between rounded-sm pl-14 pr-2 transition-all",
active ? "bg-primary/90" : "hover:bg-gray-100",
)}
>
<ChevronRightIcon
<a
class={cn(
"text-muted-foreground h-4 w-4 transition-all",
open[table.id] && "rotate-90",
active && "text-background",
"flex h-full flex-1 items-center text-xs font-light",
active && "text-background font-medium",
)}
/>
</Collapsible.Trigger>
{/if}
</div>
</div>
<Collapsible.Content>
{#each views as view}
{@const active = view.id === viewId}
<div
class={cn(
"group flex h-8 items-center justify-between rounded-sm pl-14 pr-2 transition-all",
active ? "bg-primary/90" : "hover:bg-gray-100",
)}
>
<a
class={cn("flex h-full flex-1 items-center text-xs font-light", active && "text-background font-medium")}
href={`/s/b/${shareId}/t/${table.id}/${view.id}`}
>
<SheetIcon class="mr-2 h-4 w-4" />
{view.name}
</a>
</div>
{/each}
</Collapsible.Content>
</Collapsible.Root>
{/if}
{/each}
href={`/s/b/${shareId}/t/${table.id}/${view.id}`}
>
<SheetIcon class="mr-2 h-4 w-4" />
{view.name}
</a>
</div>
{/each}
</Collapsible.Content>
</Collapsible.Root>
{/if}
{/each}
</nav>
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,19 @@
}}
class={cn(
"bg-background relative -mx-4 space-y-2 rounded-md border-2 border-transparent p-0 px-4 transition-all",
isSelected ? "border-primary shadow-lg" : "hover:bg-muted/50",
isSelected ? "border-primary shadow-lg" : !readonly && "hover:bg-muted/50",
)}
>
{#if isSelected}
<button disabled={readonly} type="button" class="handler bg-primary absolute -left-2 top-2 rounded-sm py-2">
<button
disabled={readonly}
type="button"
class="handler bg-primary absolute -left-2 top-2 rounded-sm py-2"
>
<GripVerticalIcon class="h-4 w-4 text-white" />
</button>
{/if}
<div class={cn("cursor-pointer space-y-2 py-2 pb-4")}>
<div class={cn("cursor-pointer space-y-2 py-2 pb-4", readonly && "pointer-events-none")}>
<div class="text-md flex items-center gap-2 font-medium">
<FieldIcon {field} type={field.type} class="h-4 w-4" />
<span>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<script lang="ts">
import * as Tabs from "$lib/components/ui/tabs"
import { DatabaseIcon, FormInputIcon, HardDriveIcon, SheetIcon, TextCursorInputIcon } from "lucide-svelte"
import * as Breadcrumb from "$lib/components/ui/breadcrumb/index.js"
import { getTable } from "$lib/store/table.store"
import { tab, isFormTab, formId } from "$lib/store/tab.store"
import RecordUpdating from "../record-updating/record-updating.svelte"
import { derived } from "svelte/store"
import { page } from "$app/stores"
import { getBaseById } from "$lib/store/base.store"
const table = getTable()
$: base = $getBaseById($table.baseId)
$: viewId = derived([page], ([$page]) => {
return $page.params.viewId
})
$: view = $table.views.getViewById($viewId)
$: forms = $table.forms?.props ?? []
$: currentFormId = $formId ? $formId : forms[0]?.id
$: currentForm = forms.find((form) => form.id === currentFormId)
</script>

<header class="bg-muted/40 flex h-12 items-center gap-4 border-b px-4 py-3 lg:px-6">
<div class="relative flex w-full flex-1 items-center justify-between">
<div class="relative flex items-center gap-4">
<Breadcrumb.Root>
<Breadcrumb.List>
{#if base}
<Breadcrumb.Item>
<Breadcrumb.Link class="flex items-center gap-2">
<HardDriveIcon class="h-4 w-4" />
{base.name}
</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
{/if}
<Breadcrumb.Item>
<Breadcrumb.Link class="flex items-center gap-2">
<DatabaseIcon class="h-4 w-4" />
{$table.name.value}
</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator />
{#if $isFormTab}
{#if forms.length}
<Breadcrumb.Item>
<FormInputIcon class="h-4 w-4" />
Forms
</Breadcrumb.Item>
<Breadcrumb.Separator />
<Breadcrumb.Item>
{currentForm?.name}
</Breadcrumb.Item>
{/if}
{:else}
<Breadcrumb.Item class="text-xs">
<Breadcrumb.Page class="flex items-center gap-1">
{view.name.value}
</Breadcrumb.Page>
</Breadcrumb.Item>
{/if}
</Breadcrumb.List>
</Breadcrumb.Root>

<RecordUpdating />
</div>

{#if forms.length}
<Tabs.Root
value={$tab === "data" || !$tab ? "data" : $tab}
onValueChange={(value) => {
if (!$tab && value === "data") {
return
}

tab.set(value === "data" ? "" : (value ?? ""))
}}
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transform"
>
<Tabs.List>
<Tabs.Trigger value="data">
<SheetIcon class="mr-2 h-4 w-4" />
Data
</Tabs.Trigger>
<Tabs.Trigger value="form">
<TextCursorInputIcon class="mr-2 h-4 w-4" />
Forms
</Tabs.Trigger>
</Tabs.List>
</Tabs.Root>
{/if}
</div>
</header>
24 changes: 13 additions & 11 deletions apps/frontend/src/lib/components/blocks/view-sort/view-sort.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
variant={count || open ? "secondary" : "ghost"}
builders={[builder]}
size="sm"
disabled={!readonly && (!$hasPermission("table:update") && (!sort || sort.isEmpty))}
disabled={!readonly && !$hasPermission("table:update") && (!sort || sort.isEmpty)}
>
<ArrowUpDownIcon class="mr-2 h-4 w-4" />
Sorts
Expand Down Expand Up @@ -176,19 +176,21 @@
</div>
{:else if readonly}
<div class="flex flex-col items-center gap-3 px-4 py-6 text-center">
<SortAscIcon class="text-primary h-10 w-10" />
<SortAscIcon class="text-primary h-7 w-7" />
<h3 class="text-muted-foreground text-sm font-semibold tracking-tight">There's no sorts</h3>
</div>
{/if}
<div class="flex w-full items-center justify-between px-4 py-3">
{#if $hasPermission("table:update")}
<Button {disabled} variant="ghost" size="sm" on:click={addSort}>
<PlusIcon class="mr-2 h-3 w-3" />
Add Sort
</Button>
<Button variant="outline" size="sm" on:click={submit}>Submit</Button>
{/if}
</div>
{#if !readonly}
<div class="flex w-full items-center justify-between px-4 py-3">
{#if $hasPermission("table:update")}
<Button {disabled} variant="ghost" size="sm" on:click={addSort}>
<PlusIcon class="mr-2 h-3 w-3" />
Add Sort
</Button>
<Button variant="outline" size="sm" on:click={submit}>Submit</Button>
{/if}
</div>
{/if}
</div>
</Popover.Content>
</Popover.Root>
10 changes: 10 additions & 0 deletions apps/frontend/src/routes/(share)/s/b/[shareId]/+layout.gql
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ query GetShareBaseData($shareId: ID!) {
id
name

forms {
id
name
fields
description
option {
backgroundColor
}
}

views {
id
name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import { onMount, type ComponentType } from "svelte"
import { derived, writable } from "svelte/store"
import { queryParam } from "sveltekit-search-params"
import { isDataTab, isFormTab } from "$lib/store/tab.store"
import ShareTableHeader from "$lib/components/blocks/table-header/share-table-header.svelte"
import FormsReadonly from "$lib/components/blocks/forms/forms-readonly.svelte"
let RecordDetailSheet: ComponentType
Expand Down Expand Up @@ -48,16 +51,21 @@
</script>

<div class="flex flex-1 flex-col">
<ShareTableHeader />
<ShareTableTools />

<GridViewDataTable
{viewId}
readonly
{perPage}
{currentPage}
isLoading={$getRecords.isLoading}
total={$getRecords.data?.total ?? 0}
/>
{#if $isDataTab}
<GridViewDataTable
{viewId}
readonly
{perPage}
{currentPage}
isLoading={$getRecords.isLoading}
total={$getRecords.data?.total ?? 0}
/>
{:else if $isFormTab}
<FormsReadonly />
{/if}

{#if RecordDetailSheet}
<RecordDetailSheet readonly />
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 @@ -63,6 +63,7 @@ export class Graphql {
enum ShareTargetType {
view
base
form
}
Expand Down
Loading

0 comments on commit 740c500

Please sign in to comment.