|
@@ -19,9 +19,10 @@ import Row from '@/components/Row'
|
|
|
import SaveButton from '@/components/button/SaveButton'
|
|
|
import SearchForm from '@/components/SearchForm'
|
|
|
import SortableRow from '@/components/SortableRow'
|
|
|
+import type { SubmitHandler } from 'react-hook-form'
|
|
|
import api from '@/api'
|
|
|
import { useForm } from 'react-hook-form'
|
|
|
-import { CheckCircleIcon, CloudIcon, XCircleIcon } from '@heroicons/react/20/solid'
|
|
|
+import { CheckCircleIcon, CloudIcon, XCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
|
|
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
|
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
|
import { useConnection, useRouteSearch, useSession } from '@/hooks'
|
|
@@ -31,6 +32,8 @@ interface HerdUpdateFormData extends Pick<api.Herd, 'name'> {}
|
|
|
|
|
|
interface TaskCreateFormData extends Pick<api.Task, 'description'> {}
|
|
|
|
|
|
+interface TaskUpdateFormData extends Pick<api.Task, 'description'> {}
|
|
|
+
|
|
|
function useHerdUpdateForm() {
|
|
|
const form = useForm<HerdUpdateFormData>({ mode: 'onBlur' })
|
|
|
|
|
@@ -55,19 +58,33 @@ function useTaskCreateForm() {
|
|
|
return { ...form, inputs }
|
|
|
}
|
|
|
|
|
|
+function useTaskUpdateForm() {
|
|
|
+ const form = useForm<TaskUpdateFormData>({ mode: 'onBlur' })
|
|
|
+
|
|
|
+ const inputs = {
|
|
|
+ description: form.register('description', { validate: value => {
|
|
|
+ if (value.length < 1) return 'Required'
|
|
|
+ }}),
|
|
|
+ }
|
|
|
+
|
|
|
+ return { ...form, inputs }
|
|
|
+}
|
|
|
+
|
|
|
export default function HerdView() {
|
|
|
const { account } = useSession()
|
|
|
- const { id } = useParams()
|
|
|
const createTaskForm = useTaskCreateForm()
|
|
|
- const updateHerdForm = useHerdUpdateForm()
|
|
|
+ const { id } = useParams()
|
|
|
const navigate = useNavigate()
|
|
|
const { options } = useConnection()
|
|
|
+ const updateHerdForm = useHerdUpdateForm()
|
|
|
+ const updateTaskForm = useTaskUpdateForm()
|
|
|
const { limit, page, searchParams, setPage } = useRouteSearch()
|
|
|
|
|
|
const [data, setData] = useState<api.GetHerdResponse>()
|
|
|
const [taskData, setTaskData] = useState<api.SearchResponse<api.GetTaskResponse>>()
|
|
|
|
|
|
const [busy, setBusy] = useState(false)
|
|
|
+ const [editing, setEditing] = useState<string>()
|
|
|
const [error, setError] = useState<Error>()
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
|
@@ -199,6 +216,15 @@ export default function HerdView() {
|
|
|
}
|
|
|
}, [id, options, searchParams])
|
|
|
|
|
|
+ function setTaskToEdit(task?: api.WithId<api.Task>) {
|
|
|
+ if (task) {
|
|
|
+ setEditing(task._id)
|
|
|
+ updateTaskForm.reset({ description: task.description })
|
|
|
+ } else {
|
|
|
+ setEditing(undefined)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
async function updateHerd(data: HerdUpdateFormData) {
|
|
|
if (busy) return
|
|
|
|
|
@@ -216,6 +242,27 @@ export default function HerdView() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ function updateTask(task: api.WithId<api.Task>): SubmitHandler<TaskUpdateFormData> {
|
|
|
+ return async function(data) {
|
|
|
+ if (busy) return
|
|
|
+
|
|
|
+ try {
|
|
|
+ setBusy(true)
|
|
|
+ setError(undefined)
|
|
|
+ const update = await api.updateTask(options, task._id, { task: data })
|
|
|
+ const inPageTask = taskData?.results.find(({ task }) => task._id === update.task._id)
|
|
|
+ if (inPageTask) {
|
|
|
+ inPageTask.task.description = update.task.description
|
|
|
+ }
|
|
|
+ setEditing(undefined)
|
|
|
+ } catch (err) {
|
|
|
+ setError(err as Error)
|
|
|
+ } finally {
|
|
|
+ setBusy(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
reload()
|
|
|
}, [reload])
|
|
@@ -270,7 +317,26 @@ export default function HerdView() {
|
|
|
{taskData.results.map(({ task }, i) => (
|
|
|
<SortableRow key={task._id} id={task._id} className={`task ${task.done ? 'done' : 'not-done'}`} disabled={disableSorting}>
|
|
|
<div className="position">{i + 1 + ((page - 1) * limit)}</div>
|
|
|
- <div className="description">{task.description}</div>
|
|
|
+ <div className="description">
|
|
|
+ {editing === task._id ? (
|
|
|
+ <form onSubmit={updateTaskForm.handleSubmit(updateTask(task))}>
|
|
|
+ <Row className="edit-task">
|
|
|
+ <FormInput>
|
|
|
+ <input type="text" autoFocus {...updateTaskForm.inputs.description} />
|
|
|
+ </FormInput>
|
|
|
+ <ButtonSet>
|
|
|
+ <SaveButton type="submit" className="mini" />
|
|
|
+ <Button onClick={() => setTaskToEdit(undefined)} className="mini">
|
|
|
+ <XMarkIcon />
|
|
|
+ <span>Cancel</span>
|
|
|
+ </Button>
|
|
|
+ </ButtonSet>
|
|
|
+ </Row>
|
|
|
+ </form>
|
|
|
+ ) : (
|
|
|
+ <span onClick={() => setTaskToEdit(task)}>{task.description}</span>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
<ButtonSet>
|
|
|
{task.done ? (
|
|
|
<Button className="positive mini fill" onClick={() => toggleTaskDone(task)}>
|