diff --git a/packages/be-gateway/src/routes/project/view.ts b/packages/be-gateway/src/routes/project/view.ts index 9b9bcd30..974acdb4 100644 --- a/packages/be-gateway/src/routes/project/view.ts +++ b/packages/be-gateway/src/routes/project/view.ts @@ -32,6 +32,7 @@ export default class ProjectViewController extends BaseController { priority: string point: string groupBy: string + statusIds: string[] } } @@ -49,7 +50,8 @@ export default class ProjectViewController extends BaseController { date: data.date, priority: data.priority, point: data.point, - groupBy: data.groupBy + groupBy: data.groupBy, + statusIds: data.statusIds } : {}, type, diff --git a/packages/shared-ui/src/components/Controls/ListControl/ListItem.tsx b/packages/shared-ui/src/components/Controls/ListControl/ListItem.tsx index 8668498b..c655a97c 100644 --- a/packages/shared-ui/src/components/Controls/ListControl/ListItem.tsx +++ b/packages/shared-ui/src/components/Controls/ListControl/ListItem.tsx @@ -39,6 +39,8 @@ export default function ListItem({ if (multiple && onMultiChange) { onMultiChange(prev => { // clear others selected, but itself + // when set `true` and in multiple mode, it will clear all value but itself + // Ex: user select an ALL item, then the others should be cleared if (keepMeOnly) { return [value] } @@ -50,7 +52,7 @@ export default function ListItem({ } // click on a unselected item, select it - return [...prev.filter(p => p.id !== 'ALL'), value] + return [...prev.filter(p => p.id !== 'ALL'), value].reverse() } else { return prev } diff --git a/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTask.tsx b/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTask.tsx index 417fddb6..d16b8821 100644 --- a/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTask.tsx +++ b/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTask.tsx @@ -28,40 +28,49 @@ export default function CalMonthTask({ taskStatusId }: ICalMonthTaskProps) { const { filter } = useTaskFilter() - const { status: filterStatus } = filter + const { status: filterStatus, statusIds: filterStatusIds } = filter const { color, type } = useStatusData(taskStatusId || '') const { calendarView } = useCalendarContext() - if (filterStatus !== 'ALL' && filterStatus !== type) { - return null + const view = () => + + {provided => ( +
+ {calendarView === ICalendarView.WEEK ? ( + + ) : ( + + )} +
+ )} +
+ + + // if statusIds contain ALL or nothing + // display view + if (filterStatusIds.includes('ALL') || !filterStatusIds.length) { + return view() + } + + + // if statusIds have some + // display tasks that have the same status id + if (filterStatusIds.includes(taskStatusId)) { + return view() } - return ( - - - {provided => ( -
- {calendarView === ICalendarView.WEEK ? ( - - ) : ( - - )} -
- )} -
- - ) + return null } diff --git a/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTaskList.tsx b/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTaskList.tsx index d6b53b49..c57e3190 100644 --- a/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTaskList.tsx +++ b/packages/ui-app/app/[orgID]/project/[projectId]/calendar/CalMonthTaskList.tsx @@ -34,9 +34,8 @@ export default function CalMonthTaskList({ day }: { day: Date }) { const h = dueDate.getHours() const m = dueDate.getMinutes() - const time = `${h > 9 ? (h > 12 ? h - 12 : h) : '0' + h}:${ - m > 9 ? m : '0' + m - } ${h >= 12 ? 'PM' : 'AM'}` + const time = `${h > 9 ? (h > 12 ? h - 12 : h) : '0' + h}:${m > 9 ? m : '0' + m + } ${h >= 12 ? 'PM' : 'AM'}` return ( void title?: string placeholder?: string -} - -const defaultOption: ListItemValue = { - id: '0', - title: 'None' + maxDisplay?: number } export default function StatusSelectMultiple({ title, className, + noName = false, value, onChange, - placeholder + placeholder, + maxDisplay = 3 }: IStatusSelectProps) { // const selectOption = options.find(opt => opt.id === value); const { statuses } = useProjectStatusStore() @@ -38,16 +37,33 @@ export default function StatusSelectMultiple({ // update options list useEffect(() => { const statusList = statuses.map(stt => ({ id: stt.id, title: stt.name })) + statusList.push({ + id: 'ALL', + title: 'All statuses' + }) setOptions(statusList as ListItemValue[]) }, [statuses]) // fill selectetd value after statues list updated useEffect(() => { - if (options.length) { - const selectedOption = options.filter( - opt => value && value.some(v => v === opt.id) - ) - setVal(selectedOption) + if (options.length && value?.length) { + + const newVal: ListItemValue[] = [] + const optionMap = new Map() + + options.forEach(opt => { + optionMap.set(opt.id, opt) + }) + + value.forEach(item => { + const value = optionMap.get(item) + if (value) { + newVal.push(value) + } + }) + + setVal(newVal) + } }, [options, value]) @@ -59,6 +75,10 @@ export default function StatusSelectMultiple({ }, [updateCounter]) const selectedList = val + const slicedList = val.slice(0, maxDisplay) + const rest = val.slice(maxDisplay).length + + const bgOfAll = 'linear-gradient(45deg, rgba(226,40,130,1) 0%, rgba(226,40,142,1) 50%, rgba(30,235,107,1) 50%, rgba(30,235,213,1) 100%)' return (
@@ -72,32 +92,37 @@ export default function StatusSelectMultiple({ setUpdateCounter(updateCounter + 1) }}> - {!selectedList || !selectedList.length ? Option: null} + {!selectedList || !selectedList.length ? Option : null}
- {selectedList.map(item => { + {slicedList.map(item => { const stt = statuses.find(stt => stt.id === item.id) + const isAllOption = item.id === 'ALL' ? bgOfAll : stt?.color + return (
- {item.title} + {noName ? null : {item.title}}
) })} + {rest ?
+{rest}
: null}
- + {options.map(option => { const stt = statuses.find(st => st.id === option.id) + const isAllOption = option.id === 'ALL' ? bgOfAll : stt?.color return ( - +
+ style={{ backgroundColor: stt?.color, background: isAllOption }}>
{option.title}
diff --git a/packages/ui-app/app/_features/ProjectContainer/useGetTask.ts b/packages/ui-app/app/_features/ProjectContainer/useGetTask.ts index 3251c879..435a1c71 100644 --- a/packages/ui-app/app/_features/ProjectContainer/useGetTask.ts +++ b/packages/ui-app/app/_features/ProjectContainer/useGetTask.ts @@ -60,6 +60,7 @@ export default function useGetTask() { }) setTaskLoading(true) + console.log('before getting task =======================', point) taskGetByCond( { title: term || undefined, @@ -74,6 +75,7 @@ export default function useGetTask() { ) .then(res => { const { data, status, error } = res.data + console.log('fetch task data', data) if (status !== 200) { addAllTasks([]) localforage.removeItem(key) @@ -83,6 +85,7 @@ export default function useGetTask() { localforage.setItem(key, data) setTimeout(() => { + console.log('update all task', data) addAllTasks(data) }, 300) }) diff --git a/packages/ui-app/app/_features/ProjectView/ProjectViewCreate.tsx b/packages/ui-app/app/_features/ProjectView/ProjectViewCreate.tsx index e67dbc6c..1080c0bc 100644 --- a/packages/ui-app/app/_features/ProjectView/ProjectViewCreate.tsx +++ b/packages/ui-app/app/_features/ProjectView/ProjectViewCreate.tsx @@ -1,5 +1,5 @@ import { Modal } from '@shared/ui' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { AiOutlinePlus } from 'react-icons/ai' import ProjectViewModal from './ProjectViewModal' import { IBoardFilter, ProjectViewProvider } from './context' @@ -13,10 +13,18 @@ export default function ProjectViewCreate() { const [filter, setFilter] = useState({ date: 'this-month', priority: 'ALL', - point: "INFINITE", + point: "-1", + statusIds: ['ALL'], groupBy: ETaskFilterGroupByType.STATUS }) + useEffect(() => { + if (visible === false) { + setCustomView(false) + } + + }, [visible]) + return ( { setLoading(false) setVisible(false) + setCustomView(false) setName('') }, 500) } diff --git a/packages/ui-app/app/_features/ProjectView/context.ts b/packages/ui-app/app/_features/ProjectView/context.ts index 560817f1..16f00bd9 100644 --- a/packages/ui-app/app/_features/ProjectView/context.ts +++ b/packages/ui-app/app/_features/ProjectView/context.ts @@ -7,6 +7,7 @@ export interface IBoardFilter { priority: TaskPriority | 'ALL', point: string groupBy: ETaskFilterGroupByType + statusIds: string[] } interface IProjectViewContextProps { @@ -39,7 +40,8 @@ const ProjectViewContext = createContext({ filter: { date: "this-month", priority: 'ALL', - point: "INFINITE", + point: "-1", + statusIds: ['ALL'], groupBy: ETaskFilterGroupByType.STATUS }, setFilter: () => { console.log(1) } @@ -55,7 +57,7 @@ export const useProjectViewContext = () => { const setFilterValue = ( name: keyof IBoardFilter, - val: string | ETaskFilterGroupByType + val: string | string[] | ETaskFilterGroupByType ) => { setFilter(filter => ({ ...filter, [name]: val })) } diff --git a/packages/ui-app/app/_features/ProjectView/index.tsx b/packages/ui-app/app/_features/ProjectView/index.tsx index 8b2b26e2..3834ff87 100644 --- a/packages/ui-app/app/_features/ProjectView/index.tsx +++ b/packages/ui-app/app/_features/ProjectView/index.tsx @@ -7,54 +7,46 @@ import { useProjectViewList } from './useProjectViewList' import ProjectViewIcon from './ProjectViewIcon' import { Loading } from '@shared/ui' import ProjectViewItemDropdown from './ProjectViewItemDropdown' -import { useTaskFilter } from '../TaskFilter/context' -import { IBoardFilter } from './context' -import { useDebounce } from '@/hooks/useDebounce' import DynamicIcon from '@/components/DynamicIcon' import HasRole from '../UserPermission/HasRole' +import useSetViewFilter from './useSetViewFilter' + +// function ProjectViewItem({view}: {view: }) { +// const clickOnView = (name: string) => { +// push(`${params.orgID}/project/${params.projectId}?mode=${name}`) +// } +// return
clickOnView(view.id)} +// className={`project-view-item group relative ${active ? 'active' : '' +// }`} +// key={index}> +// {icon ? ( +// +// ) : ( +// +// )} +// {view.name} +// +//
+// } export default function ProjectView() { const searchParams = useSearchParams() const { push } = useRouter() const params = useParams() const mode = searchParams.get('mode') - const { views, loading } = useProjectViewList() - const { setFilter, setDefaultFilter } = useTaskFilter() + const { views } = useProjectViewList() + + useSetViewFilter() const clickOnView = (name: string) => { push(`${params.orgID}/project/${params.projectId}?mode=${name}`) } - useDebounce(() => { - const viewId = mode - const view = views.find(v => v.id === viewId) - - if ( - view && - view.data && - !Object.keys(view.data as { [key: string]: unknown }).length - ) { - setDefaultFilter() - } - - if ( - view && - view.data && - Object.keys(view.data as { [key: string]: unknown }).length - ) { - const data = view.data as unknown as IBoardFilter - setFilter(filter => ({ - ...filter, - ...{ - date: data.date, - groupBy: data.groupBy, - priority: data.priority, - point: data.point - } - })) - } - }, [mode, views.toString()]) - return (
diff --git a/packages/ui-app/app/_features/ProjectView/useProjectViewAdd.ts b/packages/ui-app/app/_features/ProjectView/useProjectViewAdd.ts index 04802178..480612f2 100644 --- a/packages/ui-app/app/_features/ProjectView/useProjectViewAdd.ts +++ b/packages/ui-app/app/_features/ProjectView/useProjectViewAdd.ts @@ -24,6 +24,7 @@ export const useProjectViewAdd = () => { date: data.date, priority: data.priority, point: data.point, + statusIds: data.statusIds, groupBy: data.groupBy } : null diff --git a/packages/ui-app/app/_features/ProjectView/useProjectViewList.ts b/packages/ui-app/app/_features/ProjectView/useProjectViewList.ts index b3a6fc90..0926c10b 100644 --- a/packages/ui-app/app/_features/ProjectView/useProjectViewList.ts +++ b/packages/ui-app/app/_features/ProjectView/useProjectViewList.ts @@ -52,7 +52,6 @@ export const useProjectViewList = () => { if (val) { const views = val as ProjectView[] - views.forEach(v => projectViewMap.set(v.id, v.type)) addAllView(views) } diff --git a/packages/ui-app/app/_features/ProjectView/useSetViewFilter.ts b/packages/ui-app/app/_features/ProjectView/useSetViewFilter.ts new file mode 100644 index 00000000..4b1b4f6f --- /dev/null +++ b/packages/ui-app/app/_features/ProjectView/useSetViewFilter.ts @@ -0,0 +1,46 @@ +import { useDebounce } from "@/hooks/useDebounce" +import { useSearchParams } from "next/navigation" +import { useProjectViewList } from "./useProjectViewList" +import { useTaskFilter } from "../TaskFilter/context" +import { IBoardFilter } from "./context" + +export default function useSetViewFilter() { + const searchParams = useSearchParams() + const { views } = useProjectViewList() + const { setFilter, setDefaultFilter } = useTaskFilter() + const mode = searchParams.get('mode') + + // update task filter once user change to another view + useDebounce(() => { + const viewId = mode + const view = views.find(v => v.id === viewId) + + if ( + view && + view.data && + !Object.keys(view.data as { [key: string]: unknown }).length + ) { + setDefaultFilter() + } + + if ( + view && + view.data && + Object.keys(view.data as { [key: string]: unknown }).length + ) { + const data = view.data as unknown as IBoardFilter + console.log('set view data fileter', data) + + setFilter(filter => ({ + ...filter, + ...{ + date: data.date, + groupBy: data.groupBy, + priority: data.priority, + statusIds: data.statusIds, + point: data.point + } + })) + } + }, [mode, views.toString()]) +} diff --git a/packages/ui-app/app/_features/ProjectViewFilter/CalendarFilter.tsx b/packages/ui-app/app/_features/ProjectViewFilter/CalendarFilter.tsx index 37f1aa78..5c792c95 100644 --- a/packages/ui-app/app/_features/ProjectViewFilter/CalendarFilter.tsx +++ b/packages/ui-app/app/_features/ProjectViewFilter/CalendarFilter.tsx @@ -15,7 +15,7 @@ export default function ProjectViewFilterByCalendar({ type, desc, onAdd }: {

Calendar

{desc}

- +