diff --git a/api/ContextProjects.tsx b/api/ContextProjects.tsx index 5602b89fc..982bf384e 100644 --- a/api/ContextProjects.tsx +++ b/api/ContextProjects.tsx @@ -3,7 +3,6 @@ import { toast } from "@/components/ui/use-toast"; import { Context } from "@/contexts/ContextContext"; import { addDaysToDate, - newDateTimeString, toISODateString, uniqArraySorted, } from "@/helpers/functional"; @@ -25,6 +24,10 @@ import { import { FC, ReactNode, createContext, useContext } from "react"; import useSWR, { KeyedMutator } from "swr"; import { handleApiErrors } from "./globals"; +import { + createActivityApi, + createProjectActivityApi, +} from "./helpers/activity"; import { CrmProject, mapCrmProject } from "./useCrmProjects"; const client = generateClient(); @@ -323,16 +326,7 @@ export const ProjectsContextProvider: FC = ({ projects?.find((project) => project.id === projectId); const createProjectActivity = async (projectId: string) => { - const { data: activity, errors: errorsActivity } = - await client.models.Activity.create({ - formatVersion: 3, - noteBlockIds: [], - finishedOn: newDateTimeString(), - }); - if (errorsActivity) { - handleApiErrors(errorsActivity, "Error creating activity"); - return; - } + const activity = await createActivityApi(); if (!activity) return; const updated: Project[] = projects?.map((project) => @@ -341,11 +335,7 @@ export const ProjectsContextProvider: FC = ({ : { ...project, activityIds: [activity.id, ...project.activityIds] } ) || []; mutateProjects(updated, false); - const { data, errors } = await client.models.ProjectActivity.create({ - activityId: activity.id, - projectsId: projectId, - }); - if (errors) handleApiErrors(errors, "Error linking activity with project"); + const data = await createProjectActivityApi(projectId, activity.id); mutateProjects(updated); return data?.activityId; }; diff --git a/api/helpers/activity.ts b/api/helpers/activity.ts new file mode 100644 index 000000000..81bf151cd --- /dev/null +++ b/api/helpers/activity.ts @@ -0,0 +1,45 @@ +import { type Schema } from "@/amplify/data/resource"; +import { newDateTimeString } from "@/helpers/functional"; +import { generateClient } from "aws-amplify/data"; +import { handleApiErrors } from "../globals"; +const client = generateClient(); + +export const createActivityApi = async () => { + const { data, errors } = await client.models.Activity.create({ + formatVersion: 3, + noteBlockIds: [], + finishedOn: newDateTimeString(), + notes: null, + notesJson: null, + }); + if (errors) { + handleApiErrors(errors, "Error creating activity"); + return; + } + return data; +}; + +export const updateActivityBlockIds = async ( + activityId: string, + blockIds: string[] +) => { + const { data, errors } = await client.models.Activity.update({ + id: activityId, + noteBlockIds: blockIds, + }); + if (errors) + handleApiErrors(errors, "Error updating linked blocks on activity"); + return data; +}; + +export const createProjectActivityApi = async ( + projectsId: string, + activityId: string +) => { + const { data, errors } = await client.models.ProjectActivity.create({ + activityId, + projectsId, + }); + if (errors) handleApiErrors(errors, "Error linking activity with project"); + return data; +}; diff --git a/api/helpers/people.ts b/api/helpers/people.ts new file mode 100644 index 000000000..e95d84c4c --- /dev/null +++ b/api/helpers/people.ts @@ -0,0 +1,12 @@ +import { type Schema } from "@/amplify/data/resource"; +import { generateClient } from "aws-amplify/api"; +const client = generateClient(); + +export const createMentionedPersonApi = ( + noteBlockId: string, + personId: string +) => + client.models.NoteBlockPerson.create({ + noteBlockId, + personId, + }); diff --git a/api/helpers/todo.ts b/api/helpers/todo.ts new file mode 100644 index 000000000..931a12783 --- /dev/null +++ b/api/helpers/todo.ts @@ -0,0 +1,25 @@ +import { type Schema } from "@/amplify/data/resource"; +import { newDateString } from "@/helpers/functional"; +import { generateClient } from "aws-amplify/data"; +const client = generateClient(); + +export const createTodoApi = (content: string, done: boolean) => + client.models.Todo.create({ + todo: content, + status: done ? "DONE" : "OPEN", + doneOn: done ? newDateString() : null, + }); + +export const createBlockApi = ( + activityId: string, + content: string | null, + todoId: string | undefined, + type: string +) => + client.models.NoteBlock.create({ + activityId, + formatVersion: 3, + type, + content, + todoId, + }); diff --git a/api/useActivity.ts b/api/useActivity.ts index 5c2af053a..4127a229d 100644 --- a/api/useActivity.ts +++ b/api/useActivity.ts @@ -16,6 +16,7 @@ import { generateClient, SelectionSet } from "aws-amplify/data"; import { useState } from "react"; import useSWR from "swr"; import { handleApiErrors } from "./globals"; +import { createProjectActivityApi } from "./helpers/activity"; const client = generateClient(); export type TempIdMapping = { @@ -145,11 +146,7 @@ const useActivity = (activityId?: string) => { projectIds: [...activity.projectIds, projectId], }; mutateActivity(updated, false); - const { data, errors } = await client.models.ProjectActivity.create({ - activityId: activity.id, - projectsId: projectId, - }); - if (errors) handleApiErrors(errors, "Error adding project to current note"); + const data = await createProjectActivityApi(projectId, activity.id); if (data) toast({ title: "Added note to another project" }); mutateActivity(updated); return data?.id; diff --git a/api/useDailyPlans.tsx b/api/useDailyPlans.tsx index e30bc8686..83d619a4e 100644 --- a/api/useDailyPlans.tsx +++ b/api/useDailyPlans.tsx @@ -243,7 +243,6 @@ const useDailyPlans = (status?: DailyPlanStatus) => { const dayPlanProject = flow( identity, find(["id", dayPlanId]), - (test) => test, get("projects"), find(["projectId", projectId]) )(dailyPlans) as DailyPlanProject | undefined; diff --git a/api/useInboxWorkflow.ts b/api/useInboxWorkflow.ts index d2c87274b..80dbe6b4b 100644 --- a/api/useInboxWorkflow.ts +++ b/api/useInboxWorkflow.ts @@ -105,8 +105,8 @@ const useInboxWorkflow = (mutate: HandleMutationFn) => { type: !block.type ? "paragraph" : parentType === "orderedList" - ? "listItemOrdered" - : block.type, + ? "listItemOrdered" + : block.type, content: !todoId ? stringifyBlock(block) : null, ...(!todoId ? {} : { todoId }), }); diff --git a/api/useProjectTodos.ts b/api/useProjectTodos.ts index 33a6ef734..89ed80b03 100644 --- a/api/useProjectTodos.ts +++ b/api/useProjectTodos.ts @@ -1,4 +1,6 @@ import { type Schema } from "@/amplify/data/resource"; +import { stringifyBlock } from "@/components/ui-elements/editors/helpers/blocks"; +import { getPeopleMentioned } from "@/components/ui-elements/editors/helpers/mentioned-people-cud"; import { isNotNil, newDateString } from "@/helpers/functional"; import { getTodoDoneOn, @@ -22,6 +24,13 @@ import { } from "lodash/fp"; import useSWR from "swr"; import { handleApiErrors } from "./globals"; +import { + createActivityApi, + createProjectActivityApi, + updateActivityBlockIds, +} from "./helpers/activity"; +import { createMentionedPersonApi } from "./helpers/people"; +import { createBlockApi, createTodoApi } from "./helpers/todo"; const client = generateClient(); export type Todo = { @@ -154,7 +163,88 @@ const useProjectTodos = (projectId: string | undefined) => { return data?.id; }; - return { projectTodos, isLoading, error, finishTodo }; + const createTodoRecord = async (todo: JSONContent) => { + const { data, errors } = await createTodoApi(stringifyBlock(todo), false); + if (errors) handleApiErrors(errors, "Creating todo failed"); + return data; + }; + + const createNoteBlockRecord = async (activityId: string, todoId: string) => { + const { data, errors } = await createBlockApi( + activityId, + null, + todoId, + "taskItem" + ); + if (errors) handleApiErrors(errors, "Creating note block failed"); + return data?.id; + }; + + const createMentionedPerson = + (blockId: string) => + async (personId: string): Promise => { + const { data, errors } = await createMentionedPersonApi( + blockId, + personId + ); + if (errors) handleApiErrors(errors, "Creating mentioned person failed"); + return data?.id; + }; + + const createMentionedPeople = async (blockId: string, todo: JSONContent) => { + const peopleIds = await Promise.all( + flow( + getPeopleMentioned, + map("attrs.id"), + map(createMentionedPerson(blockId)) + )(todo) + ); + return peopleIds; + }; + + const createTodo = async (todo: JSONContent) => { + if (!projectId) return; + const activity = await createActivityApi(); + if (!activity) return; + const projectActivity = await createProjectActivityApi( + projectId, + activity.id + ); + if (!projectActivity) return; + const todoBlock = { + type: "taskItem", + attrs: { + checked: false, + }, + content: todo.content, + } as JSONContent; + const todoData = await createTodoRecord(todoBlock); + if (!todoData) return; + const blockId = await createNoteBlockRecord(activity.id, todoData.id); + if (!blockId) return; + const updatedActivity = await updateActivityBlockIds(activity.id, [ + blockId, + ]); + if (!updatedActivity) return; + await createMentionedPeople(blockId, todo); + mutate([ + ...(projectTodos ?? []), + { + todoId: todoData.id, + todo: todoBlock, + done: false, + doneOn: null, + activityId: activity.id, + blockId, + isOrphan: false, + updatedAt: new Date(), + projectActivityId: projectActivity.id, + }, + ]); + return todoData.id; + }; + + return { projectTodos, isLoading, error, finishTodo, createTodo }; }; export default useProjectTodos; diff --git a/components/meetings/meeting-next-actions.tsx b/components/meetings/meeting-next-actions.tsx index 7fc3f97aa..9401aec7c 100644 --- a/components/meetings/meeting-next-actions.tsx +++ b/components/meetings/meeting-next-actions.tsx @@ -2,7 +2,7 @@ import { Todo } from "@/api/useProjectTodos"; import { FC } from "react"; import DefaultAccordionItem from "../ui-elements/accordion/DefaultAccordionItem"; import { getTodoText } from "../ui-elements/editors/helpers/text-generation"; -import TodoEditor from "../ui-elements/editors/todo-editor/TodoEditor"; +import TodoViewer from "../ui-elements/editors/todo-viewer/TodoViewer"; type MeetingNextActionsProps = { todos: Todo[] | undefined; @@ -16,7 +16,7 @@ const MeetingNextActions: FC = ({ todos }) => triggerTitle="Agreed Next Actions" triggerSubTitle={getTodoText(todos)} > - + ); diff --git a/components/planning/day/DailyPlanProject.tsx b/components/planning/day/DailyPlanProject.tsx index e9434c55d..b986421a8 100644 --- a/components/planning/day/DailyPlanProject.tsx +++ b/components/planning/day/DailyPlanProject.tsx @@ -4,6 +4,7 @@ import useDailyPlans, { DailyPlanProject, } from "@/api/useDailyPlans"; import useProjectTodos, { ProjectTodo } from "@/api/useProjectTodos"; +import AddTodoSection from "@/components/ui-elements/editors/todo-editor/AddTodoSection"; import ShowHideSwitch from "@/components/ui-elements/ShowHideSwitch"; import { setPostponedTodoList, setTodoList } from "@/helpers/today"; import { flow, get, identity } from "lodash/fp"; @@ -25,7 +26,7 @@ const DailyPlanProjectComponent: FC = ({ const { addProjectToDayPlan, postponeTodo } = useDailyPlans("OPEN"); const { getProjectById } = useProjectsContext(); const [project, setProject] = useState(); - const { projectTodos, finishTodo } = useProjectTodos(project?.id); + const { projectTodos, finishTodo, createTodo } = useProjectTodos(project?.id); const [openTodos, setOpenTodos] = useState(); const [closedTodos, setClosedTodos] = useState(); const [postponedTodos, setPostponedTodos] = useState< @@ -74,6 +75,8 @@ const DailyPlanProjectComponent: FC = ({ {showTodos && ( <> + + = ({ projectId }) => { return (
- {!!openTodos?.length && } + {!!openTodos?.length && } = ({ projectId }) => { className="ml-1 md:ml-2" /> - {showClosed && closedTodos?.length && } + {showClosed && closedTodos?.length && }
); }; diff --git a/components/ui-elements/editors/helpers/blocks-cud.ts b/components/ui-elements/editors/helpers/blocks-cud.ts index cce2cdd7b..ba333535d 100644 --- a/components/ui-elements/editors/helpers/blocks-cud.ts +++ b/components/ui-elements/editors/helpers/blocks-cud.ts @@ -1,6 +1,7 @@ /* Create, update, delete operations on blocks (i.e., NoteBlock) */ import { type Schema } from "@/amplify/data/resource"; +import { createBlockApi } from "@/api/helpers/todo"; import { Activity, TempIdMapping } from "@/api/useActivity"; import { not } from "@/helpers/functional"; import { Editor, JSONContent } from "@tiptap/core"; @@ -89,13 +90,12 @@ export const createBlock = async ({ todoId, type, }: TBlockCreationSet): Promise => { - const { data, errors } = await client.models.NoteBlock.create({ + const { data, errors } = await createBlockApi( activityId, - formatVersion: 3, - type, content, todoId, - }); + type + ); if (errors) throw new TransactionError( "Creating note block failed", diff --git a/components/ui-elements/editors/helpers/mentioned-people-cud.ts b/components/ui-elements/editors/helpers/mentioned-people-cud.ts index e51a37a8a..e7d64755b 100644 --- a/components/ui-elements/editors/helpers/mentioned-people-cud.ts +++ b/components/ui-elements/editors/helpers/mentioned-people-cud.ts @@ -1,6 +1,7 @@ /* Create, update, delete operations on mentioned people (i.e., NoteMentionedPersonPerson) */ import { type Schema } from "@/amplify/data/resource"; +import { createMentionedPersonApi } from "@/api/helpers/people"; import { Activity, TempIdMapping } from "@/api/useActivity"; import { not } from "@/helpers/functional"; import { Editor, JSONContent } from "@tiptap/core"; @@ -51,10 +52,7 @@ const createMentionedPerson = async ({ personId, tempId, }: TMentionedPersonCreationSet): Promise => { - const { data, errors } = await client.models.NoteBlockPerson.create({ - noteBlockId: blockId, - personId, - }); + const { data, errors } = await createMentionedPersonApi(blockId, personId); if (errors) throw new TransactionError( "Creating mentioned person failed", diff --git a/components/ui-elements/editors/helpers/todos-cud.ts b/components/ui-elements/editors/helpers/todos-cud.ts index d0c777504..7be833ec8 100644 --- a/components/ui-elements/editors/helpers/todos-cud.ts +++ b/components/ui-elements/editors/helpers/todos-cud.ts @@ -1,6 +1,7 @@ /* Create, update, delete operations on todos (i.e., Todo) */ import { type Schema } from "@/amplify/data/resource"; +import { createTodoApi } from "@/api/helpers/todo"; import { Activity, TempIdMapping } from "@/api/useActivity"; import { newDateString, not } from "@/helpers/functional"; import { Editor, JSONContent } from "@tiptap/core"; @@ -41,11 +42,7 @@ export const createTodo = async ({ done, tempId, }: TTodoCreationSet): Promise => { - const { data, errors } = await client.models.Todo.create({ - todo: content, - status: done ? "DONE" : "OPEN", - doneOn: done ? newDateString() : null, - }); + const { data, errors } = await createTodoApi(content, done); if (errors) throw new TransactionError( "Creating todo failed", diff --git a/components/ui-elements/editors/todo-editor/AddTodoSection.tsx b/components/ui-elements/editors/todo-editor/AddTodoSection.tsx new file mode 100644 index 000000000..db123d816 --- /dev/null +++ b/components/ui-elements/editors/todo-editor/AddTodoSection.tsx @@ -0,0 +1,43 @@ +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; +import { JSONContent } from "@tiptap/core"; +import { ChevronUpCircle, PlusCircle } from "lucide-react"; +import { FC, useState } from "react"; +import TodoEditor from "./TodoEditor"; + +type AddTodoSectionProps = { + className?: string; + onSave: (json: JSONContent) => Promise; +}; + +const AddTodoSection: FC = ({ className, onSave }) => { + const [showTodoEditor, setShowTodoEditor] = useState(false); + + const saveItem = async (json: JSONContent) => { + const todoId = await onSave(json); + if (!todoId) return; + setShowTodoEditor(false); + return todoId; + }; + + return ( +
+ + + {showTodoEditor && } +
+ ); +}; + +export default AddTodoSection; diff --git a/components/ui-elements/editors/todo-editor/TodoEditor.tsx b/components/ui-elements/editors/todo-editor/TodoEditor.tsx index d7e884135..b90bad40e 100644 --- a/components/ui-elements/editors/todo-editor/TodoEditor.tsx +++ b/components/ui-elements/editors/todo-editor/TodoEditor.tsx @@ -1,20 +1,23 @@ -import { Todo } from "@/api/useProjectTodos"; -import { getTodoEditorContent } from "@/helpers/todos"; +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; +import { JSONContent } from "@tiptap/core"; import { EditorContent, useEditor } from "@tiptap/react"; -import { FC, useEffect } from "react"; -import useExtensions from "./useExtensions"; +import { Loader2, Save } from "lucide-react"; +import { FC, useEffect, useState } from "react"; +import LinkBubbleMenu from "../extensions/link-bubble-menu/LinkBubbleMenu"; +import useTodoEditorExtensions from "./useTodoEditorExtensions"; type TodoEditorProps = { - todos: Todo[]; + onSave: (json: JSONContent) => Promise; }; -const TodoEditor: FC = ({ todos }) => { - const extensions = useExtensions(); +const TodoEditor: FC = ({ onSave }) => { + const [isSaving, setIsSaving] = useState(false); + const extensions = useTodoEditorExtensions(); const editor = useEditor({ extensions, - editable: false, + content: "", immediatelyRender: false, - content: getTodoEditorContent(todos), }); useEffect(() => { @@ -22,13 +25,40 @@ const TodoEditor: FC = ({ todos }) => { editor.setOptions({ editorProps: { attributes: { - class: "prose w-full max-w-full pt-1 bg-inherit", + class: "prose w-full max-w-full px-2 bg-inherit border rounded-md", }, }, }); }, [editor]); - return ; + const saveItem = async (json: JSONContent) => { + setIsSaving(true); + const todoId = await onSave(json); + if (!todoId) return; + setIsSaving(false); + }; + + return ( + <> +

Describe the todo:

+ + {editor && } +
+ + + ); }; export default TodoEditor; diff --git a/components/ui-elements/editors/todo-editor/useTodoEditorExtensions.tsx b/components/ui-elements/editors/todo-editor/useTodoEditorExtensions.tsx new file mode 100644 index 000000000..880ac52c1 --- /dev/null +++ b/components/ui-elements/editors/todo-editor/useTodoEditorExtensions.tsx @@ -0,0 +1,88 @@ +import { queryPerson } from "@/api/usePeople"; +import { renderer } from "@/helpers/ui-notes-writer/suggestions"; +import { EditorOptions, mergeAttributes } from "@tiptap/core"; +import Document from "@tiptap/extension-document"; +import Highlight from "@tiptap/extension-highlight"; +import Link from "@tiptap/extension-link"; +import Mention from "@tiptap/extension-mention"; +import Typography from "@tiptap/extension-typography"; +import StarterKit from "@tiptap/starter-kit"; +import { useMemo } from "react"; +import LinkBubbleMenuHandler from "../extensions/link-bubble-menu/LinkBubbleMenuHandler"; + +const useTodoEditorExtensions = (): EditorOptions["extensions"] => { + const extensions = useMemo(() => { + return [ + StarterKit.configure({ + heading: false, + orderedList: false, + bulletList: false, + listItem: false, + codeBlock: false, + blockquote: false, + document: false, + }), + Document.extend({ + content: "paragraph", + }), + Highlight, + Link.extend({ inclusive: false }).configure({ + openOnClick: false, + HTMLAttributes: { + class: + "no-underline text-blue-400 hover:text-blue-600 hover:underline hover:underline-offset-2", + }, + }), + Typography, + Mention.extend({ + addAttributes() { + return { + id: { + default: null, + parseHTML: (element) => element.getAttribute("data-id"), + renderHTML: (attrs) => ({ + "data-id": attrs.id, + }), + }, + label: { + default: null, + parseHTML: (element) => element.getAttribute("data-label"), + renderHTML: (attrs) => ({ + "data-label": attrs.label, + }), + }, + recordId: { + default: null, + parseHTML: (element) => element.getAttribute("data-record-id"), + renderHTML: (attrs) => ({ + "data-record-id": attrs.recordId, + }), + }, + }; + }, + }).configure({ + HTMLAttributes: { + class: + "text-blue-400 no-underline hover:underline underline-offset-2", + }, + renderHTML: ({ options, node }) => [ + "a", + mergeAttributes( + { href: `/people/${node.attrs.id}` }, + options.HTMLAttributes + ), + `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`, + ], + suggestion: { + items: ({ query }) => queryPerson(query), + render: renderer, + }, + }), + LinkBubbleMenuHandler, + ]; + }, []); + + return extensions; +}; + +export default useTodoEditorExtensions; diff --git a/components/ui-elements/editors/todo-viewer/TodoViewer.tsx b/components/ui-elements/editors/todo-viewer/TodoViewer.tsx new file mode 100644 index 000000000..423bca18d --- /dev/null +++ b/components/ui-elements/editors/todo-viewer/TodoViewer.tsx @@ -0,0 +1,39 @@ +import { Todo } from "@/api/useProjectTodos"; +import { getTodoViewerContent } from "@/helpers/todos"; +import { EditorContent, useEditor } from "@tiptap/react"; +import { FC, useEffect } from "react"; +import useExtensions from "./useExtensions"; + +type TodoViewerProps = { + todos: Todo[]; +}; + +const TodoViewer: FC = ({ todos }) => { + const extensions = useExtensions(); + const editor = useEditor({ + extensions, + editable: false, + immediatelyRender: false, + content: getTodoViewerContent(todos), + }); + + useEffect(() => { + if (!editor) return; + editor.setOptions({ + editorProps: { + attributes: { + class: "prose w-full max-w-full pt-1 bg-inherit", + }, + }, + }); + }, [editor]); + + useEffect(() => { + if (!editor) return; + editor.commands.setContent(getTodoViewerContent(todos)); + }, [todos, editor]); + + return ; +}; + +export default TodoViewer; diff --git a/components/ui-elements/editors/todo-editor/useExtensions.ts b/components/ui-elements/editors/todo-viewer/useExtensions.ts similarity index 100% rename from components/ui-elements/editors/todo-editor/useExtensions.ts rename to components/ui-elements/editors/todo-viewer/useExtensions.ts diff --git a/components/ui-elements/project-details/next-actions.tsx b/components/ui-elements/project-details/next-actions.tsx index 4732ce396..bafe3b1a4 100644 --- a/components/ui-elements/project-details/next-actions.tsx +++ b/components/ui-elements/project-details/next-actions.tsx @@ -3,7 +3,8 @@ import useProjectTodos from "@/api/useProjectTodos"; import { FC, useEffect, useState } from "react"; import DefaultAccordionItem from "../accordion/DefaultAccordionItem"; import { getTodoText } from "../editors/helpers/text-generation"; -import TodoEditor from "../editors/todo-editor/TodoEditor"; +import AddTodoSection from "../editors/todo-editor/AddTodoSection"; +import TodoViewer from "../editors/todo-viewer/TodoViewer"; import LegacyNextActions from "./legacy-next-actions"; type ProjectNextActionsProps = { @@ -15,7 +16,7 @@ const ProjectNextActions: FC = ({ projectId }) => { const [project, setProject] = useState( projects?.find((p) => p.id === projectId) ); - const { projectTodos } = useProjectTodos(projectId); + const { projectTodos, createTodo } = useProjectTodos(projectId); useEffect(() => { setProject(projects?.find((p) => p.id === projectId)); @@ -32,7 +33,10 @@ const ProjectNextActions: FC = ({ projectId }) => { !!project?.othersNextActions } > - {projectTodos && } + + + {projectTodos && } + ); diff --git a/docs/releases/next.md b/docs/releases/next.md index 1bf0ec152..03061ac5b 100644 --- a/docs/releases/next.md +++ b/docs/releases/next.md @@ -1,7 +1,9 @@ -# Sortierung der Kunden in Wochenplanung korrigieren (Version :VERSION) +# Schnelles Hinzufügen von Aufgaben ermöglichen (Version :VERSION) -- In der Wochenplanung wird nun immer die gesamte Pipeline der Kunden für die Sortierung herangezogen und nicht nur die Pipeline für der Projekte, die gerade nicht auf On Hold gesetzt wurden. -- Projekte, die keinem Kunden zugeordnet sind, werden nun auch in der Planung angezeigt. +- Einen Button hinzugefügt, um schnell ein neues Todo hinzufügen zu können. +- Dieser Button erscheint bei der täglichen Aufgabenliste. +- Dieser Button erscheint bei der Projekt-Aufgabenliste. +- Die Liste der nächsten Aufgaben im Projekt wird nun besser aktuell gehalten, wenn neue Todos hinzu kommen oder sich deren Status ändert. ## In Arbeit @@ -44,7 +46,6 @@ - Das Datum des letzten Uploads von CRM Daten anzeigen. - Die Uploads für CRM Projekte sollen ähnlich wie bei den Finanzdaten eine Historie ermöglichen. - Eine "Lean-Ansicht" wäre toll, zum Beispiel, wenn ich Notizen zu einem Projekt sehen möchte, dann scrolle ich einfach durch die Notizen ohne erst Akkordions aufklappen zu müssen. -- Wenn ich eine Aufgabe abgeschlossen haben, möchte ich sehr häufig eine Notiz erfassen und eine Folgeaufgabe. Das ist im Moment recht kompliziert, weil ich erst ins Projekt, dann dort die Notizen aufklappen, eine neue Aktivität erzeugen und schließlich dort wieder die Notizen aufklappen, bevor ich etwas notieren kann. Besser wäre ein Button: "Done and take note" oder so. ### Finanzdaten diff --git a/helpers/todos.ts b/helpers/todos.ts index bb216eb76..225f668cf 100644 --- a/helpers/todos.ts +++ b/helpers/todos.ts @@ -105,7 +105,7 @@ export const getTodoContent = ( : getParagraphWithLinkToActivity(activityId), ]; -export const getTodoEditorContent = (todos: Todo[]): JSONContent => ({ +export const getTodoViewerContent = (todos: Todo[]): JSONContent => ({ type: "doc", content: [ { diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 000000000..1f53798bb --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: /