Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
*.db
dbschema.sql
*.db-shm
*.db-wal
.data

# Nuxt dev/build outputs
.output
Expand Down
9 changes: 8 additions & 1 deletion src/app.vue → app/app.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import 'vue-sonner/style.css'
import { Toaster } from 'vue-sonner'
import Header from '~/components/header.vue'
import Header from './components/header.vue'
</script>
<template>
<Header />
Expand All @@ -11,3 +12,9 @@ import Header from '~/components/header.vue'
<Toaster />
</ClientOnly>
</template>

<style>
* {
font-family: 'Inter', sans-serif;
}
</style>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { TaskState } from '~/types'
import type { TaskState } from '~~/shared/types'
const props = defineProps<{
state: TaskState
Expand Down
61 changes: 51 additions & 10 deletions src/components/tasks-table.vue → app/components/tasks-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
formatReturnValue,
formatDate
} from '~/lib/utils'
import type { TaskSelect } from '~/server/db/schema'
import type { TaskSelect } from '~~/shared/db/schema'
import {
Table,
TableBody,
Expand All @@ -15,16 +15,19 @@ import {
TableHead,
TableHeader
} from '~/components/ui/table'
import {
ArrowDownIcon,
ArrowUpIcon,
CopyIcon,
ArrowUpDownIcon
} from 'lucide-vue-next'
import { CopyIcon, Maximize2Icon } from 'lucide-vue-next'
import SortableArrow from '~/components/sortable-arrow.vue'
import { toast } from 'vue-sonner'
import type { QueryParams } from '~/types'
import type { QueryParams } from '~~/shared/types'
import { inject } from 'vue'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger
} from '~/components/ui/dialog'

const { data } = defineProps<{
data?: {
Expand Down Expand Up @@ -85,6 +88,7 @@ const searchHandler: ((value: string) => void) | undefined =
<TableHead> Args </TableHead>
<TableHead> Kwargs </TableHead>
<TableHead> Result </TableHead>
<TableHead class="text-center"> Error </TableHead>
<TableHead>
<div class="flex justify-between items-center gap-2">
<span>Queued At</span>
Expand Down Expand Up @@ -130,7 +134,7 @@ const searchHandler: ((value: string) => void) | undefined =
<TableCell class="underline">
<div class="flex justify-between items-center gap-1">
<NuxtLink :to="{ name: 'tasks-id', params: { id: task.id } }">{{
limitText(task.id, 15)
limitText(task.id, 13)
}}</NuxtLink>
<CopyIcon
:size="15"
Expand All @@ -146,8 +150,45 @@ const searchHandler: ((value: string) => void) | undefined =
/>
</TableCell>
<TableCell>{{ task.args }}</TableCell>
<TableCell>{{ limitText(JSON.stringify(task.kwargs), 30) }}</TableCell>
<TableCell>{{ limitText(JSON.stringify(task.kwargs), 25) }}</TableCell>
<TableCell>{{ limitText(formatReturnValue(task), 10) }}</TableCell>
<TableCell class="text-center">
<Dialog v-if="task.state === 'failure' && task.error">
<DialogTrigger as-child>
<button
type="button"
class="inline-flex items-center rounded border border-border p-1 text-muted-foreground transition hover:text-foreground cursor-pointer"
aria-label="View full error"
>
<Maximize2Icon :size="12" />
<span class="sr-only">View error details</span>
</button>
</DialogTrigger>
<DialogContent class="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle>Error details</DialogTitle>
<DialogDescription>
{{ formatTaskName(task.name) }} •
<NuxtLink
class="text-primary underline"
:to="{ name: 'tasks-id', params: { id: task.id } }"
>{{ task.id }}</NuxtLink
>
</DialogDescription>
</DialogHeader>
<pre
class="max-h-[60vh] overflow-auto rounded bg-muted p-4 text-left text-sm leading-relaxed"
>{{ task.error.trim() }}</pre
>
</DialogContent>
</Dialog>
<span
v-else
class="text-muted-foreground"
>
</span>
</TableCell>
<TableCell>{{ formatDate(String(task.queuedAt)) }}</TableCell>
<TableCell>{{
task.startedAt ? formatDate(String(task.startedAt)) : null
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import type { ButtonVariants } from '.'
import { Primitive } from 'reka-ui'
import { cn } from '@/lib/utils'
import { Primitive, type PrimitiveProps } from 'reka-ui'
import { type ButtonVariants, buttonVariants } from '.'
import { buttonVariants } from '.'
interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant']
Expand All @@ -11,7 +13,7 @@ interface Props extends PrimitiveProps {
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
as: 'button'
})
</script>

Expand Down
37 changes: 37 additions & 0 deletions app/components/ui/button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { VariantProps } from 'class-variance-authority'
import { cva } from 'class-variance-authority'

export { default as Button } from './Button.vue'

export const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline:
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost:
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: 'text-primary underline-offset-4 hover:underline'
},
size: {
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
icon: 'size-9',
'icon-sm': 'size-8',
'icon-lg': 'size-10'
}
},
defaultVariants: {
variant: 'default',
size: 'default'
}
}
)
export type ButtonVariants = VariantProps<typeof buttonVariants>
File renamed without changes.
File renamed without changes.
34 changes: 34 additions & 0 deletions app/components/ui/checkbox/Checkbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script setup lang="ts">
import type { CheckboxRootEmits, CheckboxRootProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core"
import { Check } from "lucide-vue-next"
import { CheckboxIndicator, CheckboxRoot, useForwardPropsEmits } from "reka-ui"
import { cn } from "@/lib/utils"
const props = defineProps<CheckboxRootProps & { class?: HTMLAttributes["class"] }>()
const emits = defineEmits<CheckboxRootEmits>()
const delegatedProps = reactiveOmit(props, "class")
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
<CheckboxRoot
data-slot="checkbox"
v-bind="forwarded"
:class="
cn('peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
props.class)"
>
<CheckboxIndicator
data-slot="checkbox-indicator"
class="grid place-content-center text-current transition-none"
>
<slot>
<Check class="size-3.5" />
</slot>
</CheckboxIndicator>
</CheckboxRoot>
</template>
1 change: 1 addition & 0 deletions app/components/ui/checkbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Checkbox } from "./Checkbox.vue"
19 changes: 19 additions & 0 deletions app/components/ui/dialog/Dialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup lang="ts">
import type { DialogRootEmits, DialogRootProps } from "reka-ui"
import { DialogRoot, useForwardPropsEmits } from "reka-ui"

const props = defineProps<DialogRootProps>()
const emits = defineEmits<DialogRootEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
<DialogRoot
v-slot="slotProps"
data-slot="dialog"
v-bind="forwarded"
>
<slot v-bind="slotProps" />
</DialogRoot>
</template>
15 changes: 15 additions & 0 deletions app/components/ui/dialog/DialogClose.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { DialogCloseProps } from "reka-ui"
import { DialogClose } from "reka-ui"
const props = defineProps<DialogCloseProps>()
</script>

<template>
<DialogClose
data-slot="dialog-close"
v-bind="props"
>
<slot />
</DialogClose>
</template>
53 changes: 53 additions & 0 deletions app/components/ui/dialog/DialogContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script setup lang="ts">
import type { DialogContentEmits, DialogContentProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core"
import { X } from "lucide-vue-next"
import {
DialogClose,
DialogContent,
DialogPortal,
useForwardPropsEmits,
} from "reka-ui"
import { cn } from "@/lib/utils"
import DialogOverlay from "./DialogOverlay.vue"
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<DialogContentProps & { class?: HTMLAttributes["class"], showCloseButton?: boolean }>(), {
showCloseButton: true,
})
const emits = defineEmits<DialogContentEmits>()
const delegatedProps = reactiveOmit(props, "class")
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
<DialogPortal>
<DialogOverlay />
<DialogContent
data-slot="dialog-content"
v-bind="{ ...$attrs, ...forwarded }"
:class="
cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
props.class,
)"
>
<slot />

<DialogClose
v-if="showCloseButton"
data-slot="dialog-close"
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<X />
<span class="sr-only">Close</span>
</DialogClose>
</DialogContent>
</DialogPortal>
</template>
23 changes: 23 additions & 0 deletions app/components/ui/dialog/DialogDescription.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import type { DialogDescriptionProps } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { reactiveOmit } from "@vueuse/core"
import { DialogDescription, useForwardProps } from "reka-ui"
import { cn } from "@/lib/utils"
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes["class"] }>()
const delegatedProps = reactiveOmit(props, "class")
const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
<DialogDescription
data-slot="dialog-description"
v-bind="forwardedProps"
:class="cn('text-muted-foreground text-sm', props.class)"
>
<slot />
</DialogDescription>
</template>
15 changes: 15 additions & 0 deletions app/components/ui/dialog/DialogFooter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
<div
data-slot="dialog-footer"
:class="cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)"
>
<slot />
</div>
</template>
17 changes: 17 additions & 0 deletions app/components/ui/dialog/DialogHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
}>()
</script>

<template>
<div
data-slot="dialog-header"
:class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)"
>
<slot />
</div>
</template>
Loading