diff --git a/src/constants/index.ts b/src/constants/index.ts index 5c570fcdf7..9393f03e04 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -139,6 +139,7 @@ export const ASSIGNMENT_PATH = { WM_WTStudy_Reader: 'weekend_meeting.wt_study.reader', WM_ClosingPrayer: 'weekend_meeting.closing_prayer', WM_CircuitOverseer: 'weekend_meeting.circuit_overseer', + WM_SubstituteSpeaker: 'weekend_meeting.speaker.substitute', }; export const BROTHER_ASSIGNMENT = [ diff --git a/src/definition/assignment.ts b/src/definition/assignment.ts index 656d49a3da..a89f40171f 100644 --- a/src/definition/assignment.ts +++ b/src/definition/assignment.ts @@ -114,4 +114,5 @@ export type AssignmentFieldType = | 'WM_WTStudy_Conductor' | 'WM_WTStudy_Reader' | 'WM_ClosingPrayer' - | 'WM_CircuitOverseer'; + | 'WM_CircuitOverseer' + | 'WM_SubstituteSpeaker'; diff --git a/src/definition/schedules.ts b/src/definition/schedules.ts index 926d284590..1e92cd88d4 100644 --- a/src/definition/schedules.ts +++ b/src/definition/schedules.ts @@ -228,3 +228,25 @@ export type MidweekMeetingDataType = { lc_concluding_prayer: string; co_name?: string; }; + +export type WeekendMeetingDataType = { + date_formatted: string; + weekOf: string; + no_meeting: boolean; + week_type: Week; + week_type_name: string; + event_name: string; + chairman_name: string; + opening_prayer_name: string; + public_talk_title?: string; + public_talk_number: string; + wtstudy_conductor_name: string; + wtstudy_reader_name: string; + speaker_1_name: string; + speaker_2_name: string; + speaker_cong_name: string; + substitute_speaker_name: string; + co_name?: string; + concluding_prayer_name?: string; + service_talk_title?: string; +}; diff --git a/src/features/meetings/midweek_export/index.tsx b/src/features/meetings/midweek_export/index.tsx index 66f6d766fb..9ec8133b23 100644 --- a/src/features/meetings/midweek_export/index.tsx +++ b/src/features/meetings/midweek_export/index.tsx @@ -2,7 +2,7 @@ import { Box } from '@mui/material'; import { IconLoading } from '@components/icons'; import { useAppTranslation } from '@hooks/index'; import { MidweekExportType } from './index.types'; -import useAssignmentsDelete from './useMidweekExport'; +import useMidweekExport from './useMidweekExport'; import Button from '@components/button'; import Checkbox from '@components/checkbox'; import Dialog from '@components/dialog'; @@ -28,7 +28,7 @@ const MidweekExport = ({ open, onClose }: MidweekExportType) => { S140Template, handleSelectS140Template, handleSelectS89Template, - } = useAssignmentsDelete(onClose); + } = useMidweekExport(onClose); return ( { } setIsProcessing(false); - // onClose?.(); + onClose?.(); } catch (error) { console.error(error); diff --git a/src/features/meetings/person_selector/usePersonSelector.tsx b/src/features/meetings/person_selector/usePersonSelector.tsx index 674a5cb351..a5f0fad50f 100644 --- a/src/features/meetings/person_selector/usePersonSelector.tsx +++ b/src/features/meetings/person_selector/usePersonSelector.tsx @@ -307,6 +307,7 @@ const usePersonSelector = ({ useEffect(() => { if (circuitOverseer) { setOptions([]); + setFreeSoloText(''); } }, [circuitOverseer]); diff --git a/src/features/meetings/weekend_export/index.tsx b/src/features/meetings/weekend_export/index.tsx new file mode 100644 index 0000000000..5524c1d42c --- /dev/null +++ b/src/features/meetings/weekend_export/index.tsx @@ -0,0 +1,79 @@ +import { Box } from '@mui/material'; +import { IconLoading } from '@components/icons'; +import { useAppTranslation } from '@hooks/index'; +import { WeekendExportType } from './index.types'; +import useWeekendExport from './useWeekendExport'; +import Button from '@components/button'; +import Dialog from '@components/dialog'; +import Typography from '@components/typography'; +import WeekRangeSelector from '../week_range_selector'; + +const WeekendExport = ({ open, onClose }: WeekendExportType) => { + const { t } = useAppTranslation(); + + const { + handleSetEndWeek, + handleSetStartWeek, + isProcessing, + handleExportSchedule, + } = useWeekendExport(onClose); + + return ( + + + + {t('tr_exportWM')} + + {t('tr_exportWMDesc')} + + + + + + + + + + + + ); +}; + +export default WeekendExport; diff --git a/src/features/meetings/weekend_export/index.types.ts b/src/features/meetings/weekend_export/index.types.ts new file mode 100644 index 0000000000..90dd6a23b0 --- /dev/null +++ b/src/features/meetings/weekend_export/index.types.ts @@ -0,0 +1,4 @@ +export type WeekendExportType = { + open: boolean; + onClose: VoidFunction; +}; diff --git a/src/features/meetings/weekend_export/useWeekendExport.tsx b/src/features/meetings/weekend_export/useWeekendExport.tsx new file mode 100644 index 0000000000..f54d621b63 --- /dev/null +++ b/src/features/meetings/weekend_export/useWeekendExport.tsx @@ -0,0 +1,91 @@ +import { useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { pdf } from '@react-pdf/renderer'; +import { saveAs } from 'file-saver'; +import { useAppTranslation } from '@hooks/index'; +import { WeekendExportType } from './index.types'; +import { displaySnackNotification } from '@services/recoil/app'; +import { getMessageByCode } from '@services/i18n/translation'; +import { schedulesState } from '@states/schedules'; +import { WeekendMeetingDataType } from '@definition/schedules'; +import { schedulesWeekendData } from '@services/app/schedules'; +import { + congNameState, + congNumberState, + userDataViewState, +} from '@states/settings'; +import { TemplateWeekendMeeting } from '@views/index'; + +const useWeekendExport = (onClose: WeekendExportType['onClose']) => { + const { t } = useAppTranslation(); + + const schedules = useRecoilValue(schedulesState); + const dataView = useRecoilValue(userDataViewState); + const congName = useRecoilValue(congNameState); + const congNumber = useRecoilValue(congNumberState); + + const [startWeek, setStartWeek] = useState(''); + const [endWeek, setEndWeek] = useState(''); + const [isProcessing, setIsProcessing] = useState(false); + + const handleSetStartWeek = (value: string) => setStartWeek(value); + + const handleSetEndWeek = (value: string) => setEndWeek(value); + + const handleExportSchedule = async () => { + if (startWeek.length === 0 || endWeek.length === 0) return; + + try { + setIsProcessing(true); + + const weeksList = schedules.filter( + (record) => record.weekOf >= startWeek && record.weekOf <= endWeek + ); + + const meetingData: WeekendMeetingDataType[] = []; + + for await (const schedule of weeksList) { + const data = await schedulesWeekendData(schedule, dataView); + meetingData.push(data); + } + + const firstWeek = meetingData.at(0).weekOf.replaceAll('/', ''); + const lastWeek = meetingData.at(-1).weekOf.replaceAll('/', ''); + + const blob = await pdf( + + ).toBlob(); + + const filename = `WM_${firstWeek}-${lastWeek}.pdf`; + + saveAs(blob, filename); + + setIsProcessing(false); + onClose?.(); + } catch (error) { + console.error(error); + + setIsProcessing(false); + onClose?.(); + + await displaySnackNotification({ + header: t('tr_errorTitle'), + message: getMessageByCode(error.message), + severity: 'error', + }); + } + }; + + return { + isProcessing, + handleSetStartWeek, + handleSetEndWeek, + handleExportSchedule, + }; +}; + +export default useWeekendExport; diff --git a/src/locales/en/forms-templates.json b/src/locales/en/forms-templates.json index f78a75af5c..4debb022b6 100644 --- a/src/locales/en/forms-templates.json +++ b/src/locales/en/forms-templates.json @@ -49,7 +49,7 @@ "tr_lcContentElderVariations": "To be handled by an elder", "tr_speaker": "Speaker", "tr_speakerSymposium": "Speaker (Symposium, part 1)", - "tr_wtStudyReader": "Watchtower Study Reader", + "tr_wtStudyReader": "Study Reader", "tr_weekendMeetingPrint": "Weekend Meeting Schedule", "tr_publicTalk": "Public talk", "tr_openingPrayerWeekendMeeting": "Opening Prayer", @@ -70,5 +70,7 @@ "tr_partDuration": "({{ time }} min.)", "tr_memorialWeek": "Memorial", "tr_exportMidweekMeetinDesc": "Choose the schedules and forms you'd like to export and pick your preferred template. Organized saves your template choice for next time to make the export easier.", - "tr_specialTalkWeek": "Special Talk" + "tr_specialTalkWeek": "Special Talk", + "tr_wtStudyConductor": "Study conductor", + "tr_closingPrayerWeekendMeeting": "Closing Prayer" } diff --git a/src/pages/dashboard/persons/index.tsx b/src/pages/dashboard/persons/index.tsx index 4ec76068bd..f283211278 100644 --- a/src/pages/dashboard/persons/index.tsx +++ b/src/pages/dashboard/persons/index.tsx @@ -40,7 +40,7 @@ const PersonsCard = () => { } - primaryText={t('tr_visitingSpeakers')} + primaryText={t('tr_speakersCatalog')} path="/visiting-speakers" /> diff --git a/src/pages/meetings/weekend/index.tsx b/src/pages/meetings/weekend/index.tsx index 33596e1f41..8d0abdc809 100644 --- a/src/pages/meetings/weekend/index.tsx +++ b/src/pages/meetings/weekend/index.tsx @@ -6,14 +6,22 @@ import useWeekend from './useWeekend'; import Button from '@components/button'; import PageTitle from '@components/page_title'; import WeekendEditor from '@features/meetings/weekend_editor'; +import WeekendExport from '@features/meetings/weekend_export'; const WeekendMeeting = () => { const { t } = useAppTranslation(); const { desktopUp } = useBreakpoints(); - const { hasWeeks, openAutofill, handleCloseAutofill, handleOpenAutofill } = - useWeekend(); + const { + hasWeeks, + openAutofill, + handleCloseAutofill, + handleOpenAutofill, + openExport, + handleCloseExport, + handleOpenExport, + } = useWeekend(); return ( { flexDirection: 'column', }} > + {openExport && ( + + )} + {openAutofill && ( { buttons={ hasWeeks && ( <> -