Skip to content

Commit

Permalink
chore: first draft for implementing Task Template API
Browse files Browse the repository at this point in the history
  • Loading branch information
florian-sabonchi committed Jul 10, 2023
1 parent 59eb2db commit 6e0b0f5
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 88 deletions.
2 changes: 1 addition & 1 deletion tasks/components/SubtaskTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const SubtaskTile = ({
<div>
<Checkbox
onChange={value => onChange({ ...subtask, isDone: value })}
checked={subtask.isDone}
checked={subtask.isDone !== undefined ? subtask.isDone : false}
/>
</div>
<ToggleableInput
Expand Down
12 changes: 7 additions & 5 deletions tasks/components/layout/TaskDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import { X } from 'lucide-react'
import { TimeDisplay } from '@helpwave/common/components/TimeDisplay'
import { Span } from '@helpwave/common/components/Span'
import type { TaskTemplateDTO } from '../../mutations/task_template_mutations'
import { useTaskTemplateQuery } from '../../mutations/task_template_mutations'
import { TaskTemplateListColumn } from '../TaskTemplateListColumn'
import { useState } from 'react'
import { useRouter } from 'next/router'
import { Input } from '@helpwave/common/components/user_input/Input'
import type { Languages } from '@helpwave/common/hooks/useLanguage'
import { usePersonalTaskTemplateQuery, useWardTaskTemplateQuery } from '../../mutations/task_template_mutations'
import { useAuth } from '../../hooks/useAuth'

type TaskDetailViewTranslation = {
close: string,
Expand Down Expand Up @@ -85,7 +86,8 @@ export const TaskDetailView = ({
const translation = useTranslation(language, defaultTaskDetailViewTranslation)
const [selectedTemplate, setSelectedTemplate] = useState<TaskTemplateDTO | undefined>(undefined)
const router = useRouter()
const { uuid } = router.query
const { uuid: wardId } = router.query
const { user } = useAuth()

const minTaskNameLength = 4
const maxTaskNameLength = 32
Expand All @@ -94,12 +96,12 @@ export const TaskDetailView = ({
data: personalTaskTemplatesData,
isLoading: personalTaskTemplatesIsLoading,
error: personalTaskTemplatesError
} = useTaskTemplateQuery('personalTaskTemplates')
} = usePersonalTaskTemplateQuery(user?.id)
const {
data: wardTaskTemplatesData,
isLoading: wardTaskTemplatesIsLoading,
error: wardTaskTemplatesError
} = useTaskTemplateQuery('wardTaskTemplates')
} = useWardTaskTemplateQuery(wardId?.toString())

const formatDate = (date: Date) => {
return `${date.getFullYear().toString().padStart(4, '0')}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}T${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
Expand All @@ -125,7 +127,7 @@ export const TaskDetailView = ({
subtasks: taskTemplate.subtasks
})
}}
onColumnEditClick={() => router.push(`/ward/${uuid}/templates`)}
onColumnEditClick={() => router.push(`/ward/${wardId}/templates`)}
/>
)}
<>
Expand Down
2 changes: 1 addition & 1 deletion tasks/components/layout/TaskTemplateDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const TaskTemplateDisplay = ({
<div className={tw(`grid grid-cols-${columns} gap-6`)}>
{taskTemplates.map(taskTemplate => (
<TaskTemplateCard
key={taskTemplate.name}
key={taskTemplate.id}
name={taskTemplate.name}
subtaskCount={taskTemplate.subtasks.length}
isSelected={selectedID === taskTemplate.id}
Expand Down
4 changes: 2 additions & 2 deletions tasks/components/layout/WardDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { RoomList } from '../RoomList'
import { WardForm } from '../WardForm'
import { Span } from '@helpwave/common/components/Span'
import { TaskTemplateWardPreview } from '../TaskTemplateWardPreview'
import { useTaskTemplateQuery } from '../../mutations/task_template_mutations'
import type { WardDetailDTO, WardDTO } from '../../mutations/ward_mutations'
import { useWardTaskTemplateQuery } from '../../mutations/task_template_mutations'

type WardDetailTranslation = {
updateWard: string,
Expand Down Expand Up @@ -81,7 +81,7 @@ export const WardDetail = ({
const [filledRequired, setFilledRequired] = useState(!isCreatingNewOrganization)
const [newWard, setNewWard] = useState<WardDetailDTO>(ward)

const { data, isLoading, isError } = useTaskTemplateQuery('wardTaskTemplates')
const { data, isLoading, isError } = useWardTaskTemplateQuery(ward.id)
// the value of how much space a TaskTemplateCard and the surrounding gap requires, given in px
const minimumWidthOfCards = 200
const columns = width === undefined ? 3 : Math.max(Math.floor(width / minimumWidthOfCards), 1)
Expand Down
2 changes: 1 addition & 1 deletion tasks/mutations/room_mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type TaskStatus = 'unscheduled' | 'inProgress' | 'done'

export type SubTaskDTO = {
name: string,
isDone: boolean
isDone?: boolean
}

export type TaskDTO = {
Expand Down
161 changes: 100 additions & 61 deletions tasks/mutations/task_template_mutations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import type { SubTaskDTO } from './room_mutations'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { noop } from '@helpwave/common/components/user_input/Input'
import { getAuthenticatedGrpcMetadata, taskTemplateService } from '../utils/grpc'
import {
CreateTaskTemplateRequest,
DeleteTaskTemplateRequest,
GetAllTaskTemplatesByCreatorRequest,
GetAllTaskTemplatesByWardRequest,
UpdateTaskTemplateRequest
} from '@helpwave/proto-ts/proto/services/task_svc/v1/task_template_svc_pb'
import SubTask = CreateTaskTemplateRequest.SubTask

export type TaskTemplateWardPreviewDTO = {
id: string,
Expand All @@ -12,45 +20,71 @@ export type TaskTemplateWardPreviewDTO = {
}

export type TaskTemplateDTO = {
wardId? : string,
id: string,
name: string,
notes: string,
subtasks: SubTaskDTO[],
subtasks: {
isDone?: boolean,
id?: string,
name: string
}[],
isPublicVisible: boolean
}

type QueryKey = 'personalTaskTemplates'| 'wardTaskTemplates'

// TODO remove once backend is implemented
let personalTaskTemplates: TaskTemplateDTO[] = [
{ id: 'id1', subtasks: [{ name: 'Subtask', isDone: false }], name: 'Personal Template 1', notes: '', isPublicVisible: false },
{ id: 'id2', subtasks: [], name: 'Personal Template 2', notes: '', isPublicVisible: false },
{ id: 'id3', subtasks: [], name: 'Personal Template 3', notes: '', isPublicVisible: false },
{ id: 'id4', subtasks: [], name: 'Personal Template 4', notes: '', isPublicVisible: false },
{ id: 'id5', subtasks: [], name: 'Personal Template 5', notes: '', isPublicVisible: false },
{ id: 'id6', subtasks: [], name: 'Personal Template 6', notes: '', isPublicVisible: false },
{ id: 'id7', subtasks: [], name: 'Personal Template 7', notes: '', isPublicVisible: false }
]

// TODO remove once backend is implemented
let wardTaskTemplates: TaskTemplateDTO[] = [
{ id: 'id8', subtasks: [{ name: 'Subtask', isDone: false }], name: 'Ward Template 1', notes: '', isPublicVisible: false },
{ id: 'id9', subtasks: [], name: 'Ward Template 2', notes: '', isPublicVisible: false },
{ id: 'id10', subtasks: [], name: 'Ward Template 3', notes: '', isPublicVisible: false },
{ id: 'id11', subtasks: [], name: 'Ward Template 4', notes: '', isPublicVisible: false },
{ id: 'id12', subtasks: [], name: 'Ward Template 5', notes: '', isPublicVisible: false },
{ id: 'id13', subtasks: [], name: 'Ward Template 6', notes: '', isPublicVisible: false },
{ id: 'id14', subtasks: [], name: 'Ward Template 7', notes: '', isPublicVisible: false }
]

export const useTaskTemplateQuery = (queryKey: QueryKey, onSuccess: (data: TaskTemplateDTO[]) => void = noop) => {
type QueryKey = 'personalTaskTemplates' | 'wardTaskTemplates'

export const useWardTaskTemplateQuery = (wardId? : string, onSuccess: (data: TaskTemplateDTO[]) => void = noop) => {
return useQuery({
queryKey: [queryKey],
queryKey: ['wardTaskTemplates'],
queryFn: async () => {
if (queryKey === 'wardTaskTemplates') {
let wardTaskTemplates : TaskTemplateDTO[] = []
if (wardId !== undefined) {
const req = new GetAllTaskTemplatesByWardRequest()
req.setWardId(wardId)
const res = await taskTemplateService.getAllTaskTemplatesByWard(req, getAuthenticatedGrpcMetadata())
wardTaskTemplates = res.getTemplatesList().map((template) => ({
id: template.getId(),
name: template.getName(),
notes: template.getDescription(),
subtasks: template.getSubtasksList().map((subtask) => ({
id: subtask.getId(),
name: subtask.getName(),
})),
isPublicVisible: template.getIsPublic()
}))
return wardTaskTemplates
}
// TODO fetch task templates

return wardTaskTemplates
},
onSuccess
})
}

export const usePersonalTaskTemplateQuery = (createdBy? : string, onSuccess: (data: TaskTemplateDTO[]) => void = noop) => {
return useQuery({
queryKey: ['personalTaskTemplates', createdBy],
queryFn: async () => {
let personalTaskTemplates: TaskTemplateDTO[] = []
if (createdBy !== undefined) {
const req = new GetAllTaskTemplatesByCreatorRequest()
req.setCreatedBy(createdBy)
const res = await taskTemplateService.getAllTaskTemplatesByCreator(req, getAuthenticatedGrpcMetadata())

personalTaskTemplates = res.getTemplatesList().map((template) => ({
id: template.getId(),
name: template.getName(),
notes: template.getDescription(),
subtasks: template.getSubtasksList().map((subtask) => ({
id: subtask.getId(),
name: subtask.getName(),
})),
isPublicVisible: template.getIsPublic()
}))
// return only personal Task Templates
return personalTaskTemplates
}
return personalTaskTemplates
},
onSuccess
Expand All @@ -61,21 +95,22 @@ export const useUpdateMutation = (queryKey: QueryKey, setTemplate: (taskTemplate
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (taskTemplate: TaskTemplateDTO) => {
// TODO create request for taskTemplate
if (queryKey === 'wardTaskTemplates') {
wardTaskTemplates = [...wardTaskTemplates.filter(value => value.id !== taskTemplate.id), taskTemplate]
wardTaskTemplates.sort((a, b) => a.id.localeCompare(b.id))
}
personalTaskTemplates = [...personalTaskTemplates.filter(value => value.id !== taskTemplate.id), taskTemplate]
personalTaskTemplates.sort((a, b) => a.id.localeCompare(b.id))
setTemplate(taskTemplate)
const updateTaskTemplate = new UpdateTaskTemplateRequest()

updateTaskTemplate.setName(taskTemplate.name)
updateTaskTemplate.setDescription(taskTemplate.notes)
updateTaskTemplate.setId(taskTemplate.id)

const res = await taskTemplateService.updateTaskTemplate(updateTaskTemplate, getAuthenticatedGrpcMetadata())
const newTaskTemplate: TaskTemplateDTO = { ...taskTemplate, ...res }

setTemplate(newTaskTemplate)
},
onMutate: async (taskTemplate: TaskTemplateDTO) => {
onMutate: async () => {
await queryClient.cancelQueries({ queryKey: [queryKey] })
const previousTaskTemplates = queryClient.getQueryData<TaskTemplateDTO[]>([queryKey])
queryClient.setQueryData<TaskTemplateDTO[]>(
[queryKey],
// TODO do optimistic update here
(old) => old)
return { previousTaskTemplates }
},
Expand All @@ -91,27 +126,35 @@ export const useUpdateMutation = (queryKey: QueryKey, setTemplate: (taskTemplate
export const useCreateMutation = (queryKey: QueryKey, setTemplate: (taskTemplate:TaskTemplateDTO | undefined) => void) => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (taskTemplate:TaskTemplateDTO) => {
const newTaskTemplate = { ...taskTemplate, id: Math.random().toString() }
// TODO create request for taskTemplate
if (queryKey === 'wardTaskTemplates') {
wardTaskTemplates = [...wardTaskTemplates, newTaskTemplate]
wardTaskTemplates.sort((a, b) => a.id.localeCompare(b.id))
mutationFn: async (taskTemplate: TaskTemplateDTO) => {
const createTaskTemplate = new CreateTaskTemplateRequest()

createTaskTemplate.setName(taskTemplate.name)
createTaskTemplate.setDescription(taskTemplate.notes)
createTaskTemplate.setSubtasksList(taskTemplate.subtasks.map((cSubtask) => {
const subTask = new SubTask()
subTask.setName(cSubtask.name)
return subTask
}))

if (taskTemplate.wardId !== undefined) {
createTaskTemplate.setWardId(taskTemplate.wardId)
}
personalTaskTemplates = [...personalTaskTemplates, newTaskTemplate]
personalTaskTemplates.sort((a, b) => a.id.localeCompare(b.id))

const res = await taskTemplateService.createTaskTemplate(createTaskTemplate, getAuthenticatedGrpcMetadata())
const newTaskTemplate: TaskTemplateDTO = { ...taskTemplate, ...res }

setTemplate(newTaskTemplate)
},
onMutate: async (taskTemplate:TaskTemplateDTO) => {
onError: (_, newTodo, context) => {
queryClient.setQueryData([queryKey], context === undefined ? [] : context.previousTaskTemplate)
},
onMutate: async () => {
await queryClient.cancelQueries({ queryKey: [queryKey] })
const previousTaskTemplate = queryClient.getQueryData<TaskTemplateDTO[]>([queryKey])
// TODO do optimistic update here
queryClient.setQueryData<TaskTemplateDTO[]>([queryKey], (old) => old)
return { previousTaskTemplate }
},
onError: (_, newTodo, context) => {
queryClient.setQueryData([queryKey], context === undefined ? [] : context.previousTaskTemplate)
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: [queryKey] }).then()
},
Expand All @@ -122,21 +165,17 @@ export const useDeleteMutation = (queryKey: QueryKey, setTemplate: (task:TaskTem
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (taskTemplate) => {
// TODO create request for taskTemplate
if (queryKey === 'wardTaskTemplates') {
wardTaskTemplates = wardTaskTemplates.filter(value => value.id !== taskTemplate.id)
wardTaskTemplates.sort((a, b) => a.id.localeCompare(b.id))
}
personalTaskTemplates = personalTaskTemplates.filter(value => value.id !== taskTemplate.id)
personalTaskTemplates.sort((a, b) => a.id.localeCompare(b.id))
const deleteTaskTemplate = new DeleteTaskTemplateRequest()
deleteTaskTemplate.setId(taskTemplate.id)
await taskTemplateService.deleteTaskTemplate(deleteTaskTemplate, getAuthenticatedGrpcMetadata())

setTemplate(undefined)
},
onMutate: async (taskTemplate: TaskTemplateDTO) => {
await queryClient.cancelQueries({ queryKey: [queryKey] })
const previousTaskTemplate = queryClient.getQueryData<TaskTemplateDTO[]>([queryKey])
queryClient.setQueryData<TaskTemplateDTO[]>(
[queryKey],
// TODO do optimistic update here
(old) => old)
return { previousTaskTemplate }
},
Expand Down
7 changes: 4 additions & 3 deletions tasks/pages/templates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import { TaskTemplateDisplay } from '../components/layout/TaskTemplateDisplay'
import type { TaskTemplateDTO } from '../mutations/task_template_mutations'
import {
useCreateMutation,
useDeleteMutation,
useTaskTemplateQuery,
useDeleteMutation, usePersonalTaskTemplateQuery,
useUpdateMutation
} from '../mutations/task_template_mutations'
import type { TaskTemplateFormType } from './ward/[uuid]/templates'
import { TaskTemplateDetails } from '../components/layout/TaskTemplateDetails'
import { useRouter } from 'next/router'
import { useAuth } from '../hooks/useAuth'

type PersonalTaskTemplateTranslation = {
taskTemplates: string,
Expand Down Expand Up @@ -47,12 +47,13 @@ const PersonalTaskTemplatesPage: NextPage = ({ language }: PropsWithLanguage<Per
const router = useRouter()
const { templateID } = router.query
const [usedQueryParam, setUsedQueryParam] = useState(false)
const { user } = useAuth()
const [taskTemplateForm, setTaskTemplateForm] = useState<TaskTemplateFormType>({
isValid: false,
hasChanges: false,
template: emptyTaskTemplate
})
const { isLoading, isError, data } = useTaskTemplateQuery('personalTaskTemplates')
const { isLoading, isError, data } = usePersonalTaskTemplateQuery(user?.id)

const createMutation = useCreateMutation('personalTaskTemplates', taskTemplate =>
setTaskTemplateForm({
Expand Down
Loading

0 comments on commit 6e0b0f5

Please sign in to comment.