editable schedule name, #101

Signed-off-by: FTCHD <144691102+FTCHD@users.noreply.github.com>
This commit is contained in:
FTCHD
2025-12-29 19:06:47 +07:00
parent 625ce53b26
commit c4a442953a
8 changed files with 131 additions and 7 deletions
+12
View File
@@ -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: {
+12
View File
@@ -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: {
+12
View File
@@ -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: {
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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: {
+1
View File
@@ -9,6 +9,7 @@ import type {
export type ScheduledTask = {
id: string
name?: string
cron: string
isRunning: boolean
isEnabled: boolean