This commit is contained in:
2026-03-22 13:55:23 +08:00
parent c3f196ded4
commit 69099986e0
616 changed files with 38942 additions and 3 deletions

View File

@@ -0,0 +1,80 @@
import type { ColumnDef } from '@tanstack/vue-table'
import { h } from 'vue'
import { DataTableColumnHeader, SelectColumn } from '@/components/data-table'
import { Badge } from '@/components/ui/badge'
import type { Task } from '../data/schema'
import { labels, priorities, statuses } from '../data/data'
import DataTableRowActions from './data-table-row-actions.vue'
export const columns: ColumnDef<Task>[] = [
SelectColumn as ColumnDef<Task>,
{
accessorKey: 'id',
header: ({ column }) => h(DataTableColumnHeader<Task>, { column, title: 'Task' }),
cell: ({ row }) => h('div', { class: 'w-20' }, row.getValue('id')),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'title',
header: ({ column }) => h(DataTableColumnHeader<Task>, { column, title: 'Title' }),
cell: ({ row }) => {
const label = labels.find(label => label.value === row.original.label)
return h('div', { class: 'flex space-x-2' }, [
label ? h(Badge, { variant: 'outline' }, () => label.label) : null,
h('span', { class: 'max-w-[500px] truncate font-medium' }, row.getValue('title')),
])
},
},
{
accessorKey: 'status',
header: ({ column }) => h(DataTableColumnHeader<Task>, { column, title: 'Status' }),
cell: ({ row }) => {
const status = statuses.find(
status => status.value === row.getValue('status'),
)
if (!status)
return null
return h('div', { class: 'flex w-[100px] items-center' }, [
status.icon && h(status.icon, { class: 'mr-2 h-4 w-4 text-muted-foreground' }),
h('span', status.label),
])
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: 'priority',
header: ({ column }) => h(DataTableColumnHeader<Task>, { column, title: 'Priority' }),
cell: ({ row }) => {
const priority = priorities.find(
priority => priority.value === row.getValue('priority'),
)
if (!priority)
return null
return h('div', { class: 'flex items-center' }, [
priority.icon && h(priority.icon, { class: 'mr-2 h-4 w-4 text-muted-foreground' }),
h('span', {}, priority.label),
])
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
id: 'actions',
cell: ({ row }) => h(DataTableRowActions, { row }),
},
]

View File

@@ -0,0 +1,97 @@
<script setup lang="ts">
import type { Row } from '@tanstack/vue-table'
import type { Component } from 'vue'
import { Ellipsis, FilePenLine, Trash2 } from 'lucide-vue-next'
import { Modal, ModalContent } from '@/components/prop-ui/modal'
import type { Task } from '../data/schema'
import { labels } from '../data/data'
import { taskSchema } from '../data/schema'
const props = defineProps<DataTableRowActionsProps>()
interface DataTableRowActionsProps {
row: Row<Task>
}
const task = computed(() => taskSchema.parse(props.row.original))
const taskLabel = ref(task.value.label)
const showComponent = shallowRef<Component | null>(null)
const isOpen = ref(false)
type TCommand = 'edit' | 'create' | 'delete'
const componentLoader: Record<TCommand, () => Promise<{ default: Component }>> = {
edit: () => import('./task-resource-dialog.vue'),
create: () => import('./task-resource-dialog.vue'),
delete: () => import('./task-delete.vue'),
}
async function handleSelect(command: TCommand) {
try {
const { default: component } = await componentLoader[command]()
showComponent.value = component
isOpen.value = true
}
catch (e) {
console.error(`Failed to load component for "${command}"`, e)
}
}
</script>
<template>
<Modal v-model:open="isOpen">
<UiDropdownMenu>
<UiDropdownMenuTrigger as-child>
<UiButton
variant="ghost"
class="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
>
<Ellipsis class="size-4" />
<span class="sr-only">Open menu</span>
</UiButton>
</UiDropdownMenuTrigger>
<UiDropdownMenuContent align="end" class="w-[160px]">
<UiDropdownMenuItem @select.stop="handleSelect('edit')">
<span>Edit</span>
<UiDropdownMenuShortcut> <FilePenLine class="size-4" /> </UiDropdownMenuShortcut>
</UiDropdownMenuItem>
<UiDropdownMenuItem disabled>
Make a copy
</UiDropdownMenuItem>
<UiDropdownMenuItem disabled>
Favorite
</UiDropdownMenuItem>
<UiDropdownMenuSeparator />
<UiDropdownMenuSub>
<UiDropdownMenuSubTrigger>Labels</UiDropdownMenuSubTrigger>
<UiDropdownMenuSubContent>
<UiDropdownMenuRadioGroup v-model="taskLabel">
<UiDropdownMenuRadioItem v-for="label in labels" :key="label.value" :value="label.value">
{{ label.label }}
</UiDropdownMenuRadioItem>
</UiDropdownMenuRadioGroup>
</UiDropdownMenuSubContent>
</UiDropdownMenuSub>
<UiDropdownMenuSeparator />
<UiDropdownMenuItem @select.stop="handleSelect('delete')">
<span>Delete</span>
<UiDropdownMenuShortcut> <Trash2 class="size-4" /> </UiDropdownMenuShortcut>
</UiDropdownMenuItem>
</UiDropdownMenuContent>
</UiDropdownMenu>
<ModalContent>
<component :is="showComponent" :task="task" @close="isOpen = false" />
</ModalContent>
</Modal>
</template>

View File

@@ -0,0 +1,60 @@
<script setup lang="ts">
import type { Table } from '@tanstack/vue-table'
import { X } from 'lucide-vue-next'
import { DataTableFacetedFilter, DataTableViewOptions } from '@/components/data-table'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import type { Task } from '../data/schema'
import { priorities, statuses } from '../data/data'
interface DataTableToolbarProps {
table: Table<Task>
}
const props = defineProps<DataTableToolbarProps>()
const isFiltered = computed(() => props.table.getState().columnFilters.length > 0)
</script>
<template>
<div class="flex items-center justify-between">
<div class="flex flex-col items-start flex-1 space-y-2 md:items-center md:space-x-2 md:space-y-0 md:flex-row">
<Input
placeholder="Filter tasks..."
:model-value="(table.getColumn('title')?.getFilterValue() as string) ?? ''"
class="h-8 w-[150px] lg:w-[250px]"
@input="table.getColumn('title')?.setFilterValue($event.target.value)"
/>
<div class="space-x-2">
<DataTableFacetedFilter
v-if="table.getColumn('status')"
:column="table.getColumn('status')"
title="Status"
:options="statuses"
/>
<DataTableFacetedFilter
v-if="table.getColumn('priority')"
:column="table.getColumn('priority')"
title="Priority"
:options="priorities"
/>
</div>
<Button
v-if="isFiltered"
variant="ghost"
class="h-8 px-2 lg:px-3"
@click="table.resetColumnFilters()"
>
Reset
<X class="size-4" />
</Button>
</div>
<DataTableViewOptions :table="table" />
</div>
</template>

View File

@@ -0,0 +1,51 @@
<script setup lang="ts">
import { Trash2Icon } from 'lucide-vue-next'
import type { DataTableProps } from '@/components/data-table'
import { DataTable, DataTableBulkActions, useGenerateVueTable } from '@/components/data-table'
import type { Task } from '../data/schema'
import DataTableToolbar from './data-table-toolbar.vue'
import TaskDeleteBatch from './task-delete-batch.vue'
const props = defineProps<DataTableProps<Task>>()
const table = useGenerateVueTable<Task>(props)
const taskDeleteBatchOpen = ref(false)
</script>
<template>
<DataTableBulkActions entity-name="task" :table="table">
<UiTooltip>
<UiTooltipTrigger as-child>
<UiButton
variant="destructive"
size="icon"
class="size-8"
aria-label="Delete selected tasks"
title="Delete selected tasks"
@click="taskDeleteBatchOpen = true"
>
<Trash2Icon />
<span class="sr-only">Delete selected tasks</span>
</UiButton>
</UiTooltipTrigger>
<UiTooltipContent>
<p>Delete selected tasks</p>
</UiTooltipContent>
</UiTooltip>
<TaskDeleteBatch
v-model:open="taskDeleteBatchOpen"
:table
/>
</DataTableBulkActions>
<DataTable :columns :table :data :loading>
<template #toolbar>
<DataTableToolbar :table="table" class="w-full overflow-x-auto" />
</template>
</DataTable>
</template>

View File

@@ -0,0 +1,24 @@
<script lang="ts" setup>
import { Plus } from 'lucide-vue-next'
import { Modal, ModalContent, ModalTrigger } from '@/components/prop-ui/modal'
import TaskResourceDialog from './task-resource-dialog.vue'
const isOpen = ref(false)
</script>
<template>
<Modal v-model:open="isOpen">
<ModalTrigger as-child>
<UiButton>
Create
<Plus />
</UiButton>
</ModalTrigger>
<ModalContent>
<TaskResourceDialog :task="null" @close="isOpen = false" />
</ModalContent>
</Modal>
</template>

View File

@@ -0,0 +1,76 @@
<script lang="ts" setup generic="T = Task">
import type { Table as VueTable } from '@tanstack/vue-table'
import { toast } from 'vue-sonner'
import ConfirmDialog from '@/components/confirm-dialog.vue'
import type { Task } from '../data/schema'
const { table } = defineProps<{
table: VueTable<T>
}>()
const openModel = defineModel<boolean>('open', {
default: false,
})
const CONFIRM_WORD = 'DELETE'
const confirmValue = ref('')
const selectedRows = computed(() => table.getSelectedRowModel().rows)
const selectedCount = computed(() => selectedRows.value.length || 0)
function handleConfirm() {
if (confirmValue.value !== CONFIRM_WORD) {
toast.error(`Please type "${CONFIRM_WORD}" to confirm deletion.`)
return
}
openModel.value = false
toast.promise(new Promise(resolve => setTimeout(resolve, 2000)), {
loading: 'Deleting tasks...',
success: () => {
table.resetRowSelection()
return `Successfully deleted ${selectedRows.value.length} tasks.`
},
error: 'Failed to delete tasks.',
})
}
</script>
<template>
<ConfirmDialog
v-model:open="openModel"
confirm-button-text="Delete"
destructive
:disabled="confirmValue.trim() !== CONFIRM_WORD"
@confirm="handleConfirm"
>
<template #title>
Delete {{ selectedCount }} tasks?
</template>
<template #description>
Are you sure you want to delete the selected tasks? <br>
This action cannot be undone.
</template>
<template #default>
<UiLabel class="my-4 flex flex-col items-start gap-1.5">
<span>Confirm by typing {{ CONFIRM_WORD }}:</span>
<UiInput
v-model="confirmValue"
:placeholder="`Type &quot;${CONFIRM_WORD}&quot; to confirm.`"
/>
</UiLabel>
<UiAlert variant="destructive">
<UiAlertTitle>Warning!</UiAlertTitle>
<UiAlertDescription>
Please be careful, this operation can not be rolled back.
</UiAlertDescription>
</UiAlert>
</template>
</ConfirmDialog>
</template>

View File

@@ -0,0 +1,44 @@
<script lang="ts" setup>
import { toast } from 'vue-sonner'
import { ModalClose, ModalDescription, ModalFooter, ModalHeader, ModalTitle } from '@/components/prop-ui/modal'
import type { Task } from '../data/schema'
const props = defineProps<{
task: Task
}>()
function handleRemove() {
toast(`The following task has been deleted:`, {
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(props.task, null, 2))),
})
}
</script>
<template>
<div>
<ModalHeader>
<ModalTitle>
Delete this task: {{ task.id }} ?
</ModalTitle>
<ModalDescription>
You are about to delete a task with the ID {{ task.id }}. This action cannot be undone.
</ModalDescription>
</ModalHeader>
<ModalFooter>
<ModalClose as-child>
<UiButton variant="outline">
Cancel
</UiButton>
</ModalClose>
<ModalClose as-child>
<UiButton variant="destructive" @click="handleRemove">
Delete
</UiButton>
</ModalClose>
</ModalFooter>
</div>
</template>

View File

@@ -0,0 +1,133 @@
<script lang="ts" setup>
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { toast } from 'vue-sonner'
import { FormField } from '@/components/ui/form'
import type { Task } from '../data/schema'
import type { TaskValidator } from '../validators/task.validator'
import { labels, priorities, statuses } from '../data/data'
import { taskValidator } from '../validators/task.validator'
const props = defineProps<{
task: Task | null
}>()
const emits = defineEmits(['close'])
const formSchema = toTypedSchema(taskValidator)
const initialValues = reactive<TaskValidator>({
title: props.task ? props.task.title : '',
status: props.task ? props.task.status : 'backlog',
label: props.task ? props.task.label : 'feature',
priority: props.task ? props.task.priority : 'medium',
})
const { isFieldDirty, handleSubmit } = useForm({
validationSchema: formSchema,
initialValues,
})
const onSubmit = handleSubmit((values) => {
toast('You submitted the following values:', {
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(values, null, 2))),
})
emits('close')
})
</script>
<template>
<div>
<form class="w-2/3 space-y-6" @submit="onSubmit">
<FormField v-slot="{ componentField }" name="title" :validate-on-blur="!isFieldDirty">
<UiFormItem>
<UiFormLabel>Title</UiFormLabel>
<UiFormControl>
<UiInput type="text" placeholder="shadcn" v-bind="componentField" />
</UiFormControl>
<UiFormDescription />
<UiFormMessage />
</UiFormItem>
</FormField>
<FormField v-slot="{ componentField }" name="status" :validate-on-blur="!isFieldDirty">
<UiFormItem>
<UiFormLabel>status</UiFormLabel>
<UiFormControl>
<UiSelect v-bind="componentField">
<UiSelectTrigger class="w-[180px]">
<UiSelectValue placeholder="Select a status" />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectGroup>
<UiSelectItem v-for="status in statuses" :key="status.value" :value="status.value">
<div class="flex items-center gap-2">
<component :is="status.icon" class="size-4 shrink-0" />
{{ status.label }}
</div>
</UiSelectItem>
</UiSelectGroup>
</UiSelectContent>
</UiSelect>
</UiFormControl>
<UiFormDescription />
<UiFormMessage />
</UiFormItem>
</FormField>
<FormField v-slot="{ componentField }" name="label" :validate-on-blur="!isFieldDirty">
<UiFormItem>
<UiFormLabel>label</UiFormLabel>
<UiFormControl>
<UiRadioGroup
class="flex flex-col space-y-1"
v-bind="componentField"
>
<UiFormItem
v-for="label in labels" :key="label.value"
class="flex items-center space-y-0 gap-x-3"
>
<UiFormControl>
<UiRadioGroupItem :value="label.value" />
</UiFormControl>
<UiFormLabel class="font-normal">
{{ label.label }}
</UiFormLabel>
</UiFormItem>
</UiRadioGroup>
</UiFormControl>
<UiFormDescription />
<UiFormMessage />
</UiFormItem>
</FormField>
<FormField v-slot="{ componentField }" name="priority" :validate-on-blur="!isFieldDirty">
<UiFormItem>
<UiFormLabel>priority</UiFormLabel>
<UiFormControl>
<UiRadioGroup
class="flex flex-col space-y-1"
v-bind="componentField"
>
<UiFormItem
v-for="priority in priorities" :key="priority.value"
class="flex items-center space-y-0 gap-x-3"
>
<UiFormControl>
<UiRadioGroupItem :value="priority.value" />
</UiFormControl>
<UiFormLabel class="font-normal">
{{ priority.label }}
</UiFormLabel>
</UiFormItem>
</UiRadioGroup>
</UiFormControl>
<UiFormDescription />
<UiFormMessage />
</UiFormItem>
</FormField>
<UiButton type="submit">
Submit
</UiButton>
</form>
</div>
</template>

View File

@@ -0,0 +1,68 @@
<script lang="ts" setup>
import { Download } from 'lucide-vue-next'
import { toast } from 'vue-sonner'
import { Modal, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, ModalTrigger } from '@/components/prop-ui/modal'
const isOpen = ref(false)
const file = ref()
const error = ref()
watch(file, () => {
error.value = null
})
watch(isOpen, () => {
file.value = null
})
function onSubmit() {
error.value = null
if (!file.value) {
error.value = 'File is required'
return
}
toast('You submitted the following values:', {
description: h('pre', { class: 'mt-2 w-[340px] rounded-md bg-slate-950 p-4' }, h('code', { class: 'text-white' }, JSON.stringify(file.value, null, 2))),
})
isOpen.value = false
}
</script>
<template>
<Modal v-model:open="isOpen">
<ModalTrigger as-child>
<UiButton variant="outline">
Import
<Download />
</UiButton>
</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>
Import Tasks
</ModalTitle>
<ModalDescription>
Import tasks quickly from a CSV file.
</ModalDescription>
</ModalHeader>
<div class="grid w-full max-w-sm items-center gap-1.5">
<UiLabel>File</UiLabel>
<UiInput id="file" v-model="file" type="file" />
<span v-if="error" class="text-destructive">{{ error }}</span>
</div>
<ModalFooter>
<UiButton variant="secondary" @click="isOpen = false">
Cancel
</UiButton>
<UiButton @click="onSubmit">
Import
</UiButton>
</ModalFooter>
</ModalContent>
</Modal>
</template>

View File

@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { ModalDescription, ModalHeader, ModalTitle } from '@/components/prop-ui/modal'
import type { Task } from '../data/schema'
import TaskForm from './task-form.vue'
const props = defineProps<{
task: Task | null
}>()
defineEmits(['close'])
const task = computed(() => props.task)
const title = computed(() => task.value?.id ? `Edit Task` : 'New Task')
const description = computed(() => task.value?.id ? `Edit task ${task.value.id}` : 'Create new task')
</script>
<template>
<div>
<ModalHeader>
<ModalTitle>
{{ title }}
</ModalTitle>
<ModalDescription>
{{ description }}
</ModalDescription>
</ModalHeader>
<TaskForm class="mt-2" :task="task" @close="$emit('close')" />
</div>
</template>

View File

@@ -0,0 +1,72 @@
import {
ArrowDown,
ArrowRight,
ArrowUp,
Circle,
CircleCheck,
CircleHelp,
CirclePlus,
TimerOff,
} from 'lucide-vue-next'
import { h } from 'vue'
export const labels = [
{
value: 'bug',
label: 'Bug',
},
{
value: 'feature',
label: 'Feature',
},
{
value: 'documentation',
label: 'Documentation',
},
]
export const statuses = [
{
value: 'backlog',
label: 'Backlog',
icon: h(CircleHelp),
},
{
value: 'todo',
label: 'Todo',
icon: h(Circle),
},
{
value: 'in progress',
label: 'In Progress',
icon: h(TimerOff),
},
{
value: 'done',
label: 'Done',
icon: h(CircleCheck),
},
{
value: 'canceled',
label: 'Canceled',
icon: h(CirclePlus),
},
]
export const priorities = [
{
value: 'low',
label: 'Low',
icon: h(ArrowDown),
},
{
value: 'medium',
label: 'Medium',
icon: h(ArrowRight),
},
{
value: 'high',
label: 'High',
icon: h(ArrowUp),
},
]

View File

@@ -0,0 +1,13 @@
import { z } from 'zod'
// We're keeping a simple non-relational schema here.
// IRL, you will have a schema for your data models.
export const taskSchema = z.object({
id: z.string(),
title: z.string(),
status: z.string(),
label: z.string(),
priority: z.string(),
})
export type Task = z.infer<typeof taskSchema>

View File

@@ -0,0 +1,702 @@
[
{
"id": "TASK-8782",
"title": "You can't compress the program without quantifying the open-source SSD pixel!",
"status": "in progress",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-7878",
"title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!",
"status": "backlog",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-7839",
"title": "We need to bypass the neural TCP card!",
"status": "todo",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-5562",
"title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!",
"status": "backlog",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-8686",
"title": "I'll parse the wireless SSL protocol, that should driver the API panel!",
"status": "canceled",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-1280",
"title": "Use the digital TLS panel, then you can transmit the haptic system!",
"status": "done",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-7262",
"title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!",
"status": "done",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-1138",
"title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-7184",
"title": "We need to program the back-end THX pixel!",
"status": "todo",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-5160",
"title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!",
"status": "in progress",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-5618",
"title": "Generating the driver won't do anything, we need to index the online SSL application!",
"status": "done",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-6699",
"title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!",
"status": "backlog",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-2858",
"title": "We need to override the online UDP bus!",
"status": "backlog",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-9864",
"title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!",
"status": "done",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-8404",
"title": "We need to generate the virtual HEX alarm!",
"status": "in progress",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-5365",
"title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!",
"status": "in progress",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1780",
"title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!",
"status": "todo",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-6938",
"title": "Use the redundant SCSI application, then you can hack the optical alarm!",
"status": "todo",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-9885",
"title": "We need to compress the auxiliary VGA driver!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-3216",
"title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!",
"status": "backlog",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-9285",
"title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!",
"status": "todo",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-1024",
"title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!",
"status": "in progress",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-7068",
"title": "You can't generate the capacitor without indexing the wireless HEX pixel!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-6502",
"title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!",
"status": "todo",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-5326",
"title": "We need to hack the redundant UTF8 transmitter!",
"status": "todo",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-6274",
"title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1571",
"title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-9518",
"title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!",
"status": "canceled",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-5581",
"title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!",
"status": "backlog",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-2197",
"title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!",
"status": "todo",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-8484",
"title": "We need to parse the solid state UDP firewall!",
"status": "in progress",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-9892",
"title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!",
"status": "done",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-9616",
"title": "We need to synthesize the cross-platform ASCII pixel!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-9744",
"title": "Use the back-end IP card, then you can input the solid state hard drive!",
"status": "done",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1376",
"title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!",
"status": "backlog",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-7382",
"title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!",
"status": "todo",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-2290",
"title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!",
"status": "canceled",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-1533",
"title": "You can't input the firewall without overriding the wireless TCP firewall!",
"status": "done",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-4920",
"title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!",
"status": "in progress",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-5168",
"title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-7103",
"title": "We need to parse the multi-byte EXE bandwidth!",
"status": "canceled",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-4314",
"title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!",
"status": "in progress",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-3415",
"title": "Use the cross-platform XML application, then you can quantify the solid state feed!",
"status": "todo",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-8339",
"title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6995",
"title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!",
"status": "todo",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-8053",
"title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!",
"status": "todo",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-4336",
"title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!",
"status": "todo",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-8790",
"title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!",
"status": "done",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-8980",
"title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-7342",
"title": "Use the neural CLI card, then you can parse the online port!",
"status": "backlog",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-5608",
"title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1606",
"title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!",
"status": "done",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-7872",
"title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!",
"status": "canceled",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-4167",
"title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!",
"status": "canceled",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-9581",
"title": "You can't index the port without hacking the cross-platform XSS monitor!",
"status": "backlog",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-8806",
"title": "We need to bypass the back-end SSL panel!",
"status": "done",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-6542",
"title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!",
"status": "done",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6806",
"title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-9549",
"title": "You can't bypass the bus without connecting the neural JBOD bus!",
"status": "todo",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-1075",
"title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!",
"status": "done",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-1427",
"title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!",
"status": "done",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-1907",
"title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!",
"status": "todo",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-4309",
"title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!",
"status": "backlog",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3973",
"title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!",
"status": "todo",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-7962",
"title": "Use the wireless RAM program, then you can hack the cross-platform feed!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-3360",
"title": "You can't quantify the program without synthesizing the neural OCR interface!",
"status": "done",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-9887",
"title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!",
"status": "backlog",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3649",
"title": "I'll input the virtual USB system, that should circuit the DNS monitor!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-3586",
"title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!",
"status": "in progress",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-5150",
"title": "I'll hack the wireless XSS port, that should transmitter the IP interface!",
"status": "canceled",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-3652",
"title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!",
"status": "backlog",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6884",
"title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!",
"status": "canceled",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-1591",
"title": "We need to connect the mobile XSS driver!",
"status": "in progress",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-3802",
"title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-7253",
"title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-9739",
"title": "We need to hack the multi-byte HDD bus!",
"status": "done",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-4424",
"title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!",
"status": "in progress",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-3922",
"title": "You can't back up the capacitor without generating the wireless PCI program!",
"status": "backlog",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-4921",
"title": "I'll index the open-source IP feed, that should system the GB application!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-5814",
"title": "We need to calculate the 1080p AGP feed!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-2645",
"title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!",
"status": "todo",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-4535",
"title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-4463",
"title": "We need to copy the solid state AGP monitor!",
"status": "done",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-9745",
"title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!",
"status": "canceled",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-2080",
"title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!",
"status": "todo",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3838",
"title": "I'll bypass the online TCP application, that should panel the AGP system!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-1340",
"title": "We need to navigate the virtual PNG circuit!",
"status": "todo",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-6665",
"title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!",
"status": "canceled",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-7585",
"title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!",
"status": "backlog",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6319",
"title": "We need to copy the multi-byte SCSI program!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-4369",
"title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-9035",
"title": "We need to override the solid state PNG array!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-3970",
"title": "You can't index the transmitter without quantifying the haptic ASCII card!",
"status": "todo",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-4473",
"title": "You can't bypass the protocol without overriding the neural RSS program!",
"status": "todo",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-4136",
"title": "You can't hack the hard drive without hacking the primary JSON program!",
"status": "canceled",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3939",
"title": "Use the back-end SQL firewall, then you can connect the neural hard drive!",
"status": "done",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-2007",
"title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-7516",
"title": "Use the primary SQL program, then you can generate the auxiliary transmitter!",
"status": "done",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-6906",
"title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!",
"status": "done",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-5207",
"title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!",
"status": "in progress",
"label": "bug",
"priority": "low"
}
]

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import { BasicPage } from '@/components/global-layout'
import { columns } from './components/columns'
import DataTable from './components/data-table.vue'
import TaskCreate from './components/task-create.vue'
import TaskImport from './components/task-import.vue'
import tasks from './data/tasks.json'
</script>
<template>
<BasicPage
title="Tasks"
description="Tasks description"
sticky
>
<template #actions>
<TaskImport />
<TaskCreate />
</template>
<div class="overflow-x-auto">
<DataTable :data="tasks" :columns="columns" />
</div>
</BasicPage>
</template>

View File

@@ -0,0 +1,10 @@
import z from 'zod'
export const taskValidator = z.object({
title: z.string().min(2).max(50),
status: z.string(),
label: z.string(),
priority: z.string(),
})
export type TaskValidator = z.infer<typeof taskValidator>