This commit is contained in:
beabigegg
2025-09-01 08:51:39 +08:00
parent f3f2b7d596
commit 45a42f8e64
16 changed files with 5438 additions and 48 deletions

View File

@@ -20,10 +20,8 @@ import {
ViewList,
CalendarViewMonth,
FilterList,
Sort,
Search,
SelectAll,
MoreVert,
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { useTheme } from '@/providers/ThemeProvider';
@@ -36,12 +34,14 @@ import SearchBar from '@/components/todos/SearchBar';
import TodoDialog from '@/components/todos/TodoDialog';
import { Todo } from '@/types';
import { todosApi } from '@/lib/api';
import { useSearchParams } from 'next/navigation';
type ViewMode = 'list' | 'calendar';
type FilterMode = 'all' | 'created' | 'responsible' | 'following';
const TodosPage = () => {
const { actualTheme } = useTheme();
const searchParams = useSearchParams();
const [viewMode, setViewMode] = useState<ViewMode>('list');
const [filterMode, setFilterMode] = useState<FilterMode>('all');
const [showFilters, setShowFilters] = useState(false);
@@ -64,6 +64,49 @@ const TodosPage = () => {
const [loading, setLoading] = useState(true);
const [currentUser, setCurrentUser] = useState<any>(null);
// 讀取 URL 參數並設定篩選條件
useEffect(() => {
console.log('URL search params:', searchParams.toString());
// 當從 Sidebar 點擊時,應該清除所有其他篩選,只保留當前篩選
const viewParam = searchParams.get('view');
const statusParam = searchParams.get('status');
const starredParam = searchParams.get('starred');
// 重置所有篩選狀態
setFilterMode('all');
setAppliedFilters({
status: [],
priority: [],
assignee: '',
dateFrom: null,
dateTo: null,
starred: false,
overdue: false,
dueSoon: false,
});
// 根據 URL 參數設定對應的篩選
if (viewParam && ['created', 'responsible', 'following'].includes(viewParam)) {
setFilterMode(viewParam as FilterMode);
console.log('Setting filterMode to:', viewParam);
} else if (statusParam) {
// 狀態篩選:清除視圖篩選,只保留狀態篩選
setAppliedFilters(prev => ({
...prev,
status: [statusParam]
}));
console.log('Setting status filter to:', statusParam);
} else if (starredParam === 'true') {
// 星標篩選:清除其他篩選,只保留星標篩選
setAppliedFilters(prev => ({
...prev,
starred: true
}));
console.log('Setting starred filter to: true');
}
}, [searchParams]);
// 從 API 獲取資料
useEffect(() => {
const fetchTodos = async () => {
@@ -72,9 +115,11 @@ const TodosPage = () => {
// 檢查是否有有效的 token
const token = localStorage.getItem('access_token');
console.log('Access token:', token ? 'Found' : 'Not found');
if (!token) {
console.log('No access token found, skipping API call');
console.log('No access token found, redirecting to login');
setTodos([]);
window.location.href = '/login';
return;
}
@@ -94,14 +139,16 @@ const TodosPage = () => {
}
// 獲取待辦事項
console.log('Fetching todos with filterMode:', filterMode);
const response = await todosApi.getTodos({
view: filterMode === 'all' ? 'all' : filterMode
});
console.log('Todos API response:', response);
setTodos(response.todos || []);
} catch (error) {
} catch (error: any) {
console.error('Failed to fetch todos:', error);
// 如果是認證錯誤,清除 token 並跳轉到登入頁
if (error.response?.status === 401) {
if (error?.response?.status === 401) {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user');
@@ -145,28 +192,33 @@ const TodosPage = () => {
}
}
// 視圖過濾
// 視圖過濾 - 修正:這裡應該是篩選而非直接返回
if (currentUser) {
switch (filterMode) {
case 'created':
return todo.creator_ad === currentUser.ad_account;
if (todo.creator_ad !== currentUser.ad_account) return false;
break;
case 'responsible':
return todo.responsible_users?.includes(currentUser.ad_account) || false;
if (!todo.responsible_users?.includes(currentUser.ad_account)) return false;
break;
case 'following':
return todo.followers?.includes(currentUser.ad_account) || false;
if (!todo.followers?.includes(currentUser.ad_account)) return false;
break;
default:
break; // 繼續其他篩選
break; // 'all' 模式,繼續其他篩選
}
}
// 進階篩選
// 狀態篩選
if (appliedFilters.status.length > 0 && !appliedFilters.status.includes(todo.status)) {
console.log(`Todo ${todo.title} filtered out by status: ${todo.status} not in`, appliedFilters.status);
return false;
}
// 優先級篩選
if (appliedFilters.priority.length > 0 && !appliedFilters.priority.includes(todo.priority)) {
console.log(`Todo ${todo.title} filtered out by priority: ${todo.priority} not in`, appliedFilters.priority);
return false;
}
@@ -174,52 +226,90 @@ const TodosPage = () => {
if (appliedFilters.assignee && currentUser) {
switch (appliedFilters.assignee) {
case 'me':
if (!todo.responsible_users?.includes(currentUser.ad_account)) return false;
if (!todo.responsible_users?.includes(currentUser.ad_account)) {
console.log(`Todo ${todo.title} filtered out: not assigned to me`);
return false;
}
break;
case 'created_by_me':
if (todo.creator_ad !== currentUser.ad_account) return false;
if (todo.creator_ad !== currentUser.ad_account) {
console.log(`Todo ${todo.title} filtered out: not created by me`);
return false;
}
break;
case 'followed_by_me':
if (!todo.followers?.includes(currentUser.ad_account)) return false;
if (!todo.followers?.includes(currentUser.ad_account)) {
console.log(`Todo ${todo.title} filtered out: not followed by me`);
return false;
}
break;
}
}
// 日期篩選
if (appliedFilters.dateFrom || appliedFilters.dateTo) {
if (!todo.due_date) return false;
if (!todo.due_date) {
console.log(`Todo ${todo.title} filtered out: no due date`);
return false;
}
const dueDate = new Date(todo.due_date);
if (appliedFilters.dateFrom && dueDate < new Date(appliedFilters.dateFrom)) return false;
if (appliedFilters.dateTo && dueDate > new Date(appliedFilters.dateTo)) return false;
if (appliedFilters.dateFrom && dueDate < new Date(appliedFilters.dateFrom)) {
console.log(`Todo ${todo.title} filtered out: due date before ${appliedFilters.dateFrom}`);
return false;
}
if (appliedFilters.dateTo && dueDate > new Date(appliedFilters.dateTo)) {
console.log(`Todo ${todo.title} filtered out: due date after ${appliedFilters.dateTo}`);
return false;
}
}
// 星號篩選
if (appliedFilters.starred && !todo.starred) {
console.log(`Todo ${todo.title} filtered out: not starred`);
return false;
}
// 逾期篩選
if (appliedFilters.overdue) {
if (!todo.due_date) return false;
if (!todo.due_date) {
console.log(`Todo ${todo.title} filtered out for overdue: no due date`);
return false;
}
const dueDate = new Date(todo.due_date);
const today = new Date();
today.setHours(0, 0, 0, 0);
if (dueDate >= today || todo.status === 'DONE') return false;
if (dueDate >= today || todo.status === 'DONE') {
console.log(`Todo ${todo.title} filtered out for overdue: not overdue or done`);
return false;
}
}
// 即將到期篩選
if (appliedFilters.dueSoon) {
if (!todo.due_date || todo.status === 'DONE') return false;
if (!todo.due_date || todo.status === 'DONE') {
console.log(`Todo ${todo.title} filtered out for due soon: no due date or done`);
return false;
}
const dueDate = new Date(todo.due_date);
const today = new Date();
const threeDaysFromNow = new Date();
threeDaysFromNow.setDate(today.getDate() + 3);
if (dueDate < today || dueDate > threeDaysFromNow) return false;
if (dueDate < today || dueDate > threeDaysFromNow) {
console.log(`Todo ${todo.title} filtered out for due soon: not in 3-day window`);
return false;
}
}
return true;
});
// 加入除錯資訊
useEffect(() => {
console.log('Applied filters:', appliedFilters);
console.log('Total todos:', todos.length);
console.log('Filtered todos:', filteredTodos.length);
}, [appliedFilters, todos.length, filteredTodos.length]);
const getFilterModeLabel = (mode: FilterMode) => {
switch (mode) {
case 'created': return '我建立的';
@@ -307,7 +397,7 @@ const TodosPage = () => {
setTodos(prevTodos =>
prevTodos.map(todo =>
selectedTodos.includes(todo.id)
? { ...todo, status: 'DONE', completed_at: new Date().toISOString() }
? { ...todo, status: 'DONE' as const, completed_at: new Date().toISOString() }
: todo
)
);
@@ -351,8 +441,11 @@ const TodosPage = () => {
// 單個待辦事項狀態變更處理函數
const handleStatusChange = async (todoId: string, status: string) => {
try {
// 確保 status 是有效的類型
const validStatus = status as 'NEW' | 'DOING' | 'BLOCKED' | 'DONE';
// 使用 API 更新單個待辦事項的狀態
await todosApi.updateTodo(todoId, { status });
await todosApi.updateTodo(todoId, { status: validStatus });
// 更新本地狀態
setTodos(prevTodos =>
@@ -360,8 +453,8 @@ const TodosPage = () => {
todo.id === todoId
? {
...todo,
status,
completed_at: status === 'DONE' ? new Date().toISOString() : null
status: validStatus,
completed_at: validStatus === 'DONE' ? new Date().toISOString() : undefined
}
: todo
)
@@ -559,12 +652,6 @@ const TodosPage = () => {
<FilterList fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="排序">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<Sort fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="全選">
<IconButton
@@ -577,12 +664,6 @@ const TodosPage = () => {
<SelectAll fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="更多選項">
<IconButton size="small" sx={{ color: 'text.secondary' }}>
<MoreVert fontSize="small" />
</IconButton>
</Tooltip>
</Box>
</Toolbar>
</Card>