diff --git a/src/features/calendar/l10n/messageIds.ts b/src/features/calendar/l10n/messageIds.ts index 20dac78274..ee9d375fee 100644 --- a/src/features/calendar/l10n/messageIds.ts +++ b/src/features/calendar/l10n/messageIds.ts @@ -108,6 +108,7 @@ export default makeMessages('feat.calendar', { nextDay: m<{ dates: ReactElement }>('Next day {dates}'), nextWeek: m<{ dates: ReactElement }>('Next week {dates}'), }, + openEventList: m('Make questionnaire'), }, shortWeek: m<{ weekNumber: number }>('w {weekNumber}'), showMore: m('Show'), diff --git a/src/features/campaigns/l10n/messageIds.ts b/src/features/campaigns/l10n/messageIds.ts index 762fede54b..e97eb1aef6 100644 --- a/src/features/campaigns/l10n/messageIds.ts +++ b/src/features/campaigns/l10n/messageIds.ts @@ -123,6 +123,7 @@ export default makeMessages('feat.campaigns', { allCampaigns: m('All Projects & Activities'), archive: m('Archive'), calendar: m('Calendar'), + eventList: m('Event List'), insights: m('Insights'), overview: m('Overview'), }, diff --git a/src/features/events/components/SelectionBar/index.tsx b/src/features/events/components/SelectionBar/index.tsx index 44937aff63..dbc91124d9 100644 --- a/src/features/events/components/SelectionBar/index.tsx +++ b/src/features/events/components/SelectionBar/index.tsx @@ -10,15 +10,54 @@ import { resetSelection } from 'features/events/store'; import { RootState } from 'core/store'; import SelectionBarEllipsis from '../SelectionBarEllipsis'; import useParticipantPool from 'features/events/hooks/useParticipantPool'; -import { useAppDispatch, useAppSelector } from 'core/hooks'; +import { + useAppDispatch, + useAppSelector, + useNumericRouteParams, +} from 'core/hooks'; const SelectionBar = () => { const dispatch = useAppDispatch(); const [participantsDialogOpen, setParticipantsDialogOpen] = useState(false); const { affectedParticipantIds } = useParticipantPool(); + const eventList = useAppSelector((state) => state.events.eventList); const selectedEventIds = useAppSelector( (state: RootState) => state.events.selectedEventIds ); + const { orgId } = useNumericRouteParams(); + + const handleOpenEventList = () => { + const filteredEvents = eventList.items + .filter( + (item) => item.data?.id && selectedEventIds.includes(item.data.id) + ) + .map((x) => x.data); + + const endDates = filteredEvents.map((x) => x?.end_time.slice(0, 10)); + const startDates = filteredEvents.map((x) => x?.start_time.slice(0, 10)); + + const minDate = startDates.reduce((min, current) => { + const currentDate = new Date(current || ''); + const minDate = new Date(min || ''); + return currentDate < minDate ? current : min; + }); + + const maxDate = endDates.reduce((max, current) => { + const currentDate = new Date(current || ''); + const maxDate = new Date(max || ''); + + return maxDate > currentDate ? max : current; + }); + + window + .open( + `/organize/${orgId}/projects/eventlist?minDate=${ + minDate || '' + }&maxDate=${maxDate || ''}&ids=${selectedEventIds.join(',')}`, + '_blank' + ) + ?.focus(); + }; const handleDeselect = () => { dispatch(resetSelection()); @@ -68,6 +107,10 @@ const SelectionBar = () => { gap={1} justifyContent="center" > + + { + const rawValue = router.query[paramName]; + + if (typeof rawValue !== 'string') { + return null; + } + + const date = new Date(rawValue); + + if (isNaN(date.valueOf())) { + return null; + } + + return date; + }, [router.query]); +} diff --git a/src/locale/en.yml b/src/locale/en.yml index 1f5714467f..ab412b8e59 100644 --- a/src/locale/en.yml +++ b/src/locale/en.yml @@ -126,6 +126,7 @@ feat: moveMenuHeader: Move {numberOfEvents} events to nextDay: Next day {dates} nextWeek: Next week {dates} + openEventList: Make questionnaire shortWeek: w {weekNumber} showMore: Show today: Today @@ -318,6 +319,7 @@ feat: allCampaigns: All Projects & Activities archive: Archive calendar: Calendar + eventList: Event List insights: Insights overview: Overview linkGroup: diff --git a/src/pages/organize/[orgId]/projects/eventlist/index.tsx b/src/pages/organize/[orgId]/projects/eventlist/index.tsx new file mode 100644 index 0000000000..db83d9b0af --- /dev/null +++ b/src/pages/organize/[orgId]/projects/eventlist/index.tsx @@ -0,0 +1,128 @@ +import { + Checkbox, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + TextField, +} from '@mui/material'; +import { Box } from '@mui/system'; +import Head from 'next/head'; +import { useRouter } from 'next/router'; +import { FC, useMemo } from 'react'; + +import { useNumericRouteParams } from 'core/hooks'; +import { useMessages } from 'core/i18n'; +import messageIds from 'features/campaigns/l10n/messageIds'; +import useEventsFromDateRange from 'features/events/hooks/useEventsFromDateRange'; +import useDateRouterParam from 'features/events/hooks/useDateRouterParam'; + +function batchArray(items: T[], batchSize: number): T[][] { + const batches: T[][] = []; + + const numBatches = Math.ceil(items.length / batchSize); + + for (let batchIndex = 0; batchIndex < numBatches; batchIndex++) { + batches[batchIndex] = items.slice( + batchIndex * batchSize, + Math.min(items.length, (batchIndex + 1) * batchSize) + ); + } + + return batches; +} + +const EventList: FC<{ orgId: number }> = ({ orgId }) => { + const router = useRouter(); + const messages = useMessages(messageIds); + + const selectedEventIds = useMemo(() => { + const { ids } = router.query; + if (typeof ids !== 'string') { + return []; + } + + const parsedIds = ids.split(',').map((x) => Number(x)); + + return parsedIds; + }, [router.query]); + + const endDate = useDateRouterParam('maxDate') || new Date(); + const startDate = useDateRouterParam('minDate') || new Date(); + + const filteredEvents = useEventsFromDateRange( + startDate, + endDate, + orgId + ).filter((x) => selectedEventIds.includes(x.data.id)); + + const batches = batchArray(filteredEvents, 30); + + return ( + <> + + {messages.layout.eventList()} + + + {batches.map((batch, index) => ( + + + + + + + + + + Date + Title + + + + + {batch.map((event) => ( + + + + + + {new Date(event.data.end_time).toLocaleDateString()} + + {event.data.title} + + ))} + +
+
+ ))} + + {selectedEventIds.length === 0 &&

No events selected.

} + + ); +}; + +const Wrapper = () => { + const { orgId } = useNumericRouteParams(); + if (!orgId) { + return; + } + + return ; +}; + +export default Wrapper;