feat: Add unarchive functionality to ChoreView and enhance project management features

- Implemented unarchive button for archived chores in ChoreView.
- Updated chore status handling to disable actions for archived chores.
- Enhanced MyChores component to filter tasks based on project selection, including a default project.
- Improved ProjectModal to streamline project creation and editing with a new footer layout.
- Refactored ProjectView to accurately count tasks for default and user projects.
- Added project selection dropdown in AddTaskModal with default project handling.
- Updated NavBar to handle logout using apiClient.
- Enhanced ProjectSelector to manage projects with a new menu item for project management.
This commit is contained in:
Mo Tarbin
2026-01-17 19:35:36 -05:00
parent b326b1b3d7
commit 7c8fa27aaf
19 changed files with 1132 additions and 462 deletions

View File

@@ -1,5 +1,10 @@
import { API_URL } from '../Config'
import { RefreshToken } from './Fetcher'
import { logout, RefreshToken } from './Fetcher'
import {
clearAllTokens,
isRefreshTokenExpired,
saveTokens,
} from './TokenStorage'
class ApiClient {
constructor() {
@@ -11,6 +16,17 @@ class ApiClient {
}
async refreshToken() {
// Check if refresh token is expired BEFORE attempting refresh
const refreshExpired = await isRefreshTokenExpired()
if (refreshExpired) {
console.log('Refresh token expired, forcing logout')
await clearAllTokens()
if (window.location.pathname !== '/login') {
window.location.href = '/login'
}
return { success: false, error: 'Refresh token expired' }
}
if (this.isRefreshing) {
return { success: false, error: 'Already refreshing' }
}
@@ -30,14 +46,13 @@ class ApiClient {
const data = await refreshReq.json()
const newToken = data.token || data.access_token
// Update Local Storage
localStorage.setItem('token', newToken)
if (data.expire || data.access_token_expiry) {
localStorage.setItem(
'token_expiry',
data.expire || data.access_token_expiry,
)
}
// Save all tokens including rotated refresh token
await saveTokens({
accessToken: newToken,
accessTokenExpiry: data.expire || data.access_token_expiry,
refreshToken: data.refresh_token,
refreshTokenExpiry: data.refresh_token_expiry,
})
// Update last refresh time
this.lastRefreshTime = Date.now()
@@ -90,10 +105,11 @@ class ApiClient {
}
// Helper to avoid repeating cleanup code
handleLogout() {
localStorage.removeItem('token')
localStorage.removeItem('token_expiry')
window.location.href = '/login'
async handleLogout() {
logout().then(async () => {
await clearAllTokens()
if (window.location.pathname !== '/login') window.location.href = '/login'
}) // fire and forget
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`