From 3a074ef6c5349c24c2c26750bf1fd2e733123e45 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Thu, 15 Jan 2026 20:12:00 -0500 Subject: [PATCH] Add Support Project --- src/queries/UserQueries.jsx | 4 ++ src/utils/Chores.jsx | 22 +++++++++ src/utils/Fetcher.jsx | 59 +++++++++++++++++++++++- src/views/components/ChoreActionMenu.jsx | 2 +- src/views/components/NavBar.jsx | 8 +++- 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/queries/UserQueries.jsx b/src/queries/UserQueries.jsx index d414ec3..c9a97d8 100644 --- a/src/queries/UserQueries.jsx +++ b/src/queries/UserQueries.jsx @@ -46,6 +46,10 @@ export const useUserProfile = () => { const { data, error, isLoading } = useQuery({ queryKey: ['userProfile'], queryFn: async () => { + // the below code deleted because it cause issue when token expire and screen is off + // and then user comes back to the app after long time. the user profile fetch would fail + // and there is no retry for some reason. remove this seem to fix the issue. + if (!isTokenValid()) { throw new Error('Invalid or expired token, cannot fetch user profile') } diff --git a/src/utils/Chores.jsx b/src/utils/Chores.jsx index 8beb774..74e5226 100644 --- a/src/utils/Chores.jsx +++ b/src/utils/Chores.jsx @@ -345,3 +345,25 @@ export const ChoreFilters = userId => ({ return chore.createdBy && chore.createdBy === userId }, }) + +// Project filter function - separate from ChoreFilters since it's independent +export const filterByProject = (chores, selectedProject) => { + if ( + !selectedProject || + selectedProject === 'Default Project' || + selectedProject === 'default' + ) { + // Default project should show tasks without a project (projectId is null/undefined/empty) + // Based on ChoreEdit.jsx, default projects save projectId as null + return chores.filter( + chore => + !chore.projectId || chore.projectId === null || chore.projectId === '', + ) + } + + // For custom projects, match by project ID + return chores.filter(chore => { + // Match by project ID (this should be the primary way chores are linked to projects) + return chore.projectId === selectedProject + }) +} diff --git a/src/utils/Fetcher.jsx b/src/utils/Fetcher.jsx index ca1ffe9..c6456a2 100644 --- a/src/utils/Fetcher.jsx +++ b/src/utils/Fetcher.jsx @@ -186,6 +186,14 @@ const RejectChore = id => { }) } +const UndoChoreAction = id => { + return Fetch(`/chores/${id}/undo`, { + method: 'POST', + headers: HEADERS(), + body: JSON.stringify({}), + }) +} + const NudgeChore = (id, { message, notifyAllAssignees }) => { return Fetch(`/chores/${id}/nudge`, { method: 'POST', @@ -565,12 +573,15 @@ const RefreshToken = async () => { const basedURL = apiManager.getApiURL() // Check if running on native platform - const isNative = typeof window !== 'undefined' && window.Capacitor?.isNativePlatform?.() + const isNative = + typeof window !== 'undefined' && window.Capacitor?.isNativePlatform?.() if (isNative) { // For native platforms, send refresh token in request body const { Preferences } = await import('@capacitor/preferences') - const { value: refreshToken } = await Preferences.get({ key: 'refresh_token' }) + const { value: refreshToken } = await Preferences.get({ + key: 'refresh_token', + }) if (!refreshToken) { throw new Error('No refresh token available') @@ -782,6 +793,44 @@ const DeleteChildUser = childUserId => { }) } +// Project-related API functions +const GetProjects = () => { + return Fetch(`/projects`, { + method: 'GET', + headers: HEADERS(), + }) +} + +const GetProjectById = id => { + return Fetch(`/projects/${id}`, { + method: 'GET', + headers: HEADERS(), + }) +} + +const CreateProject = project => { + return Fetch(`/projects`, { + method: 'POST', + headers: HEADERS(), + body: JSON.stringify(project), + }) +} + +const UpdateProject = (id, project) => { + return Fetch(`/projects/${id}`, { + method: 'PUT', + headers: HEADERS(), + body: JSON.stringify(project), + }) +} + +const DeleteProject = id => { + return Fetch(`/projects/${id}`, { + method: 'DELETE', + headers: HEADERS(), + }) +} + export { AcceptCircleMemberRequest, ApproveChore, @@ -798,6 +847,7 @@ export { createChore, CreateLabel, CreateLongLiveToken, + CreateProject, CreateThing, DeleteChildUser, DeleteChore, @@ -805,6 +855,7 @@ export { DeleteCircleMember, DeleteLabel, DeleteLongLiveToken, + DeleteProject, DeleteThing, DeleteTimeSession, DeleteUser, @@ -825,6 +876,8 @@ export { GetLabels, GetLongLiveTokens, GetMFAStatus, + GetProjectById, + GetProjects, GetResource, GetStorageUsage, GetSubscriptionSession, @@ -855,6 +908,7 @@ export { SkipChore, StartChore, UnArchiveChore, + UndoChoreAction, UnregisterDeviceToken, UpdateChildPassword, UpdateChoreAssignee, @@ -865,6 +919,7 @@ export { UpdateMemberRole, UpdateNotificationTarget, UpdatePassword, + UpdateProject, UpdateThingState, UpdateTimeSession, UpdateUserDetails, diff --git a/src/views/components/ChoreActionMenu.jsx b/src/views/components/ChoreActionMenu.jsx index fdd288f..57c58e8 100644 --- a/src/views/components/ChoreActionMenu.jsx +++ b/src/views/components/ChoreActionMenu.jsx @@ -9,8 +9,8 @@ import { MoreVert, NextWeek, Nfc, - Notifications, NoteAdd, + Notifications, RecordVoiceOver, SwitchAccessShortcut, Today, diff --git a/src/views/components/NavBar.jsx b/src/views/components/NavBar.jsx index 076c077..8a4fc41 100644 --- a/src/views/components/NavBar.jsx +++ b/src/views/components/NavBar.jsx @@ -2,6 +2,7 @@ import { Capacitor } from '@capacitor/core' import { Archive, ArrowBack, + FolderOpen, History, Inbox, ListAlt, @@ -54,6 +55,11 @@ const links = [ label: 'Labels', icon: , }, + { + to: 'projects', + label: 'Projects', + icon: , + }, { to: 'activities', label: 'Activities', @@ -87,8 +93,8 @@ const links = [ ] import { SafeArea } from 'capacitor-plugin-safe-area' -import { useAuth } from '../../hooks/useAuth.jsx' import Z_INDEX from '../../constants/zIndex' +import { useAuth } from '../../hooks/useAuth.jsx' import { useResource } from '../../queries/ResourceQueries' const publicPages = ['/landing', '/privacy', '/terms']