This commit is contained in:
Lakee Sivaraya
2026-01-16 12:22:52 -08:00
parent 42aa794713
commit 897891ee1e
10 changed files with 94 additions and 75 deletions

View File

@@ -21,9 +21,9 @@ import {
import type { ColumnDefinition } from '@/lib/table'
import { useCreateTable } from '@/hooks/queries/use-tables'
const logger = createLogger('CreateTableModal')
const logger = createLogger('CreateModal')
interface CreateTableModalProps {
interface CreateModalProps {
isOpen: boolean
onClose: () => void
}
@@ -44,7 +44,7 @@ function createEmptyColumn(): ColumnWithId {
return { id: nanoid(), name: '', type: 'string', required: true, unique: false }
}
export function CreateTableModal({ isOpen, onClose }: CreateTableModalProps) {
export function CreateModal({ isOpen, onClose }: CreateModalProps) {
const params = useParams()
const workspaceId = params.workspaceId as string

View File

@@ -0,0 +1,20 @@
interface EmptyStateProps {
hasSearchQuery: boolean
}
export function EmptyState({ hasSearchQuery }: EmptyStateProps) {
return (
<div className='col-span-full flex h-64 items-center justify-center rounded-[4px] bg-[var(--surface-3)] dark:bg-[var(--surface-4)]'>
<div className='text-center'>
<p className='font-medium text-[var(--text-secondary)] text-sm'>
{hasSearchQuery ? 'No tables found' : 'No tables yet'}
</p>
<p className='mt-1 text-[var(--text-muted)] text-xs'>
{hasSearchQuery
? 'Try adjusting your search query'
: 'Create your first table to store structured data for your workflows'}
</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,16 @@
interface ErrorStateProps {
error: unknown
}
export function ErrorState({ error }: ErrorStateProps) {
return (
<div className='col-span-full flex h-64 items-center justify-center rounded-[4px] bg-[var(--surface-3)] dark:bg-[var(--surface-4)]'>
<div className='text-center'>
<p className='font-medium text-[var(--text-secondary)] text-sm'>Error loading tables</p>
<p className='mt-1 text-[var(--text-muted)] text-xs'>
{error instanceof Error ? error.message : 'An error occurred'}
</p>
</div>
</div>
)
}

View File

@@ -1,2 +1,6 @@
export * from './create-table-modal'
export * from './create-modal'
export * from './empty-state'
export * from './error-state'
export * from './loading-state'
export * from './table-card'
export * from './tables-view'

View File

@@ -0,0 +1,31 @@
export function LoadingState() {
return (
<>
{Array.from({ length: 8 }).map((_, i) => (
<div
key={i}
className='flex h-full flex-col gap-[12px] rounded-[4px] bg-[var(--surface-3)] px-[8px] py-[6px] dark:bg-[var(--surface-4)]'
>
<div className='flex items-center justify-between gap-[8px]'>
<div className='h-[17px] w-[120px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
<div className='h-[22px] w-[90px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
<div className='flex flex-1 flex-col gap-[8px]'>
<div className='flex items-center justify-between'>
<div className='flex items-center gap-[12px]'>
<div className='h-[15px] w-[50px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
<div className='h-[15px] w-[50px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
<div className='h-[15px] w-[60px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
<div className='h-0 w-full border-[var(--divider)] border-t' />
<div className='flex h-[36px] flex-col gap-[6px]'>
<div className='h-[15px] w-full animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
<div className='h-[15px] w-[75%] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
</div>
</div>
))}
</>
)
}

View File

@@ -26,8 +26,8 @@ import {
} from '@/components/emcn'
import type { TableDefinition } from '@/lib/table'
import { useDeleteTable } from '@/hooks/queries/use-tables'
import { getTypeBadgeVariant } from '../[tableId]/table-data-viewer/utils'
import { formatAbsoluteDate, formatRelativeTime } from './utils'
import { getTypeBadgeVariant } from '../[tableId]/lib/utils'
import { formatAbsoluteDate, formatRelativeTime } from '../lib/utils'
const logger = createLogger('TableCard')

View File

@@ -8,10 +8,13 @@ import { Input } from '@/components/ui/input'
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import { useTablesList } from '@/hooks/queries/use-tables'
import { useDebounce } from '@/hooks/use-debounce'
import { CreateTableModal } from './components/create-table-modal'
import { TableCard } from './components/table-card'
import { CreateModal } from './create-modal'
import { EmptyState } from './empty-state'
import { ErrorState } from './error-state'
import { LoadingState } from './loading-state'
import { TableCard } from './table-card'
export function Tables() {
export function TablesView() {
const params = useParams()
const workspaceId = params.workspaceId as string
const userPermissions = useUserPermissionsContext()
@@ -84,7 +87,7 @@ export function Tables() {
{/* Content */}
<div className='mt-[24px] grid grid-cols-1 gap-[20px] md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
{isLoading ? (
<LoadingSkeletons />
<LoadingState />
) : error ? (
<ErrorState error={error} />
) : filteredTables.length === 0 ? (
@@ -99,69 +102,7 @@ export function Tables() {
</div>
</div>
<CreateTableModal isOpen={isCreateModalOpen} onClose={() => setIsCreateModalOpen(false)} />
<CreateModal isOpen={isCreateModalOpen} onClose={() => setIsCreateModalOpen(false)} />
</>
)
}
function LoadingSkeletons() {
return (
<>
{Array.from({ length: 8 }).map((_, i) => (
<div
key={i}
className='flex h-full flex-col gap-[12px] rounded-[4px] bg-[var(--surface-3)] px-[8px] py-[6px] dark:bg-[var(--surface-4)]'
>
<div className='flex items-center justify-between gap-[8px]'>
<div className='h-[17px] w-[120px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
<div className='h-[22px] w-[90px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
<div className='flex flex-1 flex-col gap-[8px]'>
<div className='flex items-center justify-between'>
<div className='flex items-center gap-[12px]'>
<div className='h-[15px] w-[50px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
<div className='h-[15px] w-[50px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
<div className='h-[15px] w-[60px] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
<div className='h-0 w-full border-[var(--divider)] border-t' />
<div className='flex h-[36px] flex-col gap-[6px]'>
<div className='h-[15px] w-full animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
<div className='h-[15px] w-[75%] animate-pulse rounded-[4px] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]' />
</div>
</div>
</div>
))}
</>
)
}
function ErrorState({ error }: { error: unknown }) {
return (
<div className='col-span-full flex h-64 items-center justify-center rounded-[4px] bg-[var(--surface-3)] dark:bg-[var(--surface-4)]'>
<div className='text-center'>
<p className='font-medium text-[var(--text-secondary)] text-sm'>Error loading tables</p>
<p className='mt-1 text-[var(--text-muted)] text-xs'>
{error instanceof Error ? error.message : 'An error occurred'}
</p>
</div>
</div>
)
}
function EmptyState({ hasSearchQuery }: { hasSearchQuery: boolean }) {
return (
<div className='col-span-full flex h-64 items-center justify-center rounded-[4px] bg-[var(--surface-3)] dark:bg-[var(--surface-4)]'>
<div className='text-center'>
<p className='font-medium text-[var(--text-secondary)] text-sm'>
{hasSearchQuery ? 'No tables found' : 'No tables yet'}
</p>
<p className='mt-1 text-[var(--text-muted)] text-xs'>
{hasSearchQuery
? 'Try adjusting your search query'
: 'Create your first table to store structured data for your workflows'}
</p>
</div>
</div>
)
}

View File

@@ -0,0 +1 @@
export * from './utils'

View File

@@ -1,3 +1,6 @@
/**
* Formats a date as relative time (e.g., "5m ago", "2d ago")
*/
export function formatRelativeTime(dateValue: string | Date): string {
const dateString = typeof dateValue === 'string' ? dateValue : dateValue.toISOString()
const date = new Date(dateString)
@@ -13,6 +16,9 @@ export function formatRelativeTime(dateValue: string | Date): string {
return `${Math.floor(diffInSeconds / 31536000)}y ago`
}
/**
* Formats a date as absolute date string (e.g., "Jan 15, 2024, 10:30 AM")
*/
export function formatAbsoluteDate(dateValue: string | Date): string {
const dateString = typeof dateValue === 'string' ? dateValue : dateValue.toISOString()
const date = new Date(dateString)

View File

@@ -1,7 +1,7 @@
import { redirect } from 'next/navigation'
import { getSession } from '@/lib/auth'
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
import { Tables } from './tables'
import { TablesView } from './components'
interface TablesPageProps {
params: Promise<{
@@ -22,5 +22,5 @@ export default async function TablesPage({ params }: TablesPageProps) {
redirect('/')
}
return <Tables />
return <TablesView />
}