diff --git a/src/context/todo.context.tsx b/src/context/todo.context.tsx index 7d7416db..b5bd0b2f 100644 --- a/src/context/todo.context.tsx +++ b/src/context/todo.context.tsx @@ -46,6 +46,7 @@ interface TodoContextType { clearCompleted: (date?: string) => void updateOptions: (options: Partial) => void reorderTodos: (todos: Todo[]) => Promise + isPending: boolean } const TodoContext = createContext(null) @@ -57,9 +58,9 @@ export function TodoProvider({ children }: { children: React.ReactNode }) { viewMode: TodoViewType.All, }) - const { data: fetchedTodos, refetch } = useGetTodos(isAuthenticated) - const { mutateAsync: addTodoAsync } = useAddTodo() - const { mutateAsync: updateTodoAsync } = useUpdateTodo() + const { data: fetchedTodos, refetch, isPending } = useGetTodos(isAuthenticated) + const { mutateAsync: addTodoAsync, isPending: isAdding } = useAddTodo() + const { mutateAsync: updateTodoAsync, isPending: isUpdating } = useUpdateTodo() const { mutateAsync: reorderTodosAsync } = useReorderTodos() const didLoadInitialOptions = useRef(false) @@ -309,6 +310,7 @@ export function TodoProvider({ children }: { children: React.ReactNode }) { todoOptions, reorderTodos, refetchTodos: refetch, + isPending: isPending || isAdding || isUpdating, }} > {children} diff --git a/src/layouts/bookmark/components/modal/advanced.modal.tsx b/src/layouts/bookmark/components/modal/advanced.modal.tsx index 28b922e2..6bfdd5fd 100644 --- a/src/layouts/bookmark/components/modal/advanced.modal.tsx +++ b/src/layouts/bookmark/components/modal/advanced.modal.tsx @@ -315,7 +315,7 @@ export function AdvancedModal({ title, onClose, isOpen, bookmark }: AdvancedModa size="md" onClick={() => onClose(null)} className={ - 'btn btn-circle !bg-base-300 hover:!bg-error/10 text-muted hover:!text-error px-10 border-none shadow-none rounded-xl transition-colors duration-300 ease-in-out' + 'btn btn-circle !bg-base-300 hover:!bg-error/10 text-muted hover:!text-error px-10 border-none shadow-none !rounded-2xl transition-colors duration-300 ease-in-out' } > لغو @@ -326,7 +326,7 @@ export function AdvancedModal({ title, onClose, isOpen, bookmark }: AdvancedModa size="md" isPrimary={true} className={ - 'btn btn-circle !w-fit px-8 border-none shadow-none text-secondary rounded-xl transition-colors duration-300 ease-in-out' + 'btn btn-circle !w-fit px-8 border-none shadow-none text-secondary !rounded-2xl transition-colors duration-300 ease-in-out' } > ذخیره diff --git a/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx b/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx index 28de46fc..e79d04d2 100644 --- a/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx +++ b/src/layouts/bookmark/components/modal/edit-bookmark.modal.tsx @@ -223,7 +223,7 @@ export function EditBookmarkModal({ size="md" disabled={isUpdating} className={ - 'btn btn-circle !bg-base-300 hover:!bg-error/10 text-muted hover:!text-error px-10 border-none shadow-none rounded-xl transition-colors duration-300 ease-in-out' + 'btn btn-circle !bg-base-300 hover:!bg-error/10 text-muted hover:!text-error px-10 border-none shadow-none !rounded-2xl transition-colors duration-300 ease-in-out' } > لغو @@ -239,7 +239,7 @@ export function EditBookmarkModal({ isPrimary={true} loading={isUpdating} className={ - 'btn btn-circle !w-fit px-8 border-none shadow-none text-secondary rounded-xl transition-colors duration-300 ease-in-out' + 'btn btn-circle !w-fit px-8 border-none shadow-none text-secondary !rounded-2xl transition-colors duration-300 ease-in-out' } > ذخیره diff --git a/src/layouts/widgets/todos/edit-todo-modal.tsx b/src/layouts/widgets/todos/edit-todo-modal.tsx new file mode 100644 index 00000000..5d51e545 --- /dev/null +++ b/src/layouts/widgets/todos/edit-todo-modal.tsx @@ -0,0 +1,216 @@ +import { useState, useRef, useEffect, useCallback } from 'react' +import { FiMessageSquare, FiTag, FiCalendar } from 'react-icons/fi' +import { TextInput } from '@/components/text-input' +import { Button } from '@/components/button/button' +import type { TodoPriority } from '@/context/todo.context' +import type { Todo } from '@/services/hooks/todo/todo.interface' +import Modal from '@/components/modal' +import { PRIORITY_OPTIONS } from '@/common/constant/priority_options' +import { PriorityButton } from '@/components/priority-options/priority-options' +import { ClickableTooltip } from '@/components/clickableTooltip' +import { DatePicker } from '@/components/date-picker/date-picker' +import type jalaliMoment from 'jalali-moment' +import { parseTodoDate } from './tools/parse-date' +import { showToast } from '@/common/toast' +import { useUpdateTodo } from '@/services/hooks/todo/update-todo.hook' +import { safeAwait } from '@/services/api' +import { translateError } from '@/utils/translate-error' +import { useQueryClient } from '@tanstack/react-query' +import Analytics from '@/analytics' + +interface EditTodoModalProps { + todo: Todo + isOpen: boolean + onClose: () => void +} + +export function EditTodoModal({ todo, isOpen, onClose }: EditTodoModalProps) { + const queryClient = useQueryClient() + + const [isDatePickerOpen, setIsDatePickerOpen] = useState(false) + const [selectedDate, setSelectedDate] = useState( + todo.date ? parseTodoDate(todo.date) : undefined + ) + const { mutateAsync, isPending } = useUpdateTodo() + const datePickerButtonRef = useRef(null) + + const [text, setText] = useState(todo.text) + const [notes, setNotes] = useState(todo.notes || '') + const [category, setCategory] = useState(todo.category || '') + const [priority, setPriority] = useState(todo.priority as TodoPriority) + + useEffect(() => { + setText(todo.text) + setNotes(todo.notes || '') + setCategory(todo.category || '') + setPriority(todo.priority as TodoPriority) + setSelectedDate(todo.date ? parseTodoDate(todo.date) : undefined) + Analytics.event('todo_edit_opened') + }, [todo]) + + const handleTextChange = useCallback((value: string) => { + setText(value) + }, []) + + const handleNotesChange = useCallback((e: React.ChangeEvent) => { + setNotes(e.target.value) + }, []) + + const handleCategoryChange = useCallback((value: string) => { + setCategory(value) + }, []) + + const handlePriorityChange = useCallback((newPriority: TodoPriority) => { + setPriority(newPriority) + }, []) + + const handleDateSelect = useCallback((date: jalaliMoment.Moment) => { + setSelectedDate(date) + setIsDatePickerOpen(false) + }, []) + + const handleSave = useCallback(async () => { + if (!text.trim()) { + showToast('متن وظیفه نمی‌تواند خالی باشد', 'error') + return + } + + const input = { + text, + notes, + category, + priority, + date: selectedDate?.add(3.5, 'hours').toISOString(), + } + + const [err, _] = await safeAwait( + mutateAsync({ + id: todo.onlineId || todo.id, + input, + }) + ) + + if (err) { + showToast(translateError(err) as any, 'error') + return + } + showToast('وظیفه با موفقیت ویرایش شد', 'success') + onClose() + queryClient.invalidateQueries({ queryKey: ['getTodos'] }) + }, [text, notes, category, priority, selectedDate, onClose]) + + return ( + +
+
+ +
+ +
+
+
+ + +
+ + + } + contentClassName="!p-0 !bg-transparent !border-none !shadow-none" + /> +
+
+ {PRIORITY_OPTIONS.map((option) => ( + + handlePriorityChange(option.value as TodoPriority) + } + /> + ))} +
+
+ +
+
+ +
+ +
+ +
+
+ +
+