editable schedule name, #101
Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
Tooltip,
|
||||
} from '@heroui/react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
@@ -134,7 +135,18 @@ export default function Bisync() {
|
||||
throw new Error('Invalid cron expression')
|
||||
}
|
||||
|
||||
const name = await invoke<string | null>('prompt', {
|
||||
title: 'Schedule Name',
|
||||
message: 'Enter a name for this schedule',
|
||||
default: `New Schedule ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: '2-digit' })}`,
|
||||
})
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Schedule name is required')
|
||||
}
|
||||
|
||||
useHostStore.getState().addScheduledTask({
|
||||
name,
|
||||
operation: 'bisync',
|
||||
cron: cronExpression,
|
||||
args: {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Tooltip,
|
||||
} from '@heroui/react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
@@ -136,7 +137,18 @@ export default function Copy() {
|
||||
throw new Error('Invalid cron expression')
|
||||
}
|
||||
|
||||
const name = await invoke<string | null>('prompt', {
|
||||
title: 'Schedule Name',
|
||||
message: 'Enter a name for this schedule',
|
||||
default: `New Schedule ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: '2-digit' })}`,
|
||||
})
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Schedule name is required')
|
||||
}
|
||||
|
||||
useHostStore.getState().addScheduledTask({
|
||||
name,
|
||||
operation: 'copy',
|
||||
cron: cronExpression,
|
||||
args: {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '@heroui/react'
|
||||
import * as Sentry from '@sentry/browser'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
@@ -159,7 +160,18 @@ export default function Delete() {
|
||||
throw new Error('Invalid cron expression')
|
||||
}
|
||||
|
||||
const name = await invoke<string | null>('prompt', {
|
||||
title: 'Schedule Name',
|
||||
message: 'Enter a name for this schedule',
|
||||
default: `New Schedule ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: '2-digit' })}`,
|
||||
})
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Schedule name is required')
|
||||
}
|
||||
|
||||
useHostStore.getState().addScheduledTask({
|
||||
name,
|
||||
operation: 'delete',
|
||||
cron: cronExpression,
|
||||
args: {
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Tooltip,
|
||||
} from '@heroui/react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
@@ -163,7 +164,18 @@ export default function Move() {
|
||||
throw new Error('Invalid cron expression')
|
||||
}
|
||||
|
||||
const name = await invoke<string | null>('prompt', {
|
||||
title: 'Schedule Name',
|
||||
message: 'Enter a name for this schedule',
|
||||
default: `New Schedule ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: '2-digit' })}`,
|
||||
})
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Schedule name is required')
|
||||
}
|
||||
|
||||
useHostStore.getState().addScheduledTask({
|
||||
name,
|
||||
operation: 'move',
|
||||
cron: cronExpression,
|
||||
args: {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from '@heroui/react'
|
||||
import * as Sentry from '@sentry/browser'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
@@ -95,7 +96,18 @@ export default function Purge() {
|
||||
throw new Error('Invalid cron expression')
|
||||
}
|
||||
|
||||
const name = await invoke<string | null>('prompt', {
|
||||
title: 'Schedule Name',
|
||||
message: 'Enter a name for this schedule',
|
||||
default: `New Schedule ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: '2-digit' })}`,
|
||||
})
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Schedule name is required')
|
||||
}
|
||||
|
||||
useHostStore.getState().addScheduledTask({
|
||||
name,
|
||||
operation: 'purge',
|
||||
cron: cronExpression,
|
||||
args: {
|
||||
|
||||
+58
-7
@@ -1,4 +1,4 @@
|
||||
import { Card, CardBody, CardHeader, Tooltip } from '@heroui/react'
|
||||
import { Card, CardBody, CardHeader, Input, Tooltip } from '@heroui/react'
|
||||
import { Button, Chip } from '@heroui/react'
|
||||
import { ask } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
@@ -6,7 +6,7 @@ import CronExpressionParser from 'cron-parser'
|
||||
import cronstrue from 'cronstrue'
|
||||
import { formatDistance } from 'date-fns'
|
||||
import { AlertCircleIcon, Clock7Icon, PauseIcon, PlayIcon, Trash2Icon } from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { buildReadablePath } from '../../lib/format'
|
||||
import { useHostStore } from '../../store/host'
|
||||
import type { ScheduledTask } from '../../types/schedules'
|
||||
@@ -19,8 +19,7 @@ export default function Schedules() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-screen gap-8">
|
||||
<h1 className="text-2xl font-bold text-center">
|
||||
You can schedule tasks to run later. Schedule one below and keep the UI running
|
||||
in the background.
|
||||
You can schedule tasks to run later, when the UI is in the background.
|
||||
</h1>
|
||||
<CommandsDropdown title="New scheduled task" />
|
||||
</div>
|
||||
@@ -41,10 +40,18 @@ export default function Schedules() {
|
||||
|
||||
function TaskCard({ task }: { task: ScheduledTask }) {
|
||||
const [isBusy, setIsBusy] = useState(false)
|
||||
const [isEditingName, setIsEditingName] = useState(false)
|
||||
const [editingName, setEditingName] = useState(task.name)
|
||||
|
||||
const removeScheduledTask = useHostStore((state) => state.removeScheduledTask)
|
||||
const updateScheduledTask = useHostStore((state) => state.updateScheduledTask)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditingName) {
|
||||
setEditingName(task.name)
|
||||
}
|
||||
}, [task.name, isEditingName])
|
||||
|
||||
const parsed = useMemo(() => CronExpressionParser.parse(task.cron), [task.cron])
|
||||
const nextRun = useMemo(() => (parsed.hasNext() ? parsed.next().toDate() : null), [parsed])
|
||||
|
||||
@@ -107,10 +114,54 @@ function TaskCard({ task }: { task: ScheduledTask }) {
|
||||
{task.operation.toUpperCase()}
|
||||
</Chip>
|
||||
<div className="flex flex-col gap-0">
|
||||
<div className="text-sm font-bold">
|
||||
{buildReadablePath(source, 'short')}
|
||||
</div>
|
||||
<Tooltip
|
||||
content="Tap to edit the name"
|
||||
placement="bottom"
|
||||
size="lg"
|
||||
color="foreground"
|
||||
>
|
||||
<Input
|
||||
size="sm"
|
||||
value={
|
||||
isEditingName
|
||||
? editingName
|
||||
: task.name || 'Untitled Schedule'
|
||||
}
|
||||
variant="bordered"
|
||||
isReadOnly={!isEditingName}
|
||||
classNames={{
|
||||
'input': 'font-bold',
|
||||
'inputWrapper': 'p-0 border-0 min-h-0 h-full w-64',
|
||||
}}
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
spellCheck="false"
|
||||
onClick={(e) => {
|
||||
setEditingName(task.name || 'Untitled Schedule')
|
||||
setIsEditingName(true)
|
||||
e.currentTarget.select()
|
||||
}}
|
||||
onBlur={() => {
|
||||
updateScheduledTask(task.id, { name: editingName })
|
||||
setIsEditingName(false)
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
updateScheduledTask(task.id, { name: editingName })
|
||||
setIsEditingName(false)
|
||||
e.currentTarget.blur()
|
||||
} else if (e.key === 'Escape') {
|
||||
setEditingName(task.name)
|
||||
setIsEditingName(false)
|
||||
e.currentTarget.blur()
|
||||
}
|
||||
}}
|
||||
onValueChange={(newName) => setEditingName(newName)}
|
||||
/>
|
||||
</Tooltip>
|
||||
<div className="text-sm text-gray-500">
|
||||
{buildReadablePath(source, 'short')} {'→'}{' '}
|
||||
{'destination' in task.args
|
||||
? buildReadablePath(task.args.destination, 'short')
|
||||
: 'N/A'}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from '@heroui/react'
|
||||
import * as Sentry from '@sentry/browser'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { message } from '@tauri-apps/plugin-dialog'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import cronstrue from 'cronstrue'
|
||||
@@ -130,7 +131,18 @@ export default function Sync() {
|
||||
throw new Error('Invalid cron expression')
|
||||
}
|
||||
|
||||
const name = await invoke<string | null>('prompt', {
|
||||
title: 'Schedule Name',
|
||||
message: 'Enter a name for this schedule',
|
||||
default: `New Schedule ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: '2-digit' })}`,
|
||||
})
|
||||
|
||||
if (!name) {
|
||||
throw new Error('Schedule name is required')
|
||||
}
|
||||
|
||||
useHostStore.getState().addScheduledTask({
|
||||
name,
|
||||
operation: 'sync',
|
||||
cron: cronExpression,
|
||||
args: {
|
||||
|
||||
Vendored
+1
@@ -9,6 +9,7 @@ import type {
|
||||
|
||||
export type ScheduledTask = {
|
||||
id: string
|
||||
name?: string
|
||||
cron: string
|
||||
isRunning: boolean
|
||||
isEnabled: boolean
|
||||
|
||||
Reference in New Issue
Block a user