Skip to content

Commit

Permalink
Implement datasets data model
Browse files Browse the repository at this point in the history
  • Loading branch information
andresgutgon committed Sep 6, 2024
1 parent ee304cb commit bc5aee9
Show file tree
Hide file tree
Showing 27 changed files with 2,478 additions and 91 deletions.
19 changes: 6 additions & 13 deletions apps/web/src/actions/datasets/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ import { z } from 'zod'

import { authProcedure } from '../procedures'

const ACCEPTED_FILE_TYPES = [
'text/csv',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.oasis.opendocument.spreadsheet',
]
const MAX_SIZE = 3
const MAX_UPLOAD_SIZE_IN_MB = 3 * 1024 * 1024

Expand All @@ -25,22 +19,21 @@ export const createDatasetAction = authProcedure
.refine((file) => {
return !file || file.size <= MAX_UPLOAD_SIZE_IN_MB
}, `Your dataset must be less than ${MAX_SIZE}MB in size`)
.refine((file) => {
return ACCEPTED_FILE_TYPES.includes(file.type)
}, 'Your dataset must be an Excel or CSV file'),
.refine(
(file) => file.type === 'text/csv',
'Your dataset must be a CSV file',
),
}),
{ type: 'formData' },
)
.handler(async ({ input, ctx }) => {
const result = await createDataset({
return createDataset({
workspace: ctx.workspace,
author: ctx.user,
disk: disk,
data: {
name: input.name,
file: input.dataset_file,
},
})

return result.unwrap()
}).then((r) => r.unwrap())
})
22 changes: 22 additions & 0 deletions apps/web/src/actions/datasets/destroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use server'

import { DatasetsRepository } from '@latitude-data/core/repositories'
import { destroyDataset } from '@latitude-data/core/services/datasets/destroy'
import { z } from 'zod'

import { authProcedure } from '../procedures'

export const destroyDatasetAction = authProcedure
.createServerAction()
.input(
z.object({
id: z.string(),
}),
)
.handler(async ({ input, ctx }) => {
const { id } = input
const repo = new DatasetsRepository(ctx.workspace.id)
const dataset = await repo.find(id).then((r) => r.unwrap())

return await destroyDataset(dataset).then((r) => r.unwrap())
})
12 changes: 12 additions & 0 deletions apps/web/src/actions/datasets/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use server'

import { DatasetsRepository } from '@latitude-data/core/repositories'

import { authProcedure } from '../procedures'

export const getDatasetsAction = authProcedure
.createServerAction()
.handler(async ({ ctx }) => {
const scope = new DatasetsRepository(ctx.workspace.id)
return await scope.findAll().then((r) => r.unwrap())
})
2 changes: 1 addition & 1 deletion apps/web/src/app/(private)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default async function DashboardLayout({
return (
<AppLayout
navigationLinks={NAV_LINKS}
currentUser={{ ...user }}
currentUser={user}
breadcrumbs={breadcrumbs}
sectionLinks={MAIN_NAV_LINKS}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client'

import { useState } from 'react'

import { Dataset } from '@latitude-data/core/browser'
import {
Button,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Text,
} from '@latitude-data/web-ui'
import DeleteDatasetModal from '$/app/(private)/datasets/_components/DeleteDatasetModal'
import useDatasets from '$/stores/datasets'

export function DatasetsTable({
datasets: serverDatasets,
}: {
datasets: Dataset[]
}) {
const [deletable, setDeletable] = useState<Dataset | null>(null)
const { data: datasets } = useDatasets(undefined, {
fallbackData: serverDatasets,
})
return (
<>
<DeleteDatasetModal dataset={deletable} setDataset={setDeletable} />
<Table>
<TableHeader>
<TableRow verticalPadding>
<TableHead>Name</TableHead>
<TableHead>Rows</TableHead>
<TableHead>Columns</TableHead>
<TableHead>Author</TableHead>
<TableHead />
</TableRow>
</TableHeader>
<TableBody>
{datasets.map((dataset) => (
<TableRow key={dataset.id} verticalPadding>
<TableCell>
<Text.H4>{dataset.name}</Text.H4>
</TableCell>
<TableCell>
<Text.H4>{dataset.fileMetadata.rowCount}</Text.H4>
</TableCell>
<TableCell>
<Text.H4>{dataset.fileMetadata.headers.length}</Text.H4>
</TableCell>
<TableCell>
<Text.H4>{dataset.author?.name}</Text.H4>
</TableCell>
<TableCell align='center'>
<Button
onClick={() => setDeletable(dataset)}
variant='nope'
iconProps={{ name: 'trash', color: 'destructive' }}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Dataset } from '@latitude-data/core/browser'
import { ReactStateDispatch } from '@latitude-data/web-ui'
import { destroyDatasetAction } from '$/actions/datasets/destroy'
import DestroyModal from '$/components/modals/DestroyModal'
import useDatasets from '$/stores/datasets'
import { useRouter } from 'next/navigation'

export default function DeleteDatasetModal({
dataset,
setDataset,
}: {
dataset: Dataset | null
setDataset: ReactStateDispatch<Dataset | null>
}) {
const router = useRouter()
const { data, destroy } = useDatasets()
const isLast = data?.length === 1
if (!dataset) return null

return (
<DestroyModal<typeof destroyDatasetAction>
title={`Delete ${dataset.name}`}
description='Deleted datasets will no longer accessible to generate new evaluations.'
onOpenChange={(open: boolean) => !open && setDataset(null)}
action={destroy}
submitStr='Delete dataset'
model={dataset}
onSuccess={() => {
if (isLast) router.refresh()

setDataset(null)
}}
/>
)
}
31 changes: 19 additions & 12 deletions apps/web/src/app/(private)/datasets/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { ReactNode } from 'react'

import { DatasetsRepository } from '@latitude-data/core/repositories'
import {
Container,
TableBlankSlate,
TableWithHeader,
Text,
} from '@latitude-data/web-ui'
import { DatasetsTable } from '$/app/(private)/datasets/_components/DatasetsTable'
import { AppLayout } from '$/components/layouts'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { ROUTES } from '$/services/routes'
Expand All @@ -19,11 +21,12 @@ export default async function DatasetsList({
children: ReactNode
}>) {
const { workspace, user } = await getCurrentUser()

const scope = new DatasetsRepository(workspace.id)
const datasets = await scope.findAll().then((r) => r.unwrap())
return (
<AppLayout
navigationLinks={NAV_LINKS}
currentUser={{ ...user }}
currentUser={user}
sectionLinks={MAIN_NAV_LINKS}
breadcrumbs={[
{
Expand All @@ -44,16 +47,20 @@ export default async function DatasetsList({
</Link>
}
/>
<TableBlankSlate
description='There are no datasets yet. Create one to start testing your prompts.'
link={
<Link href={ROUTES.datasets.new.root}>
<TableBlankSlate.Button>
Create your first dataset
</TableBlankSlate.Button>
</Link>
}
/>
{datasets.length > 0 ? (
<DatasetsTable datasets={datasets} />
) : (
<TableBlankSlate
description='There are no datasets yet. Create one to start testing your prompts.'
link={
<Link href={ROUTES.datasets.new.root}>
<TableBlankSlate.Button>
Create your first dataset
</TableBlankSlate.Button>
</Link>
}
/>
)}
</Container>
</AppLayout>
)
Expand Down
17 changes: 9 additions & 8 deletions apps/web/src/app/(private)/datasets/new/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import {
FormWrapper,
Input,
Modal,
// useToast,
} from '@latitude-data/web-ui'
import { createDatasetAction } from '$/actions/datasets/create'
import useLatitudeAction from '$/hooks/useLatitudeAction'
import { useNavigate } from '$/hooks/useNavigate'
import { ROUTES } from '$/services/routes'
import useDatasets from '$/stores/datasets'

export default function NewDataset() {
const data = { name: '' }
const navigate = useNavigate()
const { error, executeFormAction } = useLatitudeAction(createDatasetAction)
const errors = error?.fieldErrors
const { createError, createFormAction } = useDatasets({
onCreateSuccess: () => navigate.push(ROUTES.datasets.root),
})
const errors = createError?.fieldErrors
return (
<Modal
open
Expand All @@ -37,7 +37,7 @@ export default function NewDataset() {
<form
className='min-w-0'
id='createDatasetForm'
action={executeFormAction}
action={createFormAction}
>
<FormWrapper>
<Input
Expand All @@ -49,12 +49,13 @@ export default function NewDataset() {
placeholder='Amazing dataset'
/>
<DropzoneInput
multiple={false}
accept='.csv'
label='Upload dataset'
name='dataset_file'
errors={errors?.dataset_file}
placeholder='Upload csv'
multiple={false}
accept='.csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel'
description='The first line of the uploaded .csv will be used as headers. The delimiter symbol must be ";"'
/>
</FormWrapper>
</form>
Expand Down
31 changes: 17 additions & 14 deletions apps/web/src/components/modals/DestroyModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,31 @@ import {
TAnyZodSafeFunctionHandler,
} from 'zsa'

export default function DestroyModal({
action,
onSuccess,
onOpenChange,
title,
description,
submitStr,
model,
}: {
type Props<TServerAction extends TAnyZodSafeFunctionHandler> = {
action: (
data: inferServerActionInput<TAnyZodSafeFunctionHandler>,
data: inferServerActionInput<TServerAction>,
) => Promise<
| [inferServerActionReturnData<TAnyZodSafeFunctionHandler>, null]
| [null, inferServerActionError<TAnyZodSafeFunctionHandler>]
| [inferServerActionReturnData<TServerAction>, null]
| [null, inferServerActionError<TServerAction>]
>
onSuccess?: (payload: any) => void
onSuccess?: (payload: inferServerActionReturnData<TServerAction>) => void
onOpenChange?: (open: boolean) => void
title: string
description: string
submitStr: string
model: { id: string | number }
}) {
}
export default function DestroyModal<
TServerAction extends TAnyZodSafeFunctionHandler,
>({
action,
onSuccess,
onOpenChange,
title,
description,
submitStr,
model,
}: Props<TServerAction>) {
const { toast } = useToast()
const { action: actionFn } = useFormAction(action, {
onError: (error) => {
Expand Down
Loading

0 comments on commit bc5aee9

Please sign in to comment.