diff --git a/extensions/toggl-track/CHANGELOG.md b/extensions/toggl-track/CHANGELOG.md index f2c0eccd25a..555656ef8ef 100644 --- a/extensions/toggl-track/CHANGELOG.md +++ b/extensions/toggl-track/CHANGELOG.md @@ -1,5 +1,9 @@ # Toggl Track Changelog +## [New Feature] - 2024-10-07 + +- Add ability to create task for time entry form + ## [Bug Fixes] - 2024-08-20 - Remove seconds from optional timer in the Menu Bar, as it only updates every 10 seconds. diff --git a/extensions/toggl-track/src/api/index.ts b/extensions/toggl-track/src/api/index.ts index 3657c50ca74..8063e9f778a 100644 --- a/extensions/toggl-track/src/api/index.ts +++ b/extensions/toggl-track/src/api/index.ts @@ -19,7 +19,7 @@ export { type Client, } from "@/api/clients"; export { getMyTags, createTag, updateTag, deleteTag, type Tag } from "@/api/tags"; -export { getMyTasks, type Task } from "@/api/tasks"; +export { getMyTasks, createTask, type Task } from "@/api/tasks"; export { getMyTimeEntries, createTimeEntry, diff --git a/extensions/toggl-track/src/api/tasks.ts b/extensions/toggl-track/src/api/tasks.ts index 8b4b46ae3a0..bfa1677a6b0 100644 --- a/extensions/toggl-track/src/api/tasks.ts +++ b/extensions/toggl-track/src/api/tasks.ts @@ -1,10 +1,14 @@ -import { get } from "@/api/togglClient"; +import { get, post } from "@/api/togglClient"; import type { ToggleItem } from "@/api/types"; export function getMyTasks() { return get("/me/tasks"); } +export function createTask(workspaceId: number, projectId: number, name: string) { + return post(`/workspaces/${workspaceId}/projects/${projectId}/tasks`, { name }); +} + /** @see {@link https://developers.track.toggl.com/docs/api/tasks#response Toggl Api} */ export interface Task extends ToggleItem { active: boolean; diff --git a/extensions/toggl-track/src/components/CreateTimeEntryForm.tsx b/extensions/toggl-track/src/components/CreateTimeEntryForm.tsx index 010f1d3ece5..0ebff3ec121 100644 --- a/extensions/toggl-track/src/components/CreateTimeEntryForm.tsx +++ b/extensions/toggl-track/src/components/CreateTimeEntryForm.tsx @@ -1,8 +1,18 @@ -import { Action, ActionPanel, Form, Icon, Toast, clearSearchBar, showToast, useNavigation } from "@raycast/api"; -import { useCachedState } from "@raycast/utils"; +import { + Action, + ActionPanel, + Form, + Icon, + Toast, + clearSearchBar, + showToast, + useNavigation, + confirmAlert, +} from "@raycast/api"; +import { useCachedState, showFailureToast } from "@raycast/utils"; import { useMemo, useState } from "react"; -import { Client, Project, Task, TimeEntry, TimeEntryMetaData, createTimeEntry } from "@/api"; +import { Client, Project, Task, TimeEntry, TimeEntryMetaData, createTimeEntry, createTask } from "@/api"; import { useClients, useMe, useProjects, useTags, useTasks, useWorkspaces } from "@/hooks"; interface CreateTimeEntryFormParams { @@ -21,7 +31,7 @@ function CreateTimeEntryForm({ const { workspaces, isLoadingWorkspaces } = useWorkspaces(); const { clients, isLoadingClients } = useClients(); const { projects, isLoadingProjects } = useProjects(); - const { tasks, isLoadingTasks } = useTasks(); + const { tasks, isLoadingTasks, revalidateTasks } = useTasks(); const { tags, isLoadingTags } = useTags(); const [selectedWorkspace, setSelectedWorkspace] = useCachedState("defaultWorkspace", workspaces.at(0)?.id); @@ -37,6 +47,8 @@ function CreateTimeEntryForm({ const [selectedTags, setSelectedTags] = useState(initialValues?.tags || []); const [billable, setBillable] = useState(initialValues?.billable || false); + const [taskSearch, setTaskSearch] = useState(""); + async function handleSubmit(values: { description: string; billable?: boolean }) { const workspaceId = selectedProject?.workspace_id || me?.default_workspace_id; @@ -110,9 +122,31 @@ function CreateTimeEntryForm({ if (project) setSelectedProject(project); }; - const onTaskChange = (taskId: string) => { - const task = tasks.find((task) => task.id === parseInt(taskId)); - setSelectedTask(task); + const onTaskChange = async (taskId: string) => { + if (taskId == "new_task") { + const newTaskName = taskSearch; + setTaskSearch(""); + if (await confirmAlert({ title: "Create new task?", message: "Task name: " + newTaskName })) { + const toast = await showToast(Toast.Style.Animated, "Creating task..."); + try { + if (!selectedWorkspace) throw Error("Workspace ID is undefined."); + if (!selectedProject) throw Error("Workspace ID is undefined."); + const newTask = await createTask(selectedWorkspace, selectedProject.id, newTaskName); + revalidateTasks(); + setSelectedTask(newTask); + toast.style = Toast.Style.Success; + toast.title = "Created task"; + } catch (error) { + await toast.hide(); + await showFailureToast(error); + } + } else { + setSelectedTask(undefined); + } + } else { + const task = tasks.find((task) => task.id === parseInt(taskId)); + setSelectedTask(task); + } }; return ( @@ -167,7 +201,7 @@ function CreateTimeEntryForm({ > {!isLoadingProjects && ( <> - + {filteredProjects.map((project) => ( - {selectedProject && filteredTasks.length > 0 && ( - + {selectedProject && ( + setTaskSearch("")} + > {!isLoadingTasks && ( <> - + {filteredTasks.map((task) => ( - + ))} + {taskSearch !== "" && } )}