diff --git a/src/context/todo.context.tsx b/src/context/todo.context.tsx index 721aebda..5340c24b 100644 --- a/src/context/todo.context.tsx +++ b/src/context/todo.context.tsx @@ -14,6 +14,7 @@ import { useUpdateTodo } from '@/services/hooks/todo/update-todo.hook' import { useGetTodos } from '@/services/hooks/todo/get-todos.hook' import type { FetchedTodo, Todo } from '@/services/hooks/todo/todo.interface' import { playAlarm } from '@/common/playAlarm' +import { sleep } from '@/common/utils/timeout' export enum TodoViewType { Day = 'day', @@ -159,17 +160,21 @@ export function TodoProvider({ children }: { children: React.ReactNode }) { ? Math.max(...sameDateTodos.map((t) => t.order || 0)) : 0 - const [err, _] = await safeAwait( - addTodoAsync({ - text: input.text, - completed: false, - date: input.date, - priority: input.priority || TodoPriority.Low, - category: input.category || '', - order: maxOrder + 1, - description: input.notes || '', - }) - ) + const item: any = { + text: input.text, + completed: false, + date: input.date, + priority: input.priority || TodoPriority.Low, + category: input.category || '', + order: maxOrder + 1, + description: input.notes || '', + } + const id = `temp-${Date.now()}` + old.unshift({ ...item, order: 0, id }) + setTodos(() => old) + + await sleep(3000) + const [err, _] = await safeAwait(addTodoAsync(item)) if (err) { const content = translateError(err) if (typeof content === 'string') { @@ -201,6 +206,20 @@ export function TodoProvider({ children }: { children: React.ReactNode }) { ) } const isCompleted = !current.completed + + setTodos((prev) => { + if (!prev) return prev + return prev.map((todo) => { + if (todo.id === id || todo.onlineId === id) { + return { + ...todo, + completed: isCompleted, + } + } + return todo + }) + }) + const [err, _] = await safeAwait( updateTodoAsync({ id: onlineId, @@ -214,9 +233,9 @@ export function TodoProvider({ children }: { children: React.ReactNode }) { showToast(translateError(err) as string, 'error') return } - refetch() - Analytics.event('todo_toggled') + if (isCompleted) playAlarm('done_todo') + Analytics.event('todo_toggled') } const updateTodo = async (id: string, updates: Partial>) => { diff --git a/src/context/widget-visibility.context.tsx b/src/context/widget-visibility.context.tsx index b2524d72..218751ac 100644 --- a/src/context/widget-visibility.context.tsx +++ b/src/context/widget-visibility.context.tsx @@ -12,8 +12,6 @@ import CalendarLayout from '@/layouts/widgets/calendar/calendar' import { ComboWidget } from '@/layouts/widgets/comboWidget/combo-widget.layout' import { NetworkLayout } from '@/layouts/widgets/network/network.layout' import { NewsLayout } from '@/layouts/widgets/news/news.layout' -import { NotesLayout } from '@/layouts/widgets/notes/notes.layout' -import { TodosLayout } from '@/layouts/widgets/todos/todos' import { ToolsLayout } from '@/layouts/widgets/tools/tools.layout' import { WeatherLayout } from '@/layouts/widgets/weather/weather.layout' import { WigiArzLayout } from '@/layouts/widgets/wigiArz/wigi_arz.layout' @@ -22,6 +20,7 @@ import { useAuth } from './auth.context' import { CurrencyProvider } from './currency.context' import { showToast } from '@/common/toast' import { YadkarWidget } from '@/layouts/widgets/yadkar/yadkar' +import { TodoProvider } from './todo.context' export enum WidgetKeys { comboWidget = 'comboWidget', @@ -65,7 +64,11 @@ export const widgetItems: WidgetItem[] = [ emoji: '📒', label: 'یادکار (وظایف و یادداشت)', order: 0, - node: , + node: ( + + + + ), canToggle: true, isNew: true, }, @@ -139,24 +142,6 @@ export const widgetItems: WidgetItem[] = [ disabled: true, soon: true, }, - { - id: WidgetKeys.todos, - emoji: '✅', - label: 'وظایف', - order: 2, - node: , - canToggle: false, - disabled: true, - }, - { - id: WidgetKeys.notes, - emoji: '📝', - label: 'یادداشت‌ها', - order: 7, - node: , - canToggle: false, - disabled: true, - }, ] interface WidgetVisibilityContextType { diff --git a/src/layouts/widgetify-card/widgetify.layout.tsx b/src/layouts/widgetify-card/widgetify.layout.tsx index 2d4787b8..a4c09ae6 100644 --- a/src/layouts/widgetify-card/widgetify.layout.tsx +++ b/src/layouts/widgetify-card/widgetify.layout.tsx @@ -5,7 +5,6 @@ import { useAuth } from '@/context/auth.context' import { useGeneralSetting } from '@/context/general-setting.context' import { WidgetContainer } from '../widgets/widget-container' import { NotificationCenter } from './notification-center/notification-center' -import { TodoOverviewCard } from './overviewCards/todo-overviewCard' import { Pet } from './pets/pet' import { PetProvider } from './pets/pet.context' import Snowfall from 'react-snowfall' @@ -26,7 +25,6 @@ export const WidgetifyLayout = () => { const newBlurMode = !blurMode updateSetting('blurMode', newBlurMode) } - return (
@@ -60,7 +58,7 @@ export const WidgetifyLayout = () => {
- + {/* */} {/* */}
diff --git a/src/layouts/widgets/calendar/calendar.tsx b/src/layouts/widgets/calendar/calendar.tsx index 9226eabc..52057373 100644 --- a/src/layouts/widgets/calendar/calendar.tsx +++ b/src/layouts/widgets/calendar/calendar.tsx @@ -1,36 +1,112 @@ -import type React from 'react' +import { type ReactNode, useState } from 'react' +import GoogleCalendar from '@/assets/google-calendar.png' import { useDate } from '@/context/date.context' import { WidgetContainer } from '../widget-container' import { CalendarGrid } from './components/calendar-grid' import { CalendarHeader } from './components/calendar-header' -import { DaySummary } from './components/day-summary' +import { GoogleCalendarView } from './components/google-calendar/google-calendar-view' +import { FcCalendar } from 'react-icons/fc' // استفاده از لوگوی رنگی گوگل برای جلوه بهتر +import Analytics from '@/analytics' -const CalendarLayout: React.FC = () => { - const { currentDate, selectedDate, setCurrentDate, setSelectedDate, goToToday } = - useDate() +interface TabItem { + label: string + value: string + icon?: ReactNode +} + +interface CalendarTabSelectorProps { + tabs: TabItem[] + activeTab: string + setActiveTab: (tab: string) => void +} +const CalendarTabSelector: React.FC = ({ + tabs, + activeTab, + setActiveTab, +}) => { return ( - - +
+
+ {tabs.map((tab) => ( + + ))} +
+
+ ) +} - , + }, + { + label: 'گوگل‌کلندر', + value: 'google', + icon: ( + Google Calendar + ), + }, +] +const CalendarLayout: React.FC = () => { + const { currentDate, selectedDate, setCurrentDate, setSelectedDate, goToToday } = + useDate() + const [activeTab, setActiveTab] = useState('calendar') + + const onSetActiveTab = (tab: string) => { + setActiveTab(tab) + Analytics.event(`calendar_tab_switch_to_${tab}`) + } -
- + return ( + +
+ {activeTab === 'calendar' ? ( + <> + +
+ +
+ + ) : ( + + )}
+ +
) } diff --git a/src/layouts/widgets/calendar/components/calendar-grid.tsx b/src/layouts/widgets/calendar/components/calendar-grid.tsx index e5c86a6c..2edc774a 100644 --- a/src/layouts/widgets/calendar/components/calendar-grid.tsx +++ b/src/layouts/widgets/calendar/components/calendar-grid.tsx @@ -1,6 +1,5 @@ import { useAuth } from '@/context/auth.context' import { useGeneralSetting } from '@/context/general-setting.context' -import { useTodoStore } from '@/context/todo.context' import { useGetEvents } from '@/services/hooks/date/getEvents.hook' import type React from 'react' import { useState } from 'react' @@ -29,7 +28,12 @@ export const CalendarGrid: React.FC = ({ const [clickedElement, setClickedElement] = useState(null) const { data: events } = useGetEvents() - const { todos } = useTodoStore() + + const eventsForCalendar = events || { + gregorianEvents: [], + hijriEvents: [], + shamsiEvents: [], + } const { data: calendarData, refetch } = useGetCalendarData( isAuthenticated, @@ -76,11 +80,10 @@ export const CalendarGrid: React.FC = ({ key={`day-${i}`} currentDate={currentDate} day={i + 1} - events={events} + events={eventsForCalendar} googleEvents={calendarData?.googleEvents || []} selectedDateStr={selectedDateStr} setSelectedDate={setSelectedDate} - todos={todos} timezone={timezone.value} moods={calendarData?.moods ?? []} onClick={(element) => { @@ -109,8 +112,7 @@ export const CalendarGrid: React.FC = ({ triggerRef={{ current: clickedElement }} content={ refetch()} /> diff --git a/src/layouts/widgets/calendar/components/day-summary.tsx b/src/layouts/widgets/calendar/components/day-summary.tsx index f1ca8703..9273f91c 100644 --- a/src/layouts/widgets/calendar/components/day-summary.tsx +++ b/src/layouts/widgets/calendar/components/day-summary.tsx @@ -1,11 +1,9 @@ import type React from 'react' -import { FiCalendar, FiClipboard } from 'react-icons/fi' +import { FiCalendar } from 'react-icons/fi' import { useAuth } from '@/context/auth.context' -import { useTodoStore } from '@/context/todo.context' import { useGetEvents } from '@/services/hooks/date/getEvents.hook' import { useGetGoogleCalendarEvents } from '@/services/hooks/date/getGoogleCalendarEvents.hook' import { - formatDateStr, getGregorianEvents, getHijriEvents, getShamsiEvents, @@ -18,7 +16,6 @@ interface DaySummaryProps { export const DaySummary: React.FC = ({ selectedDate }) => { const { user } = useAuth() - const { todos } = useTodoStore() const { data: events } = useGetEvents() const startOfMonth = selectedDate.clone().startOf('jMonth').toDate() @@ -30,8 +27,6 @@ export const DaySummary: React.FC = ({ selectedDate }) => { endOfMonth ) - const selectedDateStr = formatDateStr(selectedDate) - const googleEventsForSelectedDate = googleEvents.filter((event) => { if (!event || !event.start || !event.start.dateTime) { return false @@ -44,11 +39,6 @@ export const DaySummary: React.FC = ({ selectedDate }) => { const googleEventCount = googleEventsForSelectedDate.length - const todosForSelectedDate = todos.filter((todo) => todo.date === selectedDateStr) - - const completedTodos = todosForSelectedDate.filter((todo) => todo.completed).length - const totalTodos = todosForSelectedDate.length - const shamsiEvents = events ? getShamsiEvents(events, selectedDate) : [] const gregorianEvents = events ? getGregorianEvents(events, selectedDate) : [] const hijriEvents = events ? getHijriEvents(events, selectedDate) : [] @@ -59,8 +49,8 @@ export const DaySummary: React.FC = ({ selectedDate }) => { return (
-
-
+
+
= ({ selectedDate }) => { 0 ? 'text-blue-500' : 'text-gray-400'}`} /> -
+
{totalEventsCount} مناسبت
@@ -84,30 +74,6 @@ export const DaySummary: React.FC = ({ selectedDate }) => {
- - {/* Todos card */} -
- 0 ? 'text-green-500' : 'text-gray-400'}`} - /> -
- {' '} -
- {totalTodos} وظیفه -
-
- {totalTodos > 0 - ? `${completedTodos} از ${totalTodos} تکمیل شده` - : 'بدون وظیفه'} -
-
-
diff --git a/src/layouts/widgets/calendar/components/day/day.tsx b/src/layouts/widgets/calendar/components/day/day.tsx index 55ab59a1..c92c2501 100644 --- a/src/layouts/widgets/calendar/components/day/day.tsx +++ b/src/layouts/widgets/calendar/components/day/day.tsx @@ -12,13 +12,11 @@ import { getShamsiEvents, } from '../../utils' import { moodOptions } from './toolTipContent' -import type { Todo } from '@/services/hooks/todo/todo.interface' interface DayItemProps { day: number currentDate: jalaliMoment.Moment events: FetchedAllEvents - todos: Todo[] selectedDateStr: string setSelectedDate: (date: jalaliMoment.Moment) => void googleEvents: GoogleCalendarEvent[] @@ -31,8 +29,6 @@ export function DayItem({ day, currentDate, events, - googleEvents = [], - todos, selectedDateStr, setSelectedDate, timezone, @@ -46,17 +42,13 @@ export function DayItem({ const todayHijriEvents = getHijriEvents(events, cellDate) const todayGregorianEvents = getGregorianEvents(events, cellDate) - const googleEventsForDay = filterGoogleEventsByDate(googleEvents, cellDate) - const hasGoogleEvents = googleEventsForDay.length > 0 - - const hasEvent = todayShamsiEvents.length || hasGoogleEvents + const hasEvent = todayShamsiEvents.length const eventIcons = [ ...todayGregorianEvents.filter((event) => event.icon).map((event) => event.icon), ...todayShamsiEvents.filter((event) => event.icon).map((event) => event.icon), ...todayHijriEvents.filter((event) => event.icon).map((event) => event.icon), ].filter(Boolean) as string[] - const hasTodo = todos.some((todo) => todo.date === dateStr) const isSelected = selectedDateStr === dateStr const isCurrentDay = isToday(cellDate, timezone) @@ -134,7 +126,7 @@ export function DayItem({ ) } - if (!hasEvent && !hasTodo) return null + if (!hasEvent) return null return (
@@ -143,9 +135,6 @@ export function DayItem({ className={`w-0.5 h-0.5 rounded-full ${isHolidayEvent ? 'bg-red-500' : 'bg-blue-500/80'} shadow-sm`} /> )} - {hasTodo && ( - - )}
) } @@ -181,20 +170,3 @@ const isToday = (date: jalaliMoment.Moment, timezone: string) => { date.jYear() === today.jYear() ) } - -const filterGoogleEventsByDate = ( - googleEvents: GoogleCalendarEvent[], - date: jalaliMoment.Moment -) => { - return googleEvents.filter((event) => { - if (event.eventType !== 'birthday') { - const eventDate = jalaliMoment(event.start.dateTime) - return ( - eventDate.jDate() === date.jDate() && - eventDate.jMonth() === date.jMonth() && - eventDate.jYear() === date.jYear() - ) - } - return undefined - }) -} diff --git a/src/layouts/widgets/calendar/components/day/toolTipContent.tsx b/src/layouts/widgets/calendar/components/day/toolTipContent.tsx index 47b53972..3c54b1a5 100644 --- a/src/layouts/widgets/calendar/components/day/toolTipContent.tsx +++ b/src/layouts/widgets/calendar/components/day/toolTipContent.tsx @@ -1,5 +1,3 @@ -import moment from 'jalali-moment' -import { AiOutlineGoogle } from 'react-icons/ai' import { FaGlobeAsia } from 'react-icons/fa' import { FaMoon } from 'react-icons/fa6' import { HiSparkles } from 'react-icons/hi2' @@ -7,13 +5,11 @@ import { useState } from 'react' import type { FetchedAllEvents } from '@/services/hooks/date/getEvents.hook' import { convertShamsiToHijri, - filterGoogleEventsByDate, getGregorianEvents, getHijriEvents, getShamsiEvents, } from '../../utils' import { useDate } from '@/context/date.context' -import type { GoogleCalendarEvent } from '@/services/hooks/date/getGoogleCalendarEvents.hook' import type React from 'react' import { useAuth } from '@/context/auth.context' import { @@ -30,7 +26,6 @@ import Analytics from '@/analytics' interface CalendarDayDetailsProps { events: FetchedAllEvents - googleEvents: GoogleCalendarEvent[] eventIcon?: string moods: MoodEntry[] onMoodChange?: (mood: MoodType) => void @@ -69,7 +64,6 @@ export const moodOptions = [ export const CalendarDayDetails: React.FC = ({ events, - googleEvents, moods, onMoodChange, }) => { @@ -159,8 +153,7 @@ export const CalendarDayDetails: React.FC = ({ const jalali = selectedDate.format('jYYYY/jMM/jD') const jalaliDay = selectedDate.format('dddd') - const dayGoogleEvents = filterGoogleEventsByDate(googleEvents, selectedDate) - const totalEvents = dayEvent.length + dayGoogleEvents.length + const totalEvents = dayEvent.length const holidayStyle = isHoliday ? 'from-orange-600 to-red-700' : 'from-sky-500 to-blue-700' @@ -234,26 +227,6 @@ export const CalendarDayDetails: React.FC = ({ {totalEvents > 0 && (
- {dayGoogleEvents.map((event, idx) => ( -
- -
-
- {event.summary} -
-
- {moment(event.start.dateTime).format('HH:mm')} -
-
-
- ))} - {dayEvent.map((event, idx) => (
{ + const { user, isAuthenticated } = useAuth() + const { selectedDate, isToday, setSelectedDate, currentDate } = useDate() + const containerRef = useRef(null) + const [currentTime, setCurrentTime] = useState(new Date()) + + const isCalendarConnected = user?.connections?.includes('google') || false + + useEffect(() => { + const timer = setInterval(() => setCurrentTime(new Date()), 60000) + return () => clearInterval(timer) + }, []) + + useEffect(() => { + if (containerRef.current && isToday(selectedDate)) { + const now = new Date() + const scrollPos = + ((now.getHours() * 60 + now.getMinutes()) / 60) * HOUR_HEIGHT - 100 + containerRef.current.scrollTo({ top: scrollPos, behavior: 'smooth' }) + } + }, [selectedDate]) + + const { data: events } = useGetGoogleCalendarEvents( + isCalendarConnected, + selectedDate.clone().startOf('day').toDate(), + selectedDate.clone().endOf('day').toDate() + ) + + const handleEventClick = (event: any) => { + Analytics.event('google_calendar_event_click') + if (event.hangoutLink) { + window.open(event.hangoutLink, '_blank') + } else if (event.location) { + window.open( + `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(event.location)}`, + '_blank' + ) + } + } + + const onNextDay = () => { + setSelectedDate(selectedDate.clone().add(1, 'jD')) + Analytics.event('google_calendar_next_day') + } + + const onPrevDay = () => { + setSelectedDate(selectedDate.clone().add(-1, 'jD')) + Analytics.event('google_calendar_prev_day') + } + + const onGoToToday = () => { + setSelectedDate(currentDate.clone()) + Analytics.event('google_calendar_go_to_today') + } + + const getInitials = (email: string) => + email.split('@')[0].substring(0, 2).toUpperCase() + + const hours = Array.from({ length: 24 }, (_, i) => i) + + const nextEvent = events?.find((e) => new Date(e.start.dateTime) > currentTime) + + if (!isCalendarConnected) { + return ( +
+

+ {isAuthenticated + ? 'برای مشاهده رویدادهای گوگل، لطفاً حساب گوگل خود را متصل کنید.' + : 'برای مشاهده رویدادهای گوگل، لطفاً وارد حساب کاربری خود شوید.'} +

+ +
+ ) + } + + return ( +
+
+
+ {events?.length || 0} رویداد +
+ {nextEvent && ( +
+ بعدی: {new Date(nextEvent.start.dateTime).getHours()}: + {new Date(nextEvent.start.dateTime) + .getMinutes() + .toString() + .padStart(2, '0')}{' '} + - {nextEvent.summary} +
+ )} +
+ +
+
+ {hours.map((hour) => { + const isPast = + isToday(selectedDate) && currentTime.getHours() > hour + return ( +
+
+ {hour === 0 ? '۱۲' : hour <= 12 ? hour : hour - 12} + + {hour < 12 ? 'ق.ظ' : 'ب.ظ'} + +
+
+
+ ) + })} + + {isToday(selectedDate) && ( +
+
+
+
+ )} + + {events?.map((event) => ( + + ))} +
+
+ +
+ + + + + +
+
+ ) +} diff --git a/src/layouts/widgets/calendar/components/google-calendar/google-event.item.tsx b/src/layouts/widgets/calendar/components/google-calendar/google-event.item.tsx new file mode 100644 index 00000000..436d0a8c --- /dev/null +++ b/src/layouts/widgets/calendar/components/google-calendar/google-event.item.tsx @@ -0,0 +1,118 @@ +import Tooltip from '@/components/toolTip' +import { HiOutlineMapPin, HiOutlineVideoCamera } from 'react-icons/hi2' + +interface CalendarEventProps { + event: any + isToday: boolean + currentTime: Date + hourHeight: number + onEventClick: (event: any) => void + getInitials: (email: string) => string +} + +export const CalendarEvent = ({ + event, + isToday, + currentTime, + onEventClick, + getInitials, + hourHeight, +}: CalendarEventProps) => { + const start = new Date(event.start.dateTime) + const end = new Date(event.end.dateTime) + const isNow = isToday && currentTime >= start && currentTime <= end + const duration = (end.getTime() - start.getTime()) / 60000 + const isShort = duration <= 45 + const hasAction = !!(event.hangoutLink || event.location) + const isEnd = end <= currentTime + const getEventStyle = (event: any) => { + const start = new Date(event.start.dateTime) + const end = new Date(event.end.dateTime) + const startMinutes = start.getHours() * 60 + start.getMinutes() + const duration = (end.getTime() - start.getTime()) / 60000 + + return { + top: `${(startMinutes / 60) * hourHeight + 2}px`, + right: '38px', + left: '10px', + height: `${Math.max((duration / 60) * hourHeight - 4, 42)}px`, + } + } + + return ( +
onEventClick(event)} + className={`absolute z-20 px-3 py-2 overflow-hidden transition-all border-r-2 rounded-xl shadow-sm + ${hasAction ? 'cursor-pointer hover:shadow-md active:scale-[0.98]' : 'cursor-default'} + ${isNow ? 'bg-primary/15 border-primary backdrop-blur-md ring-1 ring-primary/30' : 'bg-base-300/90 hover:bg-base-300 border-primary/40'} + ${isEnd && 'opacity-60! border-r!'} + `} + style={getEventStyle(event)} + > +
+
+

+ {event.summary} +

+ {!isShort && event.hangoutLink && ( +
+ + میت +
+ )} +
+ +
+
+ + {start.toLocaleTimeString('fa-IR', { + hour: '2-digit', + minute: '2-digit', + })}{' '} + {' - '}{' '} + {end.toLocaleTimeString('fa-IR', { + hour: '2-digit', + minute: '2-digit', + })} + +
+ {event.location && !isShort && ( +
+ + + {event.location} + +
+ )} +
+ + {!isShort && event.attendees && ( +
+
+ {event.attendees + .slice(0, 3) + .map((attendee: any, idx: number) => ( + +
+ {getInitials(attendee.email)} +
+
+ ))} + {event.attendees.length > 3 && ( +
+ +{event.attendees.length - 3} +
+ )} +
+
+ )} +
+
+ ) +} diff --git a/src/layouts/widgets/todos/todo.item.tsx b/src/layouts/widgets/todos/todo.item.tsx index ec78aa1e..9e9f1245 100644 --- a/src/layouts/widgets/todos/todo.item.tsx +++ b/src/layouts/widgets/todos/todo.item.tsx @@ -44,8 +44,17 @@ export function TodoItem({ const [showEditModal, setShowEditModal] = useState(false) const isUpdating = useIsMutating({ mutationKey: ['updateTodo'] }) > 0 const { mutateAsync, isPending } = useRemoveTodo(todo.onlineId || todo.id) + const [isSyncing, setIsSyncing] = useState(todo.id.startsWith('temp-') || false) + const isTemp = todo.id.startsWith('temp-') const handleDelete = (e: React.MouseEvent) => { + if (isTemp) { + return showToast( + 'این وظیفه هنوز همگام‌سازی نشده است و نمی‌توان آن را حذف کرد.', + 'error' + ) + } + e.stopPropagation() if (isPending) return if (!isAuthenticated) return showToast('برای حذف وظیفه باید وارد شوید', 'error') @@ -54,19 +63,21 @@ export function TodoItem({ } const handleEdit = (e: React.MouseEvent) => { + if (isTemp) { + return showToast( + 'این وظیفه هنوز همگام‌سازی نشده است و نمی‌توان آن را ویرایش کرد.', + 'error' + ) + } + e.stopPropagation() if (!isAuthenticated) return showToast('برای ویرایش وظیفه باید وارد شوید', 'error') setShowEditModal(true) } - const handleExpand = (e: React.MouseEvent) => { - e.stopPropagation() - setExpanded(!expanded) - } - const onConfirmDelete = async () => { - if (isPending) return + if (isPending || isSyncing) return const onlineId = todo.onlineId || todo.id if (validate(onlineId)) { @@ -92,67 +103,24 @@ export function TodoItem({ refetchTodos() } - const getBorderStyle = () => { - switch (todo.priority) { - case 'high': - return '!border-error' - case 'medium': - return '!border-warning' - case 'low': - return '!border-success' - default: - return '!border-primary' - } - } - - const getCheckedCheckboxStyle = () => { - switch (todo.priority) { - case 'high': - return '!border-error !bg-error' - case 'medium': - return '!border-warning !bg-warning' - case 'low': - return '!border-success !bg-success' - default: - return '!border-primary !bg-primary' - } - } - - const getUnCheckedCheckboxStyle = () => { - switch (todo.priority) { - case 'high': - return '!border-error' - case 'medium': - return '!border-warning' - case 'low': - return '!border-success' - default: - return '!border-primary' - } - } - - const getPriorityColor = () => { - switch (todo.priority) { - case 'high': - return 'bg-error/10 text-error' - case 'medium': - return 'bg-warning/10 text-warning' - case 'low': - return 'bg-success/10 text-success' - default: - return 'bg-primary text-primary-content' - } - } - - const onToggleClick = (e: React.MouseEvent) => { + const onToggleClick = async (e: React.MouseEvent) => { e.stopPropagation() - if (isUpdating) return + if (isUpdating || isSyncing) return if (!isAuthenticated) { return showToast('برای تغییر وضعیت وظیفه باید وارد شوید', 'error') } + setIsSyncing(true) + try { + await toggleTodo(todo.id) + } finally { + setIsSyncing(false) + } + } - toggleTodo(todo.id) + const handleExpand = (e: React.MouseEvent) => { + e.stopPropagation() + setExpanded(!expanded) } return ( @@ -172,10 +140,14 @@ export function TodoItem({
onToggleClick(e)} />
@@ -189,22 +161,28 @@ export function TodoItem({ {/* Actions */}
- - + {isSyncing ? ( + + ) : ( + <> + + + + )}
) } diff --git a/src/services/hooks/calendar/get-calendarData.hook.ts b/src/services/hooks/calendar/get-calendarData.hook.ts index b0107801..d4b254f9 100644 --- a/src/services/hooks/calendar/get-calendarData.hook.ts +++ b/src/services/hooks/calendar/get-calendarData.hook.ts @@ -18,10 +18,8 @@ export const useGetCalendarData = (enabled: boolean, start: string, end: string) queryFn: async () => getCalendarData(start, end), retry: 0, enabled: enabled && !!start && !!end, - initialData: { - moods: [], - googleEvents: [], - }, + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, refetchOnWindowFocus: false, }) } diff --git a/src/services/hooks/date/getEvents.hook.ts b/src/services/hooks/date/getEvents.hook.ts index e440aa97..8ce34ab2 100644 --- a/src/services/hooks/date/getEvents.hook.ts +++ b/src/services/hooks/date/getEvents.hook.ts @@ -19,11 +19,7 @@ export const useGetEvents = () => { queryKey: ['get-events'], queryFn: async () => getEvents(), retry: 0, - initialData: { - shamsiEvents: [], - gregorianEvents: [], - hijriEvents: [], - }, + staleTime: 10 * 60 * 1000, }) } diff --git a/src/services/hooks/date/getGoogleCalendarEvents.hook.ts b/src/services/hooks/date/getGoogleCalendarEvents.hook.ts index f20bfa41..a364a3ae 100644 --- a/src/services/hooks/date/getGoogleCalendarEvents.hook.ts +++ b/src/services/hooks/date/getGoogleCalendarEvents.hook.ts @@ -82,7 +82,7 @@ export const useGetGoogleCalendarEvents = ( const cacheKey = `${startParam}-${endParam}` return useQuery({ - queryKey: ['google-calendar-events', startParam, endParam], + queryKey: ['google-calendar-events', cacheKey], queryFn: async () => { if (cache.has(cacheKey)) { return cache.get(cacheKey) || [] @@ -93,7 +93,6 @@ export const useGetGoogleCalendarEvents = ( return events }, retry: 1, - // initialData: cache.get(cacheKey) || [], enabled: enabled, }) } diff --git a/src/styles/theme/esteghlal.css b/src/styles/theme/esteghlal.css index ae419e61..c16866ee 100644 --- a/src/styles/theme/esteghlal.css +++ b/src/styles/theme/esteghlal.css @@ -1,34 +1,34 @@ @plugin "daisyui/theme" { name: "esteghlal"; default: false; - prefersdark: false; + prefersdark: true; color-scheme: "dark"; --color-base-100: oklch(28% 0.091 267.935); - --color-base-200: oklch(37% 0.146 265.522); - --color-base-300: oklch(42% 0.199 265.638); - --color-base-content: oklch(93% 0.032 255.585); - --color-primary: oklch(68% 0.169 237.323); - --color-primary-content: oklch(27% 0.072 132.109); - --color-secondary: oklch(80% 0.105 251.813); - --color-secondary-content: oklch(28% 0.091 267.935); - --color-accent: oklch(83% 0.145 321.434); - --color-accent-content: oklch(29% 0.136 325.661); - --color-neutral: oklch(54% 0.245 262.881); - --color-neutral-content: oklch(97% 0.014 254.604); - --color-info: oklch(71% 0.143 215.221); - --color-info-content: oklch(98% 0.019 200.873); - --color-success: oklch(72% 0.219 149.579); - --color-success-content: oklch(98% 0.018 155.826); - --color-warning: oklch(79% 0.184 86.047); - --color-warning-content: oklch(98% 0.026 102.212); - --color-error: oklch(63% 0.237 25.331); - --color-error-content: oklch(97% 0.013 17.38); - --radius-selector: 1rem; - --radius-field: 0rem; - --radius-box: 0.25rem; + --color-base-200: oklch(35% 0.12 265.522); + --color-base-300: oklch(40% 0.15 265.638); + --color-base-content: oklch(95% 0.02 260); + --color-primary: oklch(65% 0.22 260); + --color-primary-content: oklch(100% 0 0); + --color-secondary: oklch(75% 0.15 251.813); + --color-secondary-content: oklch(20% 0.08 267.935); + --color-accent: oklch(80% 0.12 210); + --color-accent-content: oklch(20% 0.08 267.935); + --color-neutral: oklch(20% 0.06 267.935); + --color-neutral-content: oklch(90% 0.02 267.935); + --color-info: oklch(70% 0.16 245); + --color-info-content: oklch(100% 0 0); + --color-success: oklch(75% 0.18 150); + --color-success-content: oklch(20% 0.1 150); + --color-warning: oklch(82% 0.18 90); + --color-warning-content: oklch(20% 0.1 90); + --color-error: oklch(65% 0.22 25); + --color-error-content: oklch(100% 0 0); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; --size-selector: 0.25rem; --size-field: 0.25rem; --border: 1px; - --depth: 0; - --noise: 1; + --depth: 1; + --noise: 0; } \ No newline at end of file