From 8b54dbb47ed2aca5a6419d957acdc024b32fc0a6 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 15:58:38 +0200 Subject: [PATCH 01/99] feat: create availability page --- .../features/availability/ui/availability-page.lazy.ts | 3 +++ .../features/availability/ui/availability-page.tsx | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 src/features/binnacle/features/availability/ui/availability-page.lazy.ts create mode 100644 src/features/binnacle/features/availability/ui/availability-page.tsx diff --git a/src/features/binnacle/features/availability/ui/availability-page.lazy.ts b/src/features/binnacle/features/availability/ui/availability-page.lazy.ts new file mode 100644 index 00000000..08c91a9b --- /dev/null +++ b/src/features/binnacle/features/availability/ui/availability-page.lazy.ts @@ -0,0 +1,3 @@ +import { lazy } from 'react' + +export const LazyAvailabilityPage = lazy(() => import('./availability-page')) diff --git a/src/features/binnacle/features/availability/ui/availability-page.tsx b/src/features/binnacle/features/availability/ui/availability-page.tsx new file mode 100644 index 00000000..b0390dd5 --- /dev/null +++ b/src/features/binnacle/features/availability/ui/availability-page.tsx @@ -0,0 +1,7 @@ +import { FC } from 'react' + +const AvailabilityPage: FC = () => { + return

Hello world

+} + +export default AvailabilityPage From 6380655292f8cef67e2c8cf7d6c1d45d8b7f492f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 15:59:00 +0200 Subject: [PATCH 02/99] feat: add routing to availability page --- src/app-routes.tsx | 9 +++++++++ src/shared/components/navbar/nav-menu.tsx | 16 ++++++++++++++-- src/shared/i18n/en.json | 3 ++- src/shared/i18n/es.json | 3 ++- src/shared/router/paths.ts | 6 ++++-- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/app-routes.tsx b/src/app-routes.tsx index 8e11eca5..3bef39d9 100644 --- a/src/app-routes.tsx +++ b/src/app-routes.tsx @@ -22,6 +22,7 @@ import { RequireBlockRole } from './shared/router/require-block-role' import { RequireActivityApproval } from './shared/router/require-activity-approval' import { LazyActivitiesPage } from './features/binnacle/features/activity/ui/activities-page.lazy' import { useIsMobile } from './shared/hooks/use-is-mobile' +import { LazyAvailabilityPage } from './features/binnacle/features/availability/ui/availability-page.lazy' export const AppRoutes: FC = () => { const isMobile = useIsMobile() @@ -108,6 +109,14 @@ export const AppRoutes: FC = () => { } /> + + + + } + /> diff --git a/src/shared/components/navbar/nav-menu.tsx b/src/shared/components/navbar/nav-menu.tsx index bd5d393a..1b9aad69 100644 --- a/src/shared/components/navbar/nav-menu.tsx +++ b/src/shared/components/navbar/nav-menu.tsx @@ -2,10 +2,10 @@ import { ChevronDownIcon } from '@chakra-ui/icons' import { Icon, ListItem, Stack, UnorderedList } from '@chakra-ui/react' import { AdjustmentsVerticalIcon, + ArrowRightOnRectangleIcon, BriefcaseIcon, CalendarIcon, - CogIcon, - ArrowRightOnRectangleIcon + CogIcon } from '@heroicons/react/20/solid' import { LogoutCmd } from '../../../features/auth/application/logout-cmd' import { useTranslation } from 'react-i18next' @@ -59,6 +59,7 @@ export const NavMenu: FC = () => { activePath(paths.binnacle) || activePath(paths.calendar) || activePath(paths.activities) || + activePath(paths.availability) || (activePath(paths.pendingActivities) && !isMobile) } > @@ -112,6 +113,17 @@ export const NavMenu: FC = () => { {t('pages.pending_activities')} )} + } + isActive={activePath(paths.availability)} + isChild={true} + px={2} + py={3} + > + {t('pages.availability')} + diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index 7371f16a..4fa6730e 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -7,7 +7,8 @@ "vacations": "Vacations", "projects": "Projects", "administration": "Administration", - "activities": "Activities" + "activities": "Activities", + "availability": "Availability" }, "navbar": { "logo": "Logo", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index 3810c3d6..228679b6 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -7,7 +7,8 @@ "settings": "Preferencias", "vacations": "Vacaciones", "projects": "Proyectos", - "administration": "Administración" + "administration": "Administración", + "availability": "Disponibilidad" }, "navbar": { "logo": "Logo", diff --git a/src/shared/router/paths.ts b/src/shared/router/paths.ts index 5ad00b7e..ba0fad0a 100644 --- a/src/shared/router/paths.ts +++ b/src/shared/router/paths.ts @@ -9,7 +9,8 @@ export const rawPaths = { settings: '/settings', pendingActivities: '/binnacle/pending-activities', projects: '/administration/projects', - activities: '/binnacle/activities' + activities: '/binnacle/activities', + availability: '/binnacle/availability' } export const paths = { @@ -21,5 +22,6 @@ export const paths = { settings: `${basename}${rawPaths.settings}`, pendingActivities: `${basename}${rawPaths.pendingActivities}`, projects: `${basename}${rawPaths.projects}`, - activities: `${basename}${rawPaths.activities}` + activities: `${basename}${rawPaths.activities}`, + availability: `${basename}${rawPaths.availability}` } From 1a90a465e40f7df88bfa845a330bb0fa635cd4b4 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 16:29:51 +0200 Subject: [PATCH 03/99] feat: create availability table --- .../availability/ui/availability-page.tsx | 11 +++++++- .../ui/components/availability-table.tsx | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table.tsx diff --git a/src/features/binnacle/features/availability/ui/availability-page.tsx b/src/features/binnacle/features/availability/ui/availability-page.tsx index b0390dd5..4527deff 100644 --- a/src/features/binnacle/features/availability/ui/availability-page.tsx +++ b/src/features/binnacle/features/availability/ui/availability-page.tsx @@ -1,7 +1,16 @@ import { FC } from 'react' +import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' +import { useTranslation } from 'react-i18next' +import { AvailabilityTable } from './components/availability-table' const AvailabilityPage: FC = () => { - return

Hello world

+ const { t } = useTranslation() + + return ( + + + + ) } export default AvailabilityPage diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx new file mode 100644 index 00000000..7433197e --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -0,0 +1,26 @@ +import { FC } from 'react' +import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' +import { Table, Th, Thead, Tr } from '@chakra-ui/react' +import { chrono } from '../../../../../../shared/utils/chrono' +import { AvailabilityTableCellHeader } from './availability-table-cell-header' + +export const AvailabilityTable: FC = () => { + const { selectedDate } = useCalendarContext() + const daysOfMonth = chrono(chrono(selectedDate).startOf('month').getDate()).eachDayUntil( + chrono(selectedDate).endOf('month').getDate() + ) + const tableHeaders = ( + + + + {daysOfMonth.map((day, index) => ( + + + + ))} + + + ) + + return {tableHeaders}
+} From 40700333260a734e3557c1a913c7b1b79f7783fa Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 16:29:59 +0200 Subject: [PATCH 04/99] feat: create availability table cell header --- .../ui/components/availability-table-cell-header.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx new file mode 100644 index 00000000..5a50010b --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -0,0 +1,9 @@ +import { FC } from 'react' + +interface Props { + day: Date +} + +export const AvailabilityTableCellHeader: FC = (props) => { + return

{props.day.getDate()}

+} From 6ff6d6c5e04844ac7fa5bd3917dce468472c9e35 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 16:40:19 +0200 Subject: [PATCH 05/99] feat: add logic to set day of week and day of month --- .../availability-table-cell-header.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index 5a50010b..d3bcd6b5 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -1,9 +1,24 @@ import { FC } from 'react' +import { Box, Text } from '@chakra-ui/react' +import { chrono } from '../../../../../../shared/utils/chrono' +import { getWeekdaysName } from '../../../activity/utils/get-weekdays-name' interface Props { day: Date } -export const AvailabilityTableCellHeader: FC = (props) => { - return

{props.day.getDate()}

+const weekDays = getWeekdaysName() + +const getWeekDay = (date: Date) => { + const weekDay = chrono(date).get('weekday') + return weekDay === 0 ? 7 : weekDay +} + +export const AvailabilityTableCellHeader: FC = ({ day }) => { + return ( + + {weekDays[getWeekDay(day) - 1]} + {day.getDate()} + + ) } From 5e564e5760a3874c627004abfeac78b9b0022b28 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 17:39:01 +0200 Subject: [PATCH 06/99] feat: add border color to table header --- .../availability/ui/components/availability-table.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 7433197e..4696d9c0 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,6 +1,6 @@ import { FC } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' -import { Table, Th, Thead, Tr } from '@chakra-ui/react' +import { Table, Th, Thead, Tr, useColorModeValue } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' @@ -9,12 +9,13 @@ export const AvailabilityTable: FC = () => { const daysOfMonth = chrono(chrono(selectedDate).startOf('month').getDate()).eachDayUntil( chrono(selectedDate).endOf('month').getDate() ) + const borderColor = useColorModeValue('gray.300', 'gray.700') const tableHeaders = ( {daysOfMonth.map((day, index) => ( - + ))} From 23854a05c52c094987b4470bef5bcccd816753bc Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 17:51:23 +0200 Subject: [PATCH 07/99] feat: add login to set color on current day --- .../ui/components/availability-table-cell-header.css | 6 ++++++ .../ui/components/availability-table-cell-header.tsx | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css new file mode 100644 index 00000000..53bf902d --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css @@ -0,0 +1,6 @@ +.is-today { + border-radius: 50%; + font-weight: bold; + color: white; + background-color: var(--primary-color); +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index d3bcd6b5..4bc468bb 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -2,6 +2,8 @@ import { FC } from 'react' import { Box, Text } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { getWeekdaysName } from '../../../activity/utils/get-weekdays-name' +import { isToday } from 'date-fns' +import './availability-table-cell-header.css' interface Props { day: Date @@ -16,9 +18,11 @@ const getWeekDay = (date: Date) => { export const AvailabilityTableCellHeader: FC = ({ day }) => { return ( - + {weekDays[getWeekDay(day) - 1]} - {day.getDate()} + + {day.getDate()} + ) } From ad0d22d8dc2e30af6a4810d776decc1afce00ab3 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 17:51:42 +0200 Subject: [PATCH 08/99] feat: remove unused css --- .../ui/components/availability-table-cell-header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index 4bc468bb..3eb55215 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -20,7 +20,7 @@ export const AvailabilityTableCellHeader: FC = ({ day }) => { return ( {weekDays[getWeekDay(day) - 1]} - + {day.getDate()} From 02be82230d78dcbf20846ad182b5917e335a1581 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 17:59:25 +0200 Subject: [PATCH 09/99] feat: add isWeekend method --- src/shared/utils/chrono.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shared/utils/chrono.ts b/src/shared/utils/chrono.ts index 3d1378d4..0e1a1ab9 100644 --- a/src/shared/utils/chrono.ts +++ b/src/shared/utils/chrono.ts @@ -398,6 +398,10 @@ export const isSunday = (date: Date) => { return fns.isSunday(date) } +export const isWeekend = (date: Date) => { + return fns.isWeekend(date) +} + export const isFirstDayOfMonth = (date: Date) => { return fns.isFirstDayOfMonth(date) } From 7aee66908cbbbe584a3db9168200800b2dff0be9 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 18:01:37 +0200 Subject: [PATCH 10/99] feat: update table cell header to have new backgroind if is it weekend --- .../availability-table-cell-header.tsx | 24 ++++++++++++------- .../ui/components/availability-table.tsx | 8 +++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index 3eb55215..2a128bab 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -1,6 +1,6 @@ import { FC } from 'react' -import { Box, Text } from '@chakra-ui/react' -import { chrono } from '../../../../../../shared/utils/chrono' +import { Box, Text, Th, useColorModeValue } from '@chakra-ui/react' +import { chrono, isWeekend } from '../../../../../../shared/utils/chrono' import { getWeekdaysName } from '../../../activity/utils/get-weekdays-name' import { isToday } from 'date-fns' import './availability-table-cell-header.css' @@ -17,12 +17,20 @@ const getWeekDay = (date: Date) => { } export const AvailabilityTableCellHeader: FC = ({ day }) => { + const borderColor = useColorModeValue('gray.300', 'gray.700') + return ( - - {weekDays[getWeekDay(day) - 1]} - - {day.getDate()} - - + + + {weekDays[getWeekDay(day) - 1]} + + {day.getDate()} + + + ) } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 4696d9c0..636ec651 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,6 +1,6 @@ import { FC } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' -import { Table, Th, Thead, Tr, useColorModeValue } from '@chakra-ui/react' +import { Table, Th, Thead, Tr } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' @@ -9,15 +9,13 @@ export const AvailabilityTable: FC = () => { const daysOfMonth = chrono(chrono(selectedDate).startOf('month').getDate()).eachDayUntil( chrono(selectedDate).endOf('month').getDate() ) - const borderColor = useColorModeValue('gray.300', 'gray.700') + const tableHeaders = ( {daysOfMonth.map((day, index) => ( - - - + ))} From 05e867ee15073ba14cb3fddd4563f43a97dd50d8 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 18:20:14 +0200 Subject: [PATCH 11/99] feat: add logic to paint holidays --- .../components/availability-table-cell-header.tsx | 5 +++-- .../ui/components/availability-table.tsx | 13 ++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index 2a128bab..b13d6c0a 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -7,6 +7,7 @@ import './availability-table-cell-header.css' interface Props { day: Date + isHoliday: boolean } const weekDays = getWeekdaysName() @@ -16,14 +17,14 @@ const getWeekDay = (date: Date) => { return weekDay === 0 ? 7 : weekDay } -export const AvailabilityTableCellHeader: FC = ({ day }) => { +export const AvailabilityTableCellHeader: FC = ({ day, isHoliday }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') return ( {weekDays[getWeekDay(day) - 1]} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 636ec651..f0cef825 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -3,6 +3,8 @@ import { useCalendarContext } from '../../../activity/ui/contexts/calendar-conte import { Table, Th, Thead, Tr } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' +import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' +import { GetHolidaysQry } from '../../../holiday/application/get-holidays-qry' export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() @@ -10,12 +12,21 @@ export const AvailabilityTable: FC = () => { chrono(selectedDate).endOf('month').getDate() ) + const { result: holidays = [] } = useExecuteUseCaseOnMount(GetHolidaysQry, { + start: chrono(selectedDate).startOf('month').getDate(), + end: chrono(selectedDate).endOf('month').getDate() + }) + const tableHeaders = ( {daysOfMonth.map((day, index) => ( - + chrono(day).isSameDay(holiday.date))} + > ))} From 64caae8dbfb19ff53d7c122ea346afb4d51f8d0f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 18 Sep 2023 18:26:11 +0200 Subject: [PATCH 12/99] refactor: add function to check holiday --- .../availability/ui/components/availability-table.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index f0cef825..ab44a3d9 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -17,6 +17,9 @@ export const AvailabilityTable: FC = () => { end: chrono(selectedDate).endOf('month').getDate() }) + const checkIfHoliday = (day: Date) => + holidays.some((holiday) => chrono(day).isSameDay(holiday.date)) + const tableHeaders = ( @@ -25,7 +28,7 @@ export const AvailabilityTable: FC = () => { chrono(day).isSameDay(holiday.date))} + isHoliday={checkIfHoliday(day)} > ))} From 852b380c88fcb86eac2de94d474b69c758c3b688 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 09:21:52 +0200 Subject: [PATCH 13/99] feat: add row content --- .../ui/components/availability-table.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index ab44a3d9..13b789f9 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -35,5 +35,21 @@ export const AvailabilityTable: FC = () => { ) - return {tableHeaders}
+ const tableRows = ( + + Lorem ipsum dolor sit amet. + {daysOfMonth.map((day, index) => ( + + {day.getDate()} + + ))}{' '} + + ) + + return ( + + {tableHeaders} + {tableRows} +
+ ) } From 1785328e96d0cbe22a72a4c8579b6ec9d5ae424b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 12:06:23 +0200 Subject: [PATCH 14/99] feat: update project repository to no require organization status to make de request --- .../application/get-projects-list-qry.ts | 13 ++++++++----- .../infrastructure/fake-project-repository.ts | 8 ++------ .../infrastructure/http-project-repository.ts | 17 +++++------------ 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-list-qry.ts index adaee759..52be7fac 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.ts @@ -21,10 +21,13 @@ export class GetProjectsListQry extends Query } async internalExecute(organizationStatus?: OrganizationWithStatus): Promise { - const projects = await this.projectRepository.getProjects(organizationStatus) - const usersList = await this.getUsersListQry.execute({ - ids: projects.map((project) => project.blockedByUser).filter((id) => id !== null) as Id[] - }) - return this.projectsWithUserName.addUserNameToProjects(projects, usersList) + if (organizationStatus) { + const projects = await this.projectRepository.getProjects(organizationStatus) + const usersList = await this.getUsersListQry.execute({ + ids: projects.map((project) => project.blockedByUser).filter((id) => id !== null) as Id[] + }) + return this.projectsWithUserName.addUserNameToProjects(projects, usersList) + } + return [] } } diff --git a/src/features/administration/features/project/infrastructure/fake-project-repository.ts b/src/features/administration/features/project/infrastructure/fake-project-repository.ts index a02abaa7..8ad4f1cc 100644 --- a/src/features/administration/features/project/infrastructure/fake-project-repository.ts +++ b/src/features/administration/features/project/infrastructure/fake-project-repository.ts @@ -1,16 +1,12 @@ import { singleton } from 'tsyringe' -import { OrganizationWithStatus } from '../domain/organization-status' import { Project } from '../domain/project' import { ProjectRepository } from '../domain/project-repository' import { ProjectMother } from '../domain/tests/project-mother' @singleton() export class FakeProjectRepository implements ProjectRepository { - async getProjects(organizationWithStatus?: OrganizationWithStatus): Promise { - if (organizationWithStatus) { - return ProjectMother.projectsFilteredByOrganizationDateIsoWithName() - } - return [] + async getProjects(): Promise { + return ProjectMother.projectsFilteredByOrganizationDateIsoWithName() } async blockProject(): Promise { diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.ts b/src/features/administration/features/project/infrastructure/http-project-repository.ts index c89a05fd..20499876 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.ts +++ b/src/features/administration/features/project/infrastructure/http-project-repository.ts @@ -16,18 +16,11 @@ export class HttpProjectRepository implements ProjectRepository { constructor(private httpClient: HttpClient) {} - async getProjects(organizationWithStatus?: OrganizationWithStatus): Promise { - if (organizationWithStatus) { - const { organizationId, open } = organizationWithStatus - const data = await this.httpClient.get(HttpProjectRepository.projectPath, { - params: { - organizationId: organizationId, - open: open - } - }) - return ProjectMapper.toDomainList(data) - } - return [] + async getProjects(organizationWithStatus: OrganizationWithStatus): Promise { + const data = await this.httpClient.get(HttpProjectRepository.projectPath, { + params: organizationWithStatus + }) + return ProjectMapper.toDomainList(data) } async blockProject(projectId: Id, date: Date): Promise { From 3dbabc80cec25091ada7baa2a1fcc272ff52f35b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 15:17:36 +0200 Subject: [PATCH 15/99] feat: update organization status to organization filters and update props --- .../features/project/application/get-projects-list-qry.ts | 6 +++--- .../features/project/domain/organization-filters.ts | 6 ++++++ .../features/project/domain/organization-status.ts | 6 ------ .../features/project/domain/project-repository.ts | 6 ++++-- .../infrastructure/http-project-repository.test.ts | 4 ++-- .../project/infrastructure/http-project-repository.ts | 4 ++-- .../features/project/ui/components/projects-table.tsx | 8 ++++---- 7 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 src/features/administration/features/project/domain/organization-filters.ts delete mode 100644 src/features/administration/features/project/domain/organization-status.ts diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-list-qry.ts index 52be7fac..6c393a98 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.ts @@ -2,7 +2,7 @@ import { InvalidateCache, Query, UseCaseKey } from '@archimedes/arch' import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' -import { OrganizationWithStatus } from '../domain/organization-status' +import { OrganizationFilters } from '../domain/organization-filters' import { Project } from '../domain/project' import type { ProjectRepository } from '../domain/project-repository' import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' @@ -11,7 +11,7 @@ import { Id } from '../../../../../shared/types/id' @UseCaseKey('GetProjectsListQry') @InvalidateCache @singleton() -export class GetProjectsListQry extends Query { +export class GetProjectsListQry extends Query { constructor( @inject(ADMINISTRATION_PROJECT_REPOSITORY) private projectRepository: ProjectRepository, private getUsersListQry: GetUsersListQry, @@ -20,7 +20,7 @@ export class GetProjectsListQry extends Query super() } - async internalExecute(organizationStatus?: OrganizationWithStatus): Promise { + async internalExecute(organizationStatus?: OrganizationFilters): Promise { if (organizationStatus) { const projects = await this.projectRepository.getProjects(organizationStatus) const usersList = await this.getUsersListQry.execute({ diff --git a/src/features/administration/features/project/domain/organization-filters.ts b/src/features/administration/features/project/domain/organization-filters.ts new file mode 100644 index 00000000..5b9fe70a --- /dev/null +++ b/src/features/administration/features/project/domain/organization-filters.ts @@ -0,0 +1,6 @@ +import { Id } from '@archimedes/arch' + +export interface OrganizationFilters { + organizationIds?: Id[] + open?: boolean +} diff --git a/src/features/administration/features/project/domain/organization-status.ts b/src/features/administration/features/project/domain/organization-status.ts deleted file mode 100644 index 43aa73ac..00000000 --- a/src/features/administration/features/project/domain/organization-status.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Id } from '@archimedes/arch' - -export interface OrganizationWithStatus { - organizationId: Id - open: boolean -} diff --git a/src/features/administration/features/project/domain/project-repository.ts b/src/features/administration/features/project/domain/project-repository.ts index 54892f55..ad6046fb 100644 --- a/src/features/administration/features/project/domain/project-repository.ts +++ b/src/features/administration/features/project/domain/project-repository.ts @@ -1,9 +1,11 @@ import { Id } from '../../../../../shared/types/id' -import { OrganizationWithStatus } from './organization-status' +import { OrganizationFilters } from './organization-filters' import { Project } from './project' export interface ProjectRepository { - getProjects(organizationStatus?: OrganizationWithStatus): Promise + getProjects(organizationStatus?: OrganizationFilters): Promise + blockProject(projectId: Id, date: Date): Promise + setUnblock(projectId: Id): Promise } diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.test.ts b/src/features/administration/features/project/infrastructure/http-project-repository.test.ts index 49a298d6..e46f9b8c 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.test.ts +++ b/src/features/administration/features/project/infrastructure/http-project-repository.test.ts @@ -10,10 +10,10 @@ describe('HttpProjectRepository', () => { httpClient.get.mockResolvedValue(ProjectMother.projectsFilteredByOrganization()) - const result = await projectRepository.getProjects({ organizationId: 1, open: true }) + const result = await projectRepository.getProjects({ organizationIds: [1], open: true }) expect(httpClient.get).toHaveBeenCalledWith('/api/project', { - params: { organizationId: 1, open: true } + params: { organizationIds: [1], open: true } }) expect(result).toEqual(ProjectMother.projectsFilteredByOrganizationDateIso()) diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.ts b/src/features/administration/features/project/infrastructure/http-project-repository.ts index 20499876..0633dcb3 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.ts +++ b/src/features/administration/features/project/infrastructure/http-project-repository.ts @@ -2,7 +2,7 @@ import { HttpClient } from '../../../../../shared/http/http-client' import { singleton } from 'tsyringe' import { Id } from '../../../../../shared/types/id' import { chrono } from '../../../../../shared/utils/chrono' -import { OrganizationWithStatus } from '../domain/organization-status' +import { OrganizationFilters } from '../domain/organization-filters' import { Project } from '../domain/project' import { ProjectDto } from '../domain/project-dto' import { ProjectRepository } from '../domain/project-repository' @@ -16,7 +16,7 @@ export class HttpProjectRepository implements ProjectRepository { constructor(private httpClient: HttpClient) {} - async getProjects(organizationWithStatus: OrganizationWithStatus): Promise { + async getProjects(organizationWithStatus: OrganizationFilters): Promise { const data = await this.httpClient.get(HttpProjectRepository.projectPath, { params: organizationWithStatus }) diff --git a/src/features/administration/features/project/ui/components/projects-table.tsx b/src/features/administration/features/project/ui/components/projects-table.tsx index 63c12a5e..d535ff48 100644 --- a/src/features/administration/features/project/ui/components/projects-table.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.tsx @@ -9,7 +9,7 @@ import { ColumnsProps } from '../../../../../../shared/components/table/table.ty import { BlockProjectCmd } from '../../application/block-project-cmd' import { GetProjectsListQry } from '../../application/get-projects-list-qry' import { UnblockProjectCmd } from '../../application/unblock-project-cmd' -import { OrganizationWithStatus } from '../../domain/organization-status' +import { OrganizationFilters } from '../../domain/organization-filters' import { Project } from '../../domain/project' import { ProjectStatus } from '../../domain/project-status' import { AdaptedProjects, adaptProjectsToTable } from '../projects-page-utils' @@ -26,7 +26,7 @@ export const ProjectsTable: FC = (props) => { const { t } = useTranslation() const [organizationName, setOrganizationName] = useState('') const [lastSelectedOrganizationWithStatus, setLastSelectedOrganizationWithStatus] = - useState() + useState() const [tableProjects, setTableProjects] = useState([]) const isMobile = useIsMobile() @@ -57,8 +57,8 @@ export const ProjectsTable: FC = (props) => { async (organization: Organization, status: ProjectStatus) => { if (organization?.id) { setOrganizationName(organization.name) - const organizationWithStatus: OrganizationWithStatus = { - organizationId: organization.id, + const organizationWithStatus: OrganizationFilters = { + organizationIds: [organization.id], open: status.value } await getProjectsListQry(organizationWithStatus) From a26e63ba561c4eb03fc9aeca28a3a4ef8fecb249 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 15:36:25 +0200 Subject: [PATCH 16/99] refactor: move project from administration feature to shared --- .../project/ui/components/block-project-modal.tsx | 6 +++--- .../project/ui/components/combos/projects-combos.tsx | 4 ++-- .../components/combos/projects-filter-form.schema.ts | 2 +- .../project/ui/components/combos/status-combo.tsx | 4 ++-- .../project/ui/components/projects-table.tsx | 12 ++++++------ .../project/ui/components/unblock-project-modal.tsx | 9 ++++++--- .../features/project/ui/projects-page-utils.tsx | 3 ++- .../features/project/ui/projects-page.tsx | 2 +- .../administration}/block-project-cmd.test.ts | 2 +- .../application/administration}/block-project-cmd.ts | 2 +- .../administration}/get-projects-list-qry.test.ts | 8 ++++---- .../administration}/get-projects-list-qry.ts | 10 +++++----- .../administration}/unblock-project-cmd.test.ts | 2 +- .../administration}/unblock-project-cmd.ts | 2 +- .../project/domain/organization-filters.ts | 0 .../project/domain/project-code-error.ts | 0 .../project/domain/project-dto.ts | 2 +- .../project/domain/project-repository.ts | 0 .../project/domain/project-status.ts | 0 .../features => shared}/project/domain/project.ts | 0 .../project/domain/services/project-error-message.ts | 4 ++-- .../domain/services/projects-with-user-name.ts | 2 +- .../features => shared}/project/domain/status.ts | 0 .../project/domain/tests/project-mother.ts | 2 +- .../infrastructure/fake-project-repository.ts | 0 .../infrastructure/http-project-repository.test.ts | 4 ++-- .../infrastructure/http-project-repository.ts | 6 +++--- .../project/infrastructure/project-mapper.ts | 2 +- src/shared/archimedes/archimedes.ts | 6 +++--- src/shared/di/container.ts | 2 +- 30 files changed, 51 insertions(+), 47 deletions(-) rename src/features/{administration/features/project/application => shared/project/application/administration}/block-project-cmd.test.ts (90%) rename src/features/{administration/features/project/application => shared/project/application/administration}/block-project-cmd.ts (89%) rename src/features/{administration/features/project/application => shared/project/application/administration}/get-projects-list-qry.test.ts (78%) rename src/features/{administration/features/project/application => shared/project/application/administration}/get-projects-list-qry.ts (75%) rename src/features/{administration/features/project/application => shared/project/application/administration}/unblock-project-cmd.test.ts (89%) rename src/features/{administration/features/project/application => shared/project/application/administration}/unblock-project-cmd.ts (88%) rename src/features/{administration/features => shared}/project/domain/organization-filters.ts (100%) rename src/features/{administration/features => shared}/project/domain/project-code-error.ts (100%) rename src/features/{administration/features => shared}/project/domain/project-dto.ts (78%) rename src/features/{administration/features => shared}/project/domain/project-repository.ts (100%) rename src/features/{administration/features => shared}/project/domain/project-status.ts (100%) rename src/features/{administration/features => shared}/project/domain/project.ts (100%) rename src/features/{administration/features => shared}/project/domain/services/project-error-message.ts (83%) rename src/features/{administration/features => shared}/project/domain/services/projects-with-user-name.ts (88%) rename src/features/{administration/features => shared}/project/domain/status.ts (100%) rename src/features/{administration/features => shared}/project/domain/tests/project-mother.ts (97%) rename src/features/{administration/features => shared}/project/infrastructure/fake-project-repository.ts (100%) rename src/features/{administration/features => shared}/project/infrastructure/http-project-repository.test.ts (92%) rename src/features/{administration/features => shared}/project/infrastructure/http-project-repository.ts (88%) rename src/features/{administration/features => shared}/project/infrastructure/project-mapper.ts (89%) diff --git a/src/features/administration/features/project/ui/components/block-project-modal.tsx b/src/features/administration/features/project/ui/components/block-project-modal.tsx index 8de58742..e543494d 100644 --- a/src/features/administration/features/project/ui/components/block-project-modal.tsx +++ b/src/features/administration/features/project/ui/components/block-project-modal.tsx @@ -18,11 +18,11 @@ import { SubmitButton } from '../../../../../../shared/components/form-fields/su import { useResolve } from '../../../../../../shared/di/use-resolve' import { DateField } from '../../../../../../shared/components/form-fields/date-field' import { chrono, parseISO } from '../../../../../../shared/utils/chrono' -import { BlockProjectCmd } from '../../application/block-project-cmd' -import { Project } from '../../domain/project' -import { ProjectErrorMessage } from '../../domain/services/project-error-message' import { ProjectModalFormSchema, ProjectModalFormValidationSchema } from './project-modal.schema' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' +import { Project } from '../../../../../shared/project/domain/project' +import { ProjectErrorMessage } from '../../../../../shared/project/domain/services/project-error-message' +import { BlockProjectCmd } from '../../../../../shared/project/application/administration/block-project-cmd' type ProjectModalProps = { onClose(): void diff --git a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx index 364c48b4..dde1b73d 100644 --- a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx +++ b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx @@ -5,14 +5,14 @@ import { useForm, useWatch } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { OrganizationsCombo } from '../../../../../../binnacle/features/activity/ui/components/activity-form/components/combos/organizations-combo' import { Organization } from '../../../../../../binnacle/features/organization/domain/organization' -import { ProjectStatus } from '../../../domain/project-status' import { ProjectsFilterFormSchema, ProjectsFilterFormValidationSchema } from './projects-filter-form.schema' import { StatusCombo } from './status-combo' import { useIsMobile } from '../../../../../../../shared/hooks/use-is-mobile' -import { Status } from '../../../domain/status' +import { ProjectStatus } from '../../../../../../shared/project/domain/project-status' +import { Status } from '../../../../../../shared/project/domain/status' interface ProjectsFilterProps { onFiltersChange: (organization: Organization, status: ProjectStatus) => Promise diff --git a/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts b/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts index 17b3d9a9..00efa36c 100644 --- a/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts +++ b/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts @@ -1,7 +1,7 @@ import { Organization } from '../../../../../../binnacle/features/organization/domain/organization' import { i18n } from '../../../../../../../shared/i18n/i18n' import { object, ObjectSchema } from 'yup' -import { ProjectStatus } from '../../../domain/project-status' +import { ProjectStatus } from '../../../../../../shared/project/domain/project-status' export interface ProjectsFilterFormSchema { organization?: Organization diff --git a/src/features/administration/features/project/ui/components/combos/status-combo.tsx b/src/features/administration/features/project/ui/components/combos/status-combo.tsx index 5d015b2e..89ca3dd9 100644 --- a/src/features/administration/features/project/ui/components/combos/status-combo.tsx +++ b/src/features/administration/features/project/ui/components/combos/status-combo.tsx @@ -2,8 +2,8 @@ import { Control } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { ComboField } from '../../../../../../../shared/components/form-fields/combo-field' import { FC } from 'react' -import { ProjectStatus } from '../../../domain/project-status' -import { Status } from '../../../domain/status' +import { Status } from '../../../../../../shared/project/domain/status' +import { ProjectStatus } from '../../../../../../shared/project/domain/project-status' interface ComboProps { name?: string diff --git a/src/features/administration/features/project/ui/components/projects-table.tsx b/src/features/administration/features/project/ui/components/projects-table.tsx index d535ff48..85d8d450 100644 --- a/src/features/administration/features/project/ui/components/projects-table.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.tsx @@ -6,16 +6,16 @@ import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/us import { useSubscribeToUseCase } from '../../../../../../shared/arch/hooks/use-subscribe-to-use-case' import { Table } from '../../../../../../shared/components/table/table' import { ColumnsProps } from '../../../../../../shared/components/table/table.types' -import { BlockProjectCmd } from '../../application/block-project-cmd' -import { GetProjectsListQry } from '../../application/get-projects-list-qry' -import { UnblockProjectCmd } from '../../application/unblock-project-cmd' -import { OrganizationFilters } from '../../domain/organization-filters' -import { Project } from '../../domain/project' -import { ProjectStatus } from '../../domain/project-status' import { AdaptedProjects, adaptProjectsToTable } from '../projects-page-utils' import { ProjectsFilterFormCombos } from './combos/projects-combos' import { StatusBadge } from './status-badge' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' +import { Project } from '../../../../../shared/project/domain/project' +import { OrganizationFilters } from '../../../../../shared/project/domain/organization-filters' +import { GetProjectsListQry } from '../../../../../shared/project/application/administration/get-projects-list-qry' +import { BlockProjectCmd } from '../../../../../shared/project/application/administration/block-project-cmd' +import { UnblockProjectCmd } from '../../../../../shared/project/application/administration/unblock-project-cmd' +import { ProjectStatus } from '../../../../../shared/project/domain/project-status' interface Props { onProjectClicked(project: Project): void diff --git a/src/features/administration/features/project/ui/components/unblock-project-modal.tsx b/src/features/administration/features/project/ui/components/unblock-project-modal.tsx index ac8f06c7..78a502b4 100644 --- a/src/features/administration/features/project/ui/components/unblock-project-modal.tsx +++ b/src/features/administration/features/project/ui/components/unblock-project-modal.tsx @@ -14,15 +14,18 @@ import { FC } from 'react' import { useGetUseCase } from '../../../../../../shared/arch/hooks/use-get-use-case' import { useResolve } from '../../../../../../shared/di/use-resolve' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' -import { UnblockProjectCmd } from '../../application/unblock-project-cmd' -import { Project } from '../../domain/project' -import { ProjectErrorMessage } from '../../domain/services/project-error-message' +import { Project } from '../../../../../shared/project/domain/project' +import { ProjectErrorMessage } from '../../../../../shared/project/domain/services/project-error-message' +import { UnblockProjectCmd } from '../../../../../shared/project/application/administration/unblock-project-cmd' interface Props { project: Project + onCancel(): void + onClose(): void } + export const UnblockProjectModal: FC = (props) => { const { project, onClose, onCancel } = props const isMobile = useIsMobile() diff --git a/src/features/administration/features/project/ui/projects-page-utils.tsx b/src/features/administration/features/project/ui/projects-page-utils.tsx index 6cb8dc2e..55b05f19 100644 --- a/src/features/administration/features/project/ui/projects-page-utils.tsx +++ b/src/features/administration/features/project/ui/projects-page-utils.tsx @@ -1,5 +1,6 @@ -import { Project } from '../domain/project' import { chrono } from '../../../../../shared/utils/chrono' +import { Project } from '../../../../shared/project/domain/project' + export interface AdaptedProjects { key: number organization: string diff --git a/src/features/administration/features/project/ui/projects-page.tsx b/src/features/administration/features/project/ui/projects-page.tsx index df47dad5..8cf5ed0e 100644 --- a/src/features/administration/features/project/ui/projects-page.tsx +++ b/src/features/administration/features/project/ui/projects-page.tsx @@ -1,10 +1,10 @@ import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' -import { Project } from '../domain/project' import { BlockProjectModal } from './components/block-project-modal' import { ProjectsTable } from './components/projects-table' import { UnblockProjectModal } from './components/unblock-project-modal' +import { Project } from '../../../../shared/project/domain/project' const ProjectsPage: FC = () => { const { t } = useTranslation() diff --git a/src/features/administration/features/project/application/block-project-cmd.test.ts b/src/features/shared/project/application/administration/block-project-cmd.test.ts similarity index 90% rename from src/features/administration/features/project/application/block-project-cmd.test.ts rename to src/features/shared/project/application/administration/block-project-cmd.test.ts index b3abf629..abcd7d6e 100644 --- a/src/features/administration/features/project/application/block-project-cmd.test.ts +++ b/src/features/shared/project/application/administration/block-project-cmd.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended' -import { ProjectRepository } from '../domain/project-repository' import { BlockProjectCmd } from './block-project-cmd' +import { ProjectRepository } from '../../domain/project-repository' describe('BlockProjectCmd', () => { it('should block a project', async () => { diff --git a/src/features/administration/features/project/application/block-project-cmd.ts b/src/features/shared/project/application/administration/block-project-cmd.ts similarity index 89% rename from src/features/administration/features/project/application/block-project-cmd.ts rename to src/features/shared/project/application/administration/block-project-cmd.ts index ec7b06e5..da0ce6c9 100644 --- a/src/features/administration/features/project/application/block-project-cmd.ts +++ b/src/features/shared/project/application/administration/block-project-cmd.ts @@ -2,7 +2,7 @@ import { Command, UseCaseKey } from '@archimedes/arch' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { Id } from '../../../../../shared/types/id' import { inject, singleton } from 'tsyringe' -import type { ProjectRepository } from '../domain/project-repository' +import type { ProjectRepository } from '../../domain/project-repository' @UseCaseKey('BlockProjectCmd') @singleton() diff --git a/src/features/administration/features/project/application/get-projects-list-qry.test.ts b/src/features/shared/project/application/administration/get-projects-list-qry.test.ts similarity index 78% rename from src/features/administration/features/project/application/get-projects-list-qry.test.ts rename to src/features/shared/project/application/administration/get-projects-list-qry.test.ts index dcaac761..59983b93 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.test.ts +++ b/src/features/shared/project/application/administration/get-projects-list-qry.test.ts @@ -1,9 +1,9 @@ -import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { mock } from 'jest-mock-extended' -import { ProjectRepository } from '../domain/project-repository' -import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' +import { ProjectMother } from '../../domain/tests/project-mother' +import { ProjectRepository } from '../../domain/project-repository' +import { GetUsersListQry } from '../../../user/application/get-users-list-qry' +import { ProjectsWithUserName } from '../../domain/services/projects-with-user-name' import { GetProjectsListQry } from './get-projects-list-qry' -import { ProjectMother } from '../domain/tests/project-mother' describe('GetProjectsListQry', () => { it('should get the project list', async () => { diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/shared/project/application/administration/get-projects-list-qry.ts similarity index 75% rename from src/features/administration/features/project/application/get-projects-list-qry.ts rename to src/features/shared/project/application/administration/get-projects-list-qry.ts index 6c393a98..718276e7 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/shared/project/application/administration/get-projects-list-qry.ts @@ -1,12 +1,12 @@ import { InvalidateCache, Query, UseCaseKey } from '@archimedes/arch' -import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' +import { GetUsersListQry } from '../../../user/application/get-users-list-qry' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' -import { OrganizationFilters } from '../domain/organization-filters' -import { Project } from '../domain/project' -import type { ProjectRepository } from '../domain/project-repository' -import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { Id } from '../../../../../shared/types/id' +import { Project } from '../../domain/project' +import { OrganizationFilters } from '../../domain/organization-filters' +import type { ProjectRepository } from '../../domain/project-repository' +import { ProjectsWithUserName } from '../../domain/services/projects-with-user-name' @UseCaseKey('GetProjectsListQry') @InvalidateCache diff --git a/src/features/administration/features/project/application/unblock-project-cmd.test.ts b/src/features/shared/project/application/administration/unblock-project-cmd.test.ts similarity index 89% rename from src/features/administration/features/project/application/unblock-project-cmd.test.ts rename to src/features/shared/project/application/administration/unblock-project-cmd.test.ts index a7e286d0..5210a69e 100644 --- a/src/features/administration/features/project/application/unblock-project-cmd.test.ts +++ b/src/features/shared/project/application/administration/unblock-project-cmd.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended' -import { ProjectRepository } from '../domain/project-repository' import { UnblockProjectCmd } from './unblock-project-cmd' +import { ProjectRepository } from '../../domain/project-repository' describe('UnblockProjectCmd', () => { it('should unblock a project', async () => { diff --git a/src/features/administration/features/project/application/unblock-project-cmd.ts b/src/features/shared/project/application/administration/unblock-project-cmd.ts similarity index 88% rename from src/features/administration/features/project/application/unblock-project-cmd.ts rename to src/features/shared/project/application/administration/unblock-project-cmd.ts index f0d326ee..18d3c28d 100644 --- a/src/features/administration/features/project/application/unblock-project-cmd.ts +++ b/src/features/shared/project/application/administration/unblock-project-cmd.ts @@ -1,7 +1,7 @@ import { Command, Id, UseCaseKey } from '@archimedes/arch' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' -import type { ProjectRepository } from '../domain/project-repository' +import type { ProjectRepository } from '../../domain/project-repository' @UseCaseKey('UnblockProjectCmd') @singleton() diff --git a/src/features/administration/features/project/domain/organization-filters.ts b/src/features/shared/project/domain/organization-filters.ts similarity index 100% rename from src/features/administration/features/project/domain/organization-filters.ts rename to src/features/shared/project/domain/organization-filters.ts diff --git a/src/features/administration/features/project/domain/project-code-error.ts b/src/features/shared/project/domain/project-code-error.ts similarity index 100% rename from src/features/administration/features/project/domain/project-code-error.ts rename to src/features/shared/project/domain/project-code-error.ts diff --git a/src/features/administration/features/project/domain/project-dto.ts b/src/features/shared/project/domain/project-dto.ts similarity index 78% rename from src/features/administration/features/project/domain/project-dto.ts rename to src/features/shared/project/domain/project-dto.ts index 2f409acc..2a013346 100644 --- a/src/features/administration/features/project/domain/project-dto.ts +++ b/src/features/shared/project/domain/project-dto.ts @@ -1,4 +1,4 @@ -import { Id } from '../../../../../shared/types/id' +import { Id } from '../../../../shared/types/id' export interface ProjectDto { id: Id diff --git a/src/features/administration/features/project/domain/project-repository.ts b/src/features/shared/project/domain/project-repository.ts similarity index 100% rename from src/features/administration/features/project/domain/project-repository.ts rename to src/features/shared/project/domain/project-repository.ts diff --git a/src/features/administration/features/project/domain/project-status.ts b/src/features/shared/project/domain/project-status.ts similarity index 100% rename from src/features/administration/features/project/domain/project-status.ts rename to src/features/shared/project/domain/project-status.ts diff --git a/src/features/administration/features/project/domain/project.ts b/src/features/shared/project/domain/project.ts similarity index 100% rename from src/features/administration/features/project/domain/project.ts rename to src/features/shared/project/domain/project.ts diff --git a/src/features/administration/features/project/domain/services/project-error-message.ts b/src/features/shared/project/domain/services/project-error-message.ts similarity index 83% rename from src/features/administration/features/project/domain/services/project-error-message.ts rename to src/features/shared/project/domain/services/project-error-message.ts index 22ea5e6c..0ba67ea9 100644 --- a/src/features/administration/features/project/domain/services/project-error-message.ts +++ b/src/features/shared/project/domain/services/project-error-message.ts @@ -1,7 +1,7 @@ -import { i18n } from '../../../../../../shared/i18n/i18n' -import { NotificationMessage } from '../../../../../../shared/notification/notification-message' import { injectable } from 'tsyringe' import { ProjectCodeError } from '../project-code-error' +import { NotificationMessage } from '../../../../../shared/notification/notification-message' +import { i18n } from '../../../../../shared/i18n/i18n' type ProjectCodeError = keyof typeof ProjectCodeError diff --git a/src/features/administration/features/project/domain/services/projects-with-user-name.ts b/src/features/shared/project/domain/services/projects-with-user-name.ts similarity index 88% rename from src/features/administration/features/project/domain/services/projects-with-user-name.ts rename to src/features/shared/project/domain/services/projects-with-user-name.ts index 2d5d7cd9..1df00a75 100644 --- a/src/features/administration/features/project/domain/services/projects-with-user-name.ts +++ b/src/features/shared/project/domain/services/projects-with-user-name.ts @@ -1,6 +1,6 @@ import { singleton } from 'tsyringe' -import { UserInfo } from '../../../../../shared/user/domain/user-info' import { Project } from '../project' +import { UserInfo } from '../../../user/domain/user-info' @singleton() export class ProjectsWithUserName { diff --git a/src/features/administration/features/project/domain/status.ts b/src/features/shared/project/domain/status.ts similarity index 100% rename from src/features/administration/features/project/domain/status.ts rename to src/features/shared/project/domain/status.ts diff --git a/src/features/administration/features/project/domain/tests/project-mother.ts b/src/features/shared/project/domain/tests/project-mother.ts similarity index 97% rename from src/features/administration/features/project/domain/tests/project-mother.ts rename to src/features/shared/project/domain/tests/project-mother.ts index c2aee2e6..8b13c8fd 100644 --- a/src/features/administration/features/project/domain/tests/project-mother.ts +++ b/src/features/shared/project/domain/tests/project-mother.ts @@ -1,6 +1,6 @@ -import { parseISO } from '../../../../../../shared/utils/chrono' import { ProjectDto } from '../project-dto' import { Project } from '../project' +import { parseISO } from '../../../../../shared/utils/chrono' export class ProjectMother { static projectsFilteredByOrganization(): ProjectDto[] { diff --git a/src/features/administration/features/project/infrastructure/fake-project-repository.ts b/src/features/shared/project/infrastructure/fake-project-repository.ts similarity index 100% rename from src/features/administration/features/project/infrastructure/fake-project-repository.ts rename to src/features/shared/project/infrastructure/fake-project-repository.ts diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.test.ts b/src/features/shared/project/infrastructure/http-project-repository.test.ts similarity index 92% rename from src/features/administration/features/project/infrastructure/http-project-repository.test.ts rename to src/features/shared/project/infrastructure/http-project-repository.test.ts index e46f9b8c..8bccee68 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.test.ts +++ b/src/features/shared/project/infrastructure/http-project-repository.test.ts @@ -1,8 +1,8 @@ import { mock } from 'jest-mock-extended' -import { HttpClient } from '../../../../../shared/http/http-client' -import { chrono } from '../../../../../shared/utils/chrono' import { ProjectMother } from '../domain/tests/project-mother' import { HttpProjectRepository } from './http-project-repository' +import { chrono } from '../../../../shared/utils/chrono' +import { HttpClient } from '../../../../shared/http/http-client' describe('HttpProjectRepository', () => { test('should get projects by organizationId', async () => { diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.ts b/src/features/shared/project/infrastructure/http-project-repository.ts similarity index 88% rename from src/features/administration/features/project/infrastructure/http-project-repository.ts rename to src/features/shared/project/infrastructure/http-project-repository.ts index 0633dcb3..a94388ee 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.ts +++ b/src/features/shared/project/infrastructure/http-project-repository.ts @@ -1,12 +1,12 @@ -import { HttpClient } from '../../../../../shared/http/http-client' import { singleton } from 'tsyringe' -import { Id } from '../../../../../shared/types/id' -import { chrono } from '../../../../../shared/utils/chrono' import { OrganizationFilters } from '../domain/organization-filters' import { Project } from '../domain/project' import { ProjectDto } from '../domain/project-dto' import { ProjectRepository } from '../domain/project-repository' import { ProjectMapper } from './project-mapper' +import { Id } from '../../../../shared/types/id' +import { HttpClient } from '../../../../shared/http/http-client' +import { chrono } from '../../../../shared/utils/chrono' @singleton() export class HttpProjectRepository implements ProjectRepository { diff --git a/src/features/administration/features/project/infrastructure/project-mapper.ts b/src/features/shared/project/infrastructure/project-mapper.ts similarity index 89% rename from src/features/administration/features/project/infrastructure/project-mapper.ts rename to src/features/shared/project/infrastructure/project-mapper.ts index aad3629d..be2c0948 100644 --- a/src/features/administration/features/project/infrastructure/project-mapper.ts +++ b/src/features/shared/project/infrastructure/project-mapper.ts @@ -1,6 +1,6 @@ -import { parseISO } from '../../../../../shared/utils/chrono' import { ProjectDto } from '../domain/project-dto' import { Project } from '../domain/project' +import { parseISO } from '../../../../shared/utils/chrono' export class ProjectMapper { static toDomain(projectDto: ProjectDto): Project { diff --git a/src/shared/archimedes/archimedes.ts b/src/shared/archimedes/archimedes.ts index 88d29df9..4374399a 100644 --- a/src/shared/archimedes/archimedes.ts +++ b/src/shared/archimedes/archimedes.ts @@ -33,11 +33,11 @@ import { GetUserSettingsQry } from '../../features/shared/user/features/settings import { SaveUserSettingsCmd } from '../../features/shared/user/features/settings/application/save-user-settings-cmd' import { TOAST } from '../di/container-tokens' import { container } from 'tsyringe' -import { BlockProjectCmd } from '../../features/administration/features/project/application/block-project-cmd' -import { GetProjectsListQry } from '../../features/administration/features/project/application/get-projects-list-qry' -import { UnblockProjectCmd } from '../../features/administration/features/project/application/unblock-project-cmd' import { ToastNotificationLink } from './links/toast-notification-link' import { ToastType } from '../notification/toast' +import { BlockProjectCmd } from '../../features/shared/project/application/administration/block-project-cmd' +import { GetProjectsListQry } from '../../features/shared/project/application/administration/get-projects-list-qry' +import { UnblockProjectCmd } from '../../features/shared/project/application/administration/unblock-project-cmd' const toast = container.resolve(TOAST) Archimedes.createChain([ diff --git a/src/shared/di/container.ts b/src/shared/di/container.ts index 46fd26f4..ff21fd8b 100644 --- a/src/shared/di/container.ts +++ b/src/shared/di/container.ts @@ -1,4 +1,4 @@ -import { HttpProjectRepository as HttpAdministrationProjectRepository } from '../../features/administration/features/project/infrastructure/http-project-repository' +import { HttpProjectRepository as HttpAdministrationProjectRepository } from '../../features/shared/project/infrastructure/http-project-repository' import { HttpAuthRepository } from '../../features/auth/infrastructure/http-auth-repository' import { HttpActivityRepository } from '../../features/binnacle/features/activity/infrastructure/http-activity-repository' import { HttpHolidayRepository } from '../../features/binnacle/features/holiday/infrastructure/http-holiday-repository' From 4dadf41016f254c924f2d725786bda63efd4465b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 15:57:47 +0200 Subject: [PATCH 17/99] Revert "refactor: move project from administration feature to shared" This reverts commit a26e63ba561c4eb03fc9aeca28a3a4ef8fecb249. --- .../project/application}/block-project-cmd.test.ts | 2 +- .../project/application}/block-project-cmd.ts | 2 +- .../application}/get-projects-list-qry.test.ts | 8 ++++---- .../project/application}/get-projects-list-qry.ts | 10 +++++----- .../project/application}/unblock-project-cmd.test.ts | 2 +- .../project/application}/unblock-project-cmd.ts | 2 +- .../features}/project/domain/organization-filters.ts | 0 .../features}/project/domain/project-code-error.ts | 0 .../features}/project/domain/project-dto.ts | 2 +- .../features}/project/domain/project-repository.ts | 0 .../features}/project/domain/project-status.ts | 0 .../features}/project/domain/project.ts | 0 .../project/domain/services/project-error-message.ts | 4 ++-- .../domain/services/projects-with-user-name.ts | 2 +- .../features}/project/domain/status.ts | 0 .../features}/project/domain/tests/project-mother.ts | 2 +- .../infrastructure/fake-project-repository.ts | 0 .../infrastructure/http-project-repository.test.ts | 4 ++-- .../infrastructure/http-project-repository.ts | 6 +++--- .../project/infrastructure/project-mapper.ts | 2 +- .../project/ui/components/block-project-modal.tsx | 6 +++--- .../project/ui/components/combos/projects-combos.tsx | 4 ++-- .../components/combos/projects-filter-form.schema.ts | 2 +- .../project/ui/components/combos/status-combo.tsx | 4 ++-- .../project/ui/components/projects-table.tsx | 12 ++++++------ .../project/ui/components/unblock-project-modal.tsx | 9 +++------ .../features/project/ui/projects-page-utils.tsx | 3 +-- .../features/project/ui/projects-page.tsx | 2 +- src/shared/archimedes/archimedes.ts | 6 +++--- src/shared/di/container.ts | 2 +- 30 files changed, 47 insertions(+), 51 deletions(-) rename src/features/{shared/project/application/administration => administration/features/project/application}/block-project-cmd.test.ts (90%) rename src/features/{shared/project/application/administration => administration/features/project/application}/block-project-cmd.ts (89%) rename src/features/{shared/project/application/administration => administration/features/project/application}/get-projects-list-qry.test.ts (78%) rename src/features/{shared/project/application/administration => administration/features/project/application}/get-projects-list-qry.ts (75%) rename src/features/{shared/project/application/administration => administration/features/project/application}/unblock-project-cmd.test.ts (89%) rename src/features/{shared/project/application/administration => administration/features/project/application}/unblock-project-cmd.ts (88%) rename src/features/{shared => administration/features}/project/domain/organization-filters.ts (100%) rename src/features/{shared => administration/features}/project/domain/project-code-error.ts (100%) rename src/features/{shared => administration/features}/project/domain/project-dto.ts (78%) rename src/features/{shared => administration/features}/project/domain/project-repository.ts (100%) rename src/features/{shared => administration/features}/project/domain/project-status.ts (100%) rename src/features/{shared => administration/features}/project/domain/project.ts (100%) rename src/features/{shared => administration/features}/project/domain/services/project-error-message.ts (83%) rename src/features/{shared => administration/features}/project/domain/services/projects-with-user-name.ts (88%) rename src/features/{shared => administration/features}/project/domain/status.ts (100%) rename src/features/{shared => administration/features}/project/domain/tests/project-mother.ts (97%) rename src/features/{shared => administration/features}/project/infrastructure/fake-project-repository.ts (100%) rename src/features/{shared => administration/features}/project/infrastructure/http-project-repository.test.ts (92%) rename src/features/{shared => administration/features}/project/infrastructure/http-project-repository.ts (88%) rename src/features/{shared => administration/features}/project/infrastructure/project-mapper.ts (89%) diff --git a/src/features/shared/project/application/administration/block-project-cmd.test.ts b/src/features/administration/features/project/application/block-project-cmd.test.ts similarity index 90% rename from src/features/shared/project/application/administration/block-project-cmd.test.ts rename to src/features/administration/features/project/application/block-project-cmd.test.ts index abcd7d6e..b3abf629 100644 --- a/src/features/shared/project/application/administration/block-project-cmd.test.ts +++ b/src/features/administration/features/project/application/block-project-cmd.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended' +import { ProjectRepository } from '../domain/project-repository' import { BlockProjectCmd } from './block-project-cmd' -import { ProjectRepository } from '../../domain/project-repository' describe('BlockProjectCmd', () => { it('should block a project', async () => { diff --git a/src/features/shared/project/application/administration/block-project-cmd.ts b/src/features/administration/features/project/application/block-project-cmd.ts similarity index 89% rename from src/features/shared/project/application/administration/block-project-cmd.ts rename to src/features/administration/features/project/application/block-project-cmd.ts index da0ce6c9..ec7b06e5 100644 --- a/src/features/shared/project/application/administration/block-project-cmd.ts +++ b/src/features/administration/features/project/application/block-project-cmd.ts @@ -2,7 +2,7 @@ import { Command, UseCaseKey } from '@archimedes/arch' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { Id } from '../../../../../shared/types/id' import { inject, singleton } from 'tsyringe' -import type { ProjectRepository } from '../../domain/project-repository' +import type { ProjectRepository } from '../domain/project-repository' @UseCaseKey('BlockProjectCmd') @singleton() diff --git a/src/features/shared/project/application/administration/get-projects-list-qry.test.ts b/src/features/administration/features/project/application/get-projects-list-qry.test.ts similarity index 78% rename from src/features/shared/project/application/administration/get-projects-list-qry.test.ts rename to src/features/administration/features/project/application/get-projects-list-qry.test.ts index 59983b93..dcaac761 100644 --- a/src/features/shared/project/application/administration/get-projects-list-qry.test.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.test.ts @@ -1,9 +1,9 @@ +import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { mock } from 'jest-mock-extended' -import { ProjectMother } from '../../domain/tests/project-mother' -import { ProjectRepository } from '../../domain/project-repository' -import { GetUsersListQry } from '../../../user/application/get-users-list-qry' -import { ProjectsWithUserName } from '../../domain/services/projects-with-user-name' +import { ProjectRepository } from '../domain/project-repository' +import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { GetProjectsListQry } from './get-projects-list-qry' +import { ProjectMother } from '../domain/tests/project-mother' describe('GetProjectsListQry', () => { it('should get the project list', async () => { diff --git a/src/features/shared/project/application/administration/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-list-qry.ts similarity index 75% rename from src/features/shared/project/application/administration/get-projects-list-qry.ts rename to src/features/administration/features/project/application/get-projects-list-qry.ts index 718276e7..6c393a98 100644 --- a/src/features/shared/project/application/administration/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.ts @@ -1,12 +1,12 @@ import { InvalidateCache, Query, UseCaseKey } from '@archimedes/arch' -import { GetUsersListQry } from '../../../user/application/get-users-list-qry' +import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' +import { OrganizationFilters } from '../domain/organization-filters' +import { Project } from '../domain/project' +import type { ProjectRepository } from '../domain/project-repository' +import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { Id } from '../../../../../shared/types/id' -import { Project } from '../../domain/project' -import { OrganizationFilters } from '../../domain/organization-filters' -import type { ProjectRepository } from '../../domain/project-repository' -import { ProjectsWithUserName } from '../../domain/services/projects-with-user-name' @UseCaseKey('GetProjectsListQry') @InvalidateCache diff --git a/src/features/shared/project/application/administration/unblock-project-cmd.test.ts b/src/features/administration/features/project/application/unblock-project-cmd.test.ts similarity index 89% rename from src/features/shared/project/application/administration/unblock-project-cmd.test.ts rename to src/features/administration/features/project/application/unblock-project-cmd.test.ts index 5210a69e..a7e286d0 100644 --- a/src/features/shared/project/application/administration/unblock-project-cmd.test.ts +++ b/src/features/administration/features/project/application/unblock-project-cmd.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended' +import { ProjectRepository } from '../domain/project-repository' import { UnblockProjectCmd } from './unblock-project-cmd' -import { ProjectRepository } from '../../domain/project-repository' describe('UnblockProjectCmd', () => { it('should unblock a project', async () => { diff --git a/src/features/shared/project/application/administration/unblock-project-cmd.ts b/src/features/administration/features/project/application/unblock-project-cmd.ts similarity index 88% rename from src/features/shared/project/application/administration/unblock-project-cmd.ts rename to src/features/administration/features/project/application/unblock-project-cmd.ts index 18d3c28d..f0d326ee 100644 --- a/src/features/shared/project/application/administration/unblock-project-cmd.ts +++ b/src/features/administration/features/project/application/unblock-project-cmd.ts @@ -1,7 +1,7 @@ import { Command, Id, UseCaseKey } from '@archimedes/arch' import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' -import type { ProjectRepository } from '../../domain/project-repository' +import type { ProjectRepository } from '../domain/project-repository' @UseCaseKey('UnblockProjectCmd') @singleton() diff --git a/src/features/shared/project/domain/organization-filters.ts b/src/features/administration/features/project/domain/organization-filters.ts similarity index 100% rename from src/features/shared/project/domain/organization-filters.ts rename to src/features/administration/features/project/domain/organization-filters.ts diff --git a/src/features/shared/project/domain/project-code-error.ts b/src/features/administration/features/project/domain/project-code-error.ts similarity index 100% rename from src/features/shared/project/domain/project-code-error.ts rename to src/features/administration/features/project/domain/project-code-error.ts diff --git a/src/features/shared/project/domain/project-dto.ts b/src/features/administration/features/project/domain/project-dto.ts similarity index 78% rename from src/features/shared/project/domain/project-dto.ts rename to src/features/administration/features/project/domain/project-dto.ts index 2a013346..2f409acc 100644 --- a/src/features/shared/project/domain/project-dto.ts +++ b/src/features/administration/features/project/domain/project-dto.ts @@ -1,4 +1,4 @@ -import { Id } from '../../../../shared/types/id' +import { Id } from '../../../../../shared/types/id' export interface ProjectDto { id: Id diff --git a/src/features/shared/project/domain/project-repository.ts b/src/features/administration/features/project/domain/project-repository.ts similarity index 100% rename from src/features/shared/project/domain/project-repository.ts rename to src/features/administration/features/project/domain/project-repository.ts diff --git a/src/features/shared/project/domain/project-status.ts b/src/features/administration/features/project/domain/project-status.ts similarity index 100% rename from src/features/shared/project/domain/project-status.ts rename to src/features/administration/features/project/domain/project-status.ts diff --git a/src/features/shared/project/domain/project.ts b/src/features/administration/features/project/domain/project.ts similarity index 100% rename from src/features/shared/project/domain/project.ts rename to src/features/administration/features/project/domain/project.ts diff --git a/src/features/shared/project/domain/services/project-error-message.ts b/src/features/administration/features/project/domain/services/project-error-message.ts similarity index 83% rename from src/features/shared/project/domain/services/project-error-message.ts rename to src/features/administration/features/project/domain/services/project-error-message.ts index 0ba67ea9..22ea5e6c 100644 --- a/src/features/shared/project/domain/services/project-error-message.ts +++ b/src/features/administration/features/project/domain/services/project-error-message.ts @@ -1,7 +1,7 @@ +import { i18n } from '../../../../../../shared/i18n/i18n' +import { NotificationMessage } from '../../../../../../shared/notification/notification-message' import { injectable } from 'tsyringe' import { ProjectCodeError } from '../project-code-error' -import { NotificationMessage } from '../../../../../shared/notification/notification-message' -import { i18n } from '../../../../../shared/i18n/i18n' type ProjectCodeError = keyof typeof ProjectCodeError diff --git a/src/features/shared/project/domain/services/projects-with-user-name.ts b/src/features/administration/features/project/domain/services/projects-with-user-name.ts similarity index 88% rename from src/features/shared/project/domain/services/projects-with-user-name.ts rename to src/features/administration/features/project/domain/services/projects-with-user-name.ts index 1df00a75..2d5d7cd9 100644 --- a/src/features/shared/project/domain/services/projects-with-user-name.ts +++ b/src/features/administration/features/project/domain/services/projects-with-user-name.ts @@ -1,6 +1,6 @@ import { singleton } from 'tsyringe' +import { UserInfo } from '../../../../../shared/user/domain/user-info' import { Project } from '../project' -import { UserInfo } from '../../../user/domain/user-info' @singleton() export class ProjectsWithUserName { diff --git a/src/features/shared/project/domain/status.ts b/src/features/administration/features/project/domain/status.ts similarity index 100% rename from src/features/shared/project/domain/status.ts rename to src/features/administration/features/project/domain/status.ts diff --git a/src/features/shared/project/domain/tests/project-mother.ts b/src/features/administration/features/project/domain/tests/project-mother.ts similarity index 97% rename from src/features/shared/project/domain/tests/project-mother.ts rename to src/features/administration/features/project/domain/tests/project-mother.ts index 8b13c8fd..c2aee2e6 100644 --- a/src/features/shared/project/domain/tests/project-mother.ts +++ b/src/features/administration/features/project/domain/tests/project-mother.ts @@ -1,6 +1,6 @@ +import { parseISO } from '../../../../../../shared/utils/chrono' import { ProjectDto } from '../project-dto' import { Project } from '../project' -import { parseISO } from '../../../../../shared/utils/chrono' export class ProjectMother { static projectsFilteredByOrganization(): ProjectDto[] { diff --git a/src/features/shared/project/infrastructure/fake-project-repository.ts b/src/features/administration/features/project/infrastructure/fake-project-repository.ts similarity index 100% rename from src/features/shared/project/infrastructure/fake-project-repository.ts rename to src/features/administration/features/project/infrastructure/fake-project-repository.ts diff --git a/src/features/shared/project/infrastructure/http-project-repository.test.ts b/src/features/administration/features/project/infrastructure/http-project-repository.test.ts similarity index 92% rename from src/features/shared/project/infrastructure/http-project-repository.test.ts rename to src/features/administration/features/project/infrastructure/http-project-repository.test.ts index 8bccee68..e46f9b8c 100644 --- a/src/features/shared/project/infrastructure/http-project-repository.test.ts +++ b/src/features/administration/features/project/infrastructure/http-project-repository.test.ts @@ -1,8 +1,8 @@ import { mock } from 'jest-mock-extended' +import { HttpClient } from '../../../../../shared/http/http-client' +import { chrono } from '../../../../../shared/utils/chrono' import { ProjectMother } from '../domain/tests/project-mother' import { HttpProjectRepository } from './http-project-repository' -import { chrono } from '../../../../shared/utils/chrono' -import { HttpClient } from '../../../../shared/http/http-client' describe('HttpProjectRepository', () => { test('should get projects by organizationId', async () => { diff --git a/src/features/shared/project/infrastructure/http-project-repository.ts b/src/features/administration/features/project/infrastructure/http-project-repository.ts similarity index 88% rename from src/features/shared/project/infrastructure/http-project-repository.ts rename to src/features/administration/features/project/infrastructure/http-project-repository.ts index a94388ee..0633dcb3 100644 --- a/src/features/shared/project/infrastructure/http-project-repository.ts +++ b/src/features/administration/features/project/infrastructure/http-project-repository.ts @@ -1,12 +1,12 @@ +import { HttpClient } from '../../../../../shared/http/http-client' import { singleton } from 'tsyringe' +import { Id } from '../../../../../shared/types/id' +import { chrono } from '../../../../../shared/utils/chrono' import { OrganizationFilters } from '../domain/organization-filters' import { Project } from '../domain/project' import { ProjectDto } from '../domain/project-dto' import { ProjectRepository } from '../domain/project-repository' import { ProjectMapper } from './project-mapper' -import { Id } from '../../../../shared/types/id' -import { HttpClient } from '../../../../shared/http/http-client' -import { chrono } from '../../../../shared/utils/chrono' @singleton() export class HttpProjectRepository implements ProjectRepository { diff --git a/src/features/shared/project/infrastructure/project-mapper.ts b/src/features/administration/features/project/infrastructure/project-mapper.ts similarity index 89% rename from src/features/shared/project/infrastructure/project-mapper.ts rename to src/features/administration/features/project/infrastructure/project-mapper.ts index be2c0948..aad3629d 100644 --- a/src/features/shared/project/infrastructure/project-mapper.ts +++ b/src/features/administration/features/project/infrastructure/project-mapper.ts @@ -1,6 +1,6 @@ +import { parseISO } from '../../../../../shared/utils/chrono' import { ProjectDto } from '../domain/project-dto' import { Project } from '../domain/project' -import { parseISO } from '../../../../shared/utils/chrono' export class ProjectMapper { static toDomain(projectDto: ProjectDto): Project { diff --git a/src/features/administration/features/project/ui/components/block-project-modal.tsx b/src/features/administration/features/project/ui/components/block-project-modal.tsx index e543494d..8de58742 100644 --- a/src/features/administration/features/project/ui/components/block-project-modal.tsx +++ b/src/features/administration/features/project/ui/components/block-project-modal.tsx @@ -18,11 +18,11 @@ import { SubmitButton } from '../../../../../../shared/components/form-fields/su import { useResolve } from '../../../../../../shared/di/use-resolve' import { DateField } from '../../../../../../shared/components/form-fields/date-field' import { chrono, parseISO } from '../../../../../../shared/utils/chrono' +import { BlockProjectCmd } from '../../application/block-project-cmd' +import { Project } from '../../domain/project' +import { ProjectErrorMessage } from '../../domain/services/project-error-message' import { ProjectModalFormSchema, ProjectModalFormValidationSchema } from './project-modal.schema' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' -import { Project } from '../../../../../shared/project/domain/project' -import { ProjectErrorMessage } from '../../../../../shared/project/domain/services/project-error-message' -import { BlockProjectCmd } from '../../../../../shared/project/application/administration/block-project-cmd' type ProjectModalProps = { onClose(): void diff --git a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx index dde1b73d..364c48b4 100644 --- a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx +++ b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx @@ -5,14 +5,14 @@ import { useForm, useWatch } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { OrganizationsCombo } from '../../../../../../binnacle/features/activity/ui/components/activity-form/components/combos/organizations-combo' import { Organization } from '../../../../../../binnacle/features/organization/domain/organization' +import { ProjectStatus } from '../../../domain/project-status' import { ProjectsFilterFormSchema, ProjectsFilterFormValidationSchema } from './projects-filter-form.schema' import { StatusCombo } from './status-combo' import { useIsMobile } from '../../../../../../../shared/hooks/use-is-mobile' -import { ProjectStatus } from '../../../../../../shared/project/domain/project-status' -import { Status } from '../../../../../../shared/project/domain/status' +import { Status } from '../../../domain/status' interface ProjectsFilterProps { onFiltersChange: (organization: Organization, status: ProjectStatus) => Promise diff --git a/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts b/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts index 00efa36c..17b3d9a9 100644 --- a/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts +++ b/src/features/administration/features/project/ui/components/combos/projects-filter-form.schema.ts @@ -1,7 +1,7 @@ import { Organization } from '../../../../../../binnacle/features/organization/domain/organization' import { i18n } from '../../../../../../../shared/i18n/i18n' import { object, ObjectSchema } from 'yup' -import { ProjectStatus } from '../../../../../../shared/project/domain/project-status' +import { ProjectStatus } from '../../../domain/project-status' export interface ProjectsFilterFormSchema { organization?: Organization diff --git a/src/features/administration/features/project/ui/components/combos/status-combo.tsx b/src/features/administration/features/project/ui/components/combos/status-combo.tsx index 89ca3dd9..5d015b2e 100644 --- a/src/features/administration/features/project/ui/components/combos/status-combo.tsx +++ b/src/features/administration/features/project/ui/components/combos/status-combo.tsx @@ -2,8 +2,8 @@ import { Control } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { ComboField } from '../../../../../../../shared/components/form-fields/combo-field' import { FC } from 'react' -import { Status } from '../../../../../../shared/project/domain/status' -import { ProjectStatus } from '../../../../../../shared/project/domain/project-status' +import { ProjectStatus } from '../../../domain/project-status' +import { Status } from '../../../domain/status' interface ComboProps { name?: string diff --git a/src/features/administration/features/project/ui/components/projects-table.tsx b/src/features/administration/features/project/ui/components/projects-table.tsx index 85d8d450..d535ff48 100644 --- a/src/features/administration/features/project/ui/components/projects-table.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.tsx @@ -6,16 +6,16 @@ import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/us import { useSubscribeToUseCase } from '../../../../../../shared/arch/hooks/use-subscribe-to-use-case' import { Table } from '../../../../../../shared/components/table/table' import { ColumnsProps } from '../../../../../../shared/components/table/table.types' +import { BlockProjectCmd } from '../../application/block-project-cmd' +import { GetProjectsListQry } from '../../application/get-projects-list-qry' +import { UnblockProjectCmd } from '../../application/unblock-project-cmd' +import { OrganizationFilters } from '../../domain/organization-filters' +import { Project } from '../../domain/project' +import { ProjectStatus } from '../../domain/project-status' import { AdaptedProjects, adaptProjectsToTable } from '../projects-page-utils' import { ProjectsFilterFormCombos } from './combos/projects-combos' import { StatusBadge } from './status-badge' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' -import { Project } from '../../../../../shared/project/domain/project' -import { OrganizationFilters } from '../../../../../shared/project/domain/organization-filters' -import { GetProjectsListQry } from '../../../../../shared/project/application/administration/get-projects-list-qry' -import { BlockProjectCmd } from '../../../../../shared/project/application/administration/block-project-cmd' -import { UnblockProjectCmd } from '../../../../../shared/project/application/administration/unblock-project-cmd' -import { ProjectStatus } from '../../../../../shared/project/domain/project-status' interface Props { onProjectClicked(project: Project): void diff --git a/src/features/administration/features/project/ui/components/unblock-project-modal.tsx b/src/features/administration/features/project/ui/components/unblock-project-modal.tsx index 78a502b4..ac8f06c7 100644 --- a/src/features/administration/features/project/ui/components/unblock-project-modal.tsx +++ b/src/features/administration/features/project/ui/components/unblock-project-modal.tsx @@ -14,18 +14,15 @@ import { FC } from 'react' import { useGetUseCase } from '../../../../../../shared/arch/hooks/use-get-use-case' import { useResolve } from '../../../../../../shared/di/use-resolve' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' -import { Project } from '../../../../../shared/project/domain/project' -import { ProjectErrorMessage } from '../../../../../shared/project/domain/services/project-error-message' -import { UnblockProjectCmd } from '../../../../../shared/project/application/administration/unblock-project-cmd' +import { UnblockProjectCmd } from '../../application/unblock-project-cmd' +import { Project } from '../../domain/project' +import { ProjectErrorMessage } from '../../domain/services/project-error-message' interface Props { project: Project - onCancel(): void - onClose(): void } - export const UnblockProjectModal: FC = (props) => { const { project, onClose, onCancel } = props const isMobile = useIsMobile() diff --git a/src/features/administration/features/project/ui/projects-page-utils.tsx b/src/features/administration/features/project/ui/projects-page-utils.tsx index 55b05f19..6cb8dc2e 100644 --- a/src/features/administration/features/project/ui/projects-page-utils.tsx +++ b/src/features/administration/features/project/ui/projects-page-utils.tsx @@ -1,6 +1,5 @@ +import { Project } from '../domain/project' import { chrono } from '../../../../../shared/utils/chrono' -import { Project } from '../../../../shared/project/domain/project' - export interface AdaptedProjects { key: number organization: string diff --git a/src/features/administration/features/project/ui/projects-page.tsx b/src/features/administration/features/project/ui/projects-page.tsx index 8cf5ed0e..df47dad5 100644 --- a/src/features/administration/features/project/ui/projects-page.tsx +++ b/src/features/administration/features/project/ui/projects-page.tsx @@ -1,10 +1,10 @@ import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' +import { Project } from '../domain/project' import { BlockProjectModal } from './components/block-project-modal' import { ProjectsTable } from './components/projects-table' import { UnblockProjectModal } from './components/unblock-project-modal' -import { Project } from '../../../../shared/project/domain/project' const ProjectsPage: FC = () => { const { t } = useTranslation() diff --git a/src/shared/archimedes/archimedes.ts b/src/shared/archimedes/archimedes.ts index 4374399a..88d29df9 100644 --- a/src/shared/archimedes/archimedes.ts +++ b/src/shared/archimedes/archimedes.ts @@ -33,11 +33,11 @@ import { GetUserSettingsQry } from '../../features/shared/user/features/settings import { SaveUserSettingsCmd } from '../../features/shared/user/features/settings/application/save-user-settings-cmd' import { TOAST } from '../di/container-tokens' import { container } from 'tsyringe' +import { BlockProjectCmd } from '../../features/administration/features/project/application/block-project-cmd' +import { GetProjectsListQry } from '../../features/administration/features/project/application/get-projects-list-qry' +import { UnblockProjectCmd } from '../../features/administration/features/project/application/unblock-project-cmd' import { ToastNotificationLink } from './links/toast-notification-link' import { ToastType } from '../notification/toast' -import { BlockProjectCmd } from '../../features/shared/project/application/administration/block-project-cmd' -import { GetProjectsListQry } from '../../features/shared/project/application/administration/get-projects-list-qry' -import { UnblockProjectCmd } from '../../features/shared/project/application/administration/unblock-project-cmd' const toast = container.resolve(TOAST) Archimedes.createChain([ diff --git a/src/shared/di/container.ts b/src/shared/di/container.ts index ff21fd8b..46fd26f4 100644 --- a/src/shared/di/container.ts +++ b/src/shared/di/container.ts @@ -1,4 +1,4 @@ -import { HttpProjectRepository as HttpAdministrationProjectRepository } from '../../features/shared/project/infrastructure/http-project-repository' +import { HttpProjectRepository as HttpAdministrationProjectRepository } from '../../features/administration/features/project/infrastructure/http-project-repository' import { HttpAuthRepository } from '../../features/auth/infrastructure/http-auth-repository' import { HttpActivityRepository } from '../../features/binnacle/features/activity/infrastructure/http-activity-repository' import { HttpHolidayRepository } from '../../features/binnacle/features/holiday/infrastructure/http-holiday-repository' From e8edc86ce3a8f1483da5ca2a1d84e4ac40ef3e03 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 16:17:28 +0200 Subject: [PATCH 18/99] refactor: project repository unification (administration part) --- .../project/application/block-project-cmd.test.ts | 2 +- .../project/application/block-project-cmd.ts | 8 +++----- .../application/get-projects-list-qry.test.ts | 6 +++--- .../project/application/get-projects-list-qry.ts | 12 ++++++------ .../project/application/unblock-project-cmd.test.ts | 2 +- .../project/application/unblock-project-cmd.ts | 8 +++----- .../domain/services/projects-with-user-name.ts | 3 ++- .../project/ui/components/block-project-modal.tsx | 4 ++-- .../project/ui/components/projects-table.test.tsx | 12 +++++------- .../project/ui/components/projects-table.tsx | 4 ++-- .../project/ui/components/unblock-project-modal.tsx | 7 +++++-- .../features/project/ui/projects-page-utils.tsx | 3 ++- .../features/project/ui/projects-page.tsx | 2 +- .../project/domain/organization-filters.ts | 0 .../project/domain/project-code-error.ts | 0 .../project/domain/project-dto.ts | 2 +- .../project/domain/project-repository.ts | 2 +- .../features => shared}/project/domain/project.ts | 0 .../project/domain/services/project-error-message.ts | 4 ++-- .../project/domain/tests/project-mother.ts | 0 .../infrastructure/fake-project-repository.ts | 0 .../infrastructure/http-project-repository.test.ts | 4 ++-- .../infrastructure/http-project-repository.ts | 6 +++--- .../project/infrastructure/project-mapper.ts | 2 +- src/shared/di/container-tokens.ts | 1 - src/shared/di/container.ts | 5 +---- src/test-utils/di/integration-di.ts | 5 +---- src/test-utils/di/unit-di.ts | 6 +++--- 28 files changed, 51 insertions(+), 59 deletions(-) rename src/features/{administration/features => shared}/project/domain/organization-filters.ts (100%) rename src/features/{administration/features => shared}/project/domain/project-code-error.ts (100%) rename src/features/{administration/features => shared}/project/domain/project-dto.ts (78%) rename src/features/{administration/features => shared}/project/domain/project-repository.ts (85%) rename src/features/{administration/features => shared}/project/domain/project.ts (100%) rename src/features/{administration/features => shared}/project/domain/services/project-error-message.ts (83%) rename src/features/{administration/features => shared}/project/domain/tests/project-mother.ts (100%) rename src/features/{administration/features => shared}/project/infrastructure/fake-project-repository.ts (100%) rename src/features/{administration/features => shared}/project/infrastructure/http-project-repository.test.ts (92%) rename src/features/{administration/features => shared}/project/infrastructure/http-project-repository.ts (88%) rename src/features/{administration/features => shared}/project/infrastructure/project-mapper.ts (89%) diff --git a/src/features/administration/features/project/application/block-project-cmd.test.ts b/src/features/administration/features/project/application/block-project-cmd.test.ts index b3abf629..d1e157c3 100644 --- a/src/features/administration/features/project/application/block-project-cmd.test.ts +++ b/src/features/administration/features/project/application/block-project-cmd.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended' -import { ProjectRepository } from '../domain/project-repository' import { BlockProjectCmd } from './block-project-cmd' +import { ProjectRepository } from '../../../../shared/project/domain/project-repository' describe('BlockProjectCmd', () => { it('should block a project', async () => { diff --git a/src/features/administration/features/project/application/block-project-cmd.ts b/src/features/administration/features/project/application/block-project-cmd.ts index ec7b06e5..6fb1e27d 100644 --- a/src/features/administration/features/project/application/block-project-cmd.ts +++ b/src/features/administration/features/project/application/block-project-cmd.ts @@ -1,15 +1,13 @@ import { Command, UseCaseKey } from '@archimedes/arch' -import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' +import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { Id } from '../../../../../shared/types/id' import { inject, singleton } from 'tsyringe' -import type { ProjectRepository } from '../domain/project-repository' +import type { ProjectRepository } from '../../../../shared/project/domain/project-repository' @UseCaseKey('BlockProjectCmd') @singleton() export class BlockProjectCmd extends Command<{ projectId: Id; date: Date }> { - constructor( - @inject(ADMINISTRATION_PROJECT_REPOSITORY) private projectRepository: ProjectRepository - ) { + constructor(@inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository) { super() } diff --git a/src/features/administration/features/project/application/get-projects-list-qry.test.ts b/src/features/administration/features/project/application/get-projects-list-qry.test.ts index dcaac761..608397cc 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.test.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.test.ts @@ -1,9 +1,9 @@ import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { mock } from 'jest-mock-extended' -import { ProjectRepository } from '../domain/project-repository' -import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { GetProjectsListQry } from './get-projects-list-qry' -import { ProjectMother } from '../domain/tests/project-mother' +import { ProjectRepository } from '../../../../shared/project/domain/project-repository' +import { ProjectMother } from '../../../../shared/project/domain/tests/project-mother' +import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' describe('GetProjectsListQry', () => { it('should get the project list', async () => { diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-list-qry.ts index 6c393a98..dfb4dee3 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.ts @@ -1,19 +1,19 @@ import { InvalidateCache, Query, UseCaseKey } from '@archimedes/arch' import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' -import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' +import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' -import { OrganizationFilters } from '../domain/organization-filters' -import { Project } from '../domain/project' -import type { ProjectRepository } from '../domain/project-repository' -import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { Id } from '../../../../../shared/types/id' +import { Project } from '../../../../shared/project/domain/project' +import { OrganizationFilters } from '../../../../shared/project/domain/organization-filters' +import type { ProjectRepository } from '../../../../shared/project/domain/project-repository' +import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' @UseCaseKey('GetProjectsListQry') @InvalidateCache @singleton() export class GetProjectsListQry extends Query { constructor( - @inject(ADMINISTRATION_PROJECT_REPOSITORY) private projectRepository: ProjectRepository, + @inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository, private getUsersListQry: GetUsersListQry, private projectsWithUserName: ProjectsWithUserName ) { diff --git a/src/features/administration/features/project/application/unblock-project-cmd.test.ts b/src/features/administration/features/project/application/unblock-project-cmd.test.ts index a7e286d0..5cee99b6 100644 --- a/src/features/administration/features/project/application/unblock-project-cmd.test.ts +++ b/src/features/administration/features/project/application/unblock-project-cmd.test.ts @@ -1,6 +1,6 @@ import { mock } from 'jest-mock-extended' -import { ProjectRepository } from '../domain/project-repository' import { UnblockProjectCmd } from './unblock-project-cmd' +import { ProjectRepository } from '../../../../shared/project/domain/project-repository' describe('UnblockProjectCmd', () => { it('should unblock a project', async () => { diff --git a/src/features/administration/features/project/application/unblock-project-cmd.ts b/src/features/administration/features/project/application/unblock-project-cmd.ts index f0d326ee..560f3522 100644 --- a/src/features/administration/features/project/application/unblock-project-cmd.ts +++ b/src/features/administration/features/project/application/unblock-project-cmd.ts @@ -1,14 +1,12 @@ import { Command, Id, UseCaseKey } from '@archimedes/arch' -import { ADMINISTRATION_PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' +import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' -import type { ProjectRepository } from '../domain/project-repository' +import type { ProjectRepository } from '../../../../shared/project/domain/project-repository' @UseCaseKey('UnblockProjectCmd') @singleton() export class UnblockProjectCmd extends Command { - constructor( - @inject(ADMINISTRATION_PROJECT_REPOSITORY) private projectRepository: ProjectRepository - ) { + constructor(@inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository) { super() } diff --git a/src/features/administration/features/project/domain/services/projects-with-user-name.ts b/src/features/administration/features/project/domain/services/projects-with-user-name.ts index 2d5d7cd9..a4f9c0ed 100644 --- a/src/features/administration/features/project/domain/services/projects-with-user-name.ts +++ b/src/features/administration/features/project/domain/services/projects-with-user-name.ts @@ -1,7 +1,8 @@ import { singleton } from 'tsyringe' import { UserInfo } from '../../../../../shared/user/domain/user-info' -import { Project } from '../project' +import { Project } from '../../../../../shared/project/domain/project' +//TODO Renombrar correctamente @singleton() export class ProjectsWithUserName { addUserNameToProjects(projectsWithoutUserName: Project[], usersList: UserInfo[]): Project[] { diff --git a/src/features/administration/features/project/ui/components/block-project-modal.tsx b/src/features/administration/features/project/ui/components/block-project-modal.tsx index 8de58742..d0f8748a 100644 --- a/src/features/administration/features/project/ui/components/block-project-modal.tsx +++ b/src/features/administration/features/project/ui/components/block-project-modal.tsx @@ -19,10 +19,10 @@ import { useResolve } from '../../../../../../shared/di/use-resolve' import { DateField } from '../../../../../../shared/components/form-fields/date-field' import { chrono, parseISO } from '../../../../../../shared/utils/chrono' import { BlockProjectCmd } from '../../application/block-project-cmd' -import { Project } from '../../domain/project' -import { ProjectErrorMessage } from '../../domain/services/project-error-message' import { ProjectModalFormSchema, ProjectModalFormValidationSchema } from './project-modal.schema' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' +import { Project } from '../../../../../shared/project/domain/project' +import { ProjectErrorMessage } from '../../../../../shared/project/domain/services/project-error-message' type ProjectModalProps = { onClose(): void diff --git a/src/features/administration/features/project/ui/components/projects-table.test.tsx b/src/features/administration/features/project/ui/components/projects-table.test.tsx index 39205b9d..9de52b6e 100644 --- a/src/features/administration/features/project/ui/components/projects-table.test.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.test.tsx @@ -2,18 +2,18 @@ import userEvent from '@testing-library/user-event' import { OrganizationRepository } from '../../../../../binnacle/features/organization/domain/organization-repository' import { UserRepository } from '../../../../../shared/user/domain/user-repository' import { - ADMINISTRATION_PROJECT_REPOSITORY, ORGANIZATION_REPOSITORY, + PROJECT_REPOSITORY, USER_REPOSITORY } from '../../../../../../shared/di/container-tokens' import { OrganizationMother } from '../../../../../../test-utils/mothers/organization-mother' import { UserMother } from '../../../../../../test-utils/mothers/user-mother' import { container } from 'tsyringe' -import { ProjectRepository } from '../../domain/project-repository' -import { ProjectMother } from '../../domain/tests/project-mother' import { ProjectsTable } from './projects-table' -import { render, screen, waitFor, act } from '../../../../../../test-utils/render' +import { act, render, screen, waitFor } from '../../../../../../test-utils/render' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' +import { ProjectMother } from '../../../../../shared/project/domain/tests/project-mother' +import { ProjectRepository } from '../../../../../shared/project/domain/project-repository' jest.mock('../../../../../../shared/hooks/use-is-mobile') @@ -51,9 +51,7 @@ describe('ProjectsTable', () => { }) function setup() { - const projectRepository = container.resolve>( - ADMINISTRATION_PROJECT_REPOSITORY - ) + const projectRepository = container.resolve>(PROJECT_REPOSITORY) const userRepository = container.resolve>(USER_REPOSITORY) const organizationRepository = container.resolve>(ORGANIZATION_REPOSITORY) diff --git a/src/features/administration/features/project/ui/components/projects-table.tsx b/src/features/administration/features/project/ui/components/projects-table.tsx index d535ff48..81106e35 100644 --- a/src/features/administration/features/project/ui/components/projects-table.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.tsx @@ -9,13 +9,13 @@ import { ColumnsProps } from '../../../../../../shared/components/table/table.ty import { BlockProjectCmd } from '../../application/block-project-cmd' import { GetProjectsListQry } from '../../application/get-projects-list-qry' import { UnblockProjectCmd } from '../../application/unblock-project-cmd' -import { OrganizationFilters } from '../../domain/organization-filters' -import { Project } from '../../domain/project' import { ProjectStatus } from '../../domain/project-status' import { AdaptedProjects, adaptProjectsToTable } from '../projects-page-utils' import { ProjectsFilterFormCombos } from './combos/projects-combos' import { StatusBadge } from './status-badge' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' +import { Project } from '../../../../../shared/project/domain/project' +import { OrganizationFilters } from '../../../../../shared/project/domain/organization-filters' interface Props { onProjectClicked(project: Project): void diff --git a/src/features/administration/features/project/ui/components/unblock-project-modal.tsx b/src/features/administration/features/project/ui/components/unblock-project-modal.tsx index ac8f06c7..4fc18307 100644 --- a/src/features/administration/features/project/ui/components/unblock-project-modal.tsx +++ b/src/features/administration/features/project/ui/components/unblock-project-modal.tsx @@ -15,14 +15,17 @@ import { useGetUseCase } from '../../../../../../shared/arch/hooks/use-get-use-c import { useResolve } from '../../../../../../shared/di/use-resolve' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' import { UnblockProjectCmd } from '../../application/unblock-project-cmd' -import { Project } from '../../domain/project' -import { ProjectErrorMessage } from '../../domain/services/project-error-message' +import { Project } from '../../../../../shared/project/domain/project' +import { ProjectErrorMessage } from '../../../../../shared/project/domain/services/project-error-message' interface Props { project: Project + onCancel(): void + onClose(): void } + export const UnblockProjectModal: FC = (props) => { const { project, onClose, onCancel } = props const isMobile = useIsMobile() diff --git a/src/features/administration/features/project/ui/projects-page-utils.tsx b/src/features/administration/features/project/ui/projects-page-utils.tsx index 6cb8dc2e..55b05f19 100644 --- a/src/features/administration/features/project/ui/projects-page-utils.tsx +++ b/src/features/administration/features/project/ui/projects-page-utils.tsx @@ -1,5 +1,6 @@ -import { Project } from '../domain/project' import { chrono } from '../../../../../shared/utils/chrono' +import { Project } from '../../../../shared/project/domain/project' + export interface AdaptedProjects { key: number organization: string diff --git a/src/features/administration/features/project/ui/projects-page.tsx b/src/features/administration/features/project/ui/projects-page.tsx index df47dad5..8cf5ed0e 100644 --- a/src/features/administration/features/project/ui/projects-page.tsx +++ b/src/features/administration/features/project/ui/projects-page.tsx @@ -1,10 +1,10 @@ import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' -import { Project } from '../domain/project' import { BlockProjectModal } from './components/block-project-modal' import { ProjectsTable } from './components/projects-table' import { UnblockProjectModal } from './components/unblock-project-modal' +import { Project } from '../../../../shared/project/domain/project' const ProjectsPage: FC = () => { const { t } = useTranslation() diff --git a/src/features/administration/features/project/domain/organization-filters.ts b/src/features/shared/project/domain/organization-filters.ts similarity index 100% rename from src/features/administration/features/project/domain/organization-filters.ts rename to src/features/shared/project/domain/organization-filters.ts diff --git a/src/features/administration/features/project/domain/project-code-error.ts b/src/features/shared/project/domain/project-code-error.ts similarity index 100% rename from src/features/administration/features/project/domain/project-code-error.ts rename to src/features/shared/project/domain/project-code-error.ts diff --git a/src/features/administration/features/project/domain/project-dto.ts b/src/features/shared/project/domain/project-dto.ts similarity index 78% rename from src/features/administration/features/project/domain/project-dto.ts rename to src/features/shared/project/domain/project-dto.ts index 2f409acc..2a013346 100644 --- a/src/features/administration/features/project/domain/project-dto.ts +++ b/src/features/shared/project/domain/project-dto.ts @@ -1,4 +1,4 @@ -import { Id } from '../../../../../shared/types/id' +import { Id } from '../../../../shared/types/id' export interface ProjectDto { id: Id diff --git a/src/features/administration/features/project/domain/project-repository.ts b/src/features/shared/project/domain/project-repository.ts similarity index 85% rename from src/features/administration/features/project/domain/project-repository.ts rename to src/features/shared/project/domain/project-repository.ts index ad6046fb..1692c87e 100644 --- a/src/features/administration/features/project/domain/project-repository.ts +++ b/src/features/shared/project/domain/project-repository.ts @@ -1,6 +1,6 @@ -import { Id } from '../../../../../shared/types/id' import { OrganizationFilters } from './organization-filters' import { Project } from './project' +import { Id } from '../../../../shared/types/id' export interface ProjectRepository { getProjects(organizationStatus?: OrganizationFilters): Promise diff --git a/src/features/administration/features/project/domain/project.ts b/src/features/shared/project/domain/project.ts similarity index 100% rename from src/features/administration/features/project/domain/project.ts rename to src/features/shared/project/domain/project.ts diff --git a/src/features/administration/features/project/domain/services/project-error-message.ts b/src/features/shared/project/domain/services/project-error-message.ts similarity index 83% rename from src/features/administration/features/project/domain/services/project-error-message.ts rename to src/features/shared/project/domain/services/project-error-message.ts index 22ea5e6c..0ba67ea9 100644 --- a/src/features/administration/features/project/domain/services/project-error-message.ts +++ b/src/features/shared/project/domain/services/project-error-message.ts @@ -1,7 +1,7 @@ -import { i18n } from '../../../../../../shared/i18n/i18n' -import { NotificationMessage } from '../../../../../../shared/notification/notification-message' import { injectable } from 'tsyringe' import { ProjectCodeError } from '../project-code-error' +import { NotificationMessage } from '../../../../../shared/notification/notification-message' +import { i18n } from '../../../../../shared/i18n/i18n' type ProjectCodeError = keyof typeof ProjectCodeError diff --git a/src/features/administration/features/project/domain/tests/project-mother.ts b/src/features/shared/project/domain/tests/project-mother.ts similarity index 100% rename from src/features/administration/features/project/domain/tests/project-mother.ts rename to src/features/shared/project/domain/tests/project-mother.ts diff --git a/src/features/administration/features/project/infrastructure/fake-project-repository.ts b/src/features/shared/project/infrastructure/fake-project-repository.ts similarity index 100% rename from src/features/administration/features/project/infrastructure/fake-project-repository.ts rename to src/features/shared/project/infrastructure/fake-project-repository.ts diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.test.ts b/src/features/shared/project/infrastructure/http-project-repository.test.ts similarity index 92% rename from src/features/administration/features/project/infrastructure/http-project-repository.test.ts rename to src/features/shared/project/infrastructure/http-project-repository.test.ts index e46f9b8c..8bccee68 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.test.ts +++ b/src/features/shared/project/infrastructure/http-project-repository.test.ts @@ -1,8 +1,8 @@ import { mock } from 'jest-mock-extended' -import { HttpClient } from '../../../../../shared/http/http-client' -import { chrono } from '../../../../../shared/utils/chrono' import { ProjectMother } from '../domain/tests/project-mother' import { HttpProjectRepository } from './http-project-repository' +import { chrono } from '../../../../shared/utils/chrono' +import { HttpClient } from '../../../../shared/http/http-client' describe('HttpProjectRepository', () => { test('should get projects by organizationId', async () => { diff --git a/src/features/administration/features/project/infrastructure/http-project-repository.ts b/src/features/shared/project/infrastructure/http-project-repository.ts similarity index 88% rename from src/features/administration/features/project/infrastructure/http-project-repository.ts rename to src/features/shared/project/infrastructure/http-project-repository.ts index 0633dcb3..a94388ee 100644 --- a/src/features/administration/features/project/infrastructure/http-project-repository.ts +++ b/src/features/shared/project/infrastructure/http-project-repository.ts @@ -1,12 +1,12 @@ -import { HttpClient } from '../../../../../shared/http/http-client' import { singleton } from 'tsyringe' -import { Id } from '../../../../../shared/types/id' -import { chrono } from '../../../../../shared/utils/chrono' import { OrganizationFilters } from '../domain/organization-filters' import { Project } from '../domain/project' import { ProjectDto } from '../domain/project-dto' import { ProjectRepository } from '../domain/project-repository' import { ProjectMapper } from './project-mapper' +import { Id } from '../../../../shared/types/id' +import { HttpClient } from '../../../../shared/http/http-client' +import { chrono } from '../../../../shared/utils/chrono' @singleton() export class HttpProjectRepository implements ProjectRepository { diff --git a/src/features/administration/features/project/infrastructure/project-mapper.ts b/src/features/shared/project/infrastructure/project-mapper.ts similarity index 89% rename from src/features/administration/features/project/infrastructure/project-mapper.ts rename to src/features/shared/project/infrastructure/project-mapper.ts index aad3629d..be2c0948 100644 --- a/src/features/administration/features/project/infrastructure/project-mapper.ts +++ b/src/features/shared/project/infrastructure/project-mapper.ts @@ -1,6 +1,6 @@ -import { parseISO } from '../../../../../shared/utils/chrono' import { ProjectDto } from '../domain/project-dto' import { Project } from '../domain/project' +import { parseISO } from '../../../../shared/utils/chrono' export class ProjectMapper { static toDomain(projectDto: ProjectDto): Project { diff --git a/src/shared/di/container-tokens.ts b/src/shared/di/container-tokens.ts index 63df0cae..f5d6c21d 100644 --- a/src/shared/di/container-tokens.ts +++ b/src/shared/di/container-tokens.ts @@ -11,4 +11,3 @@ export const VERSION_REPOSITORY = Symbol('VERSION_REPOSITORY') export const ORGANIZATION_REPOSITORY = Symbol('ORGANIZATION_REPOSITORY') export const PROJECT_REPOSITORY = Symbol('PROJECT_REPOSITORY') export const PROJECT_ROLE_REPOSITORY = Symbol('PROJECT_ROLE_REPOSITORY') -export const ADMINISTRATION_PROJECT_REPOSITORY = Symbol('ADMINISTRATION_PROJECT_REPOSITORY') diff --git a/src/shared/di/container.ts b/src/shared/di/container.ts index 46fd26f4..4ddaa26a 100644 --- a/src/shared/di/container.ts +++ b/src/shared/di/container.ts @@ -1,10 +1,8 @@ -import { HttpProjectRepository as HttpAdministrationProjectRepository } from '../../features/administration/features/project/infrastructure/http-project-repository' import { HttpAuthRepository } from '../../features/auth/infrastructure/http-auth-repository' import { HttpActivityRepository } from '../../features/binnacle/features/activity/infrastructure/http-activity-repository' import { HttpHolidayRepository } from '../../features/binnacle/features/holiday/infrastructure/http-holiday-repository' import { HttpOrganizationRepository } from '../../features/binnacle/features/organization/infrastructure/http-organization-repository' import { HttpProjectRoleRepository } from '../../features/binnacle/features/project-role/infrastructure/http-project-role-repository' -import { HttpProjectRepository } from '../../features/binnacle/features/project/infrastructure/http-project-repository' import { HttpSearchRepository } from '../../features/binnacle/features/search/infrastructure/http-search-repository' import { HttpVacationRepository } from '../../features/binnacle/features/vacation/infrastructure/http-vacation-repository' import { LocalStorageUserSettingsRepository } from '../../features/shared/user/features/settings/infrastructure/local-storage-user-settings-repository' @@ -13,7 +11,6 @@ import { HttpVersionRepository } from '../../features/version/infrastructure/htt import { container } from 'tsyringe' import { ACTIVITY_REPOSITORY, - ADMINISTRATION_PROJECT_REPOSITORY, AUTH_REPOSITORY, HOLIDAY_REPOSITORY, ORGANIZATION_REPOSITORY, @@ -28,6 +25,7 @@ import { VERSION_REPOSITORY } from './container-tokens' import { toast, ToastType } from '../notification/toast' +import { HttpProjectRepository } from '../../features/shared/project/infrastructure/http-project-repository' container.register(STORAGE, { useValue: localStorage }) container.register(TOAST, { useValue: toast }) @@ -42,4 +40,3 @@ container.registerSingleton(PROJECT_ROLE_REPOSITORY, HttpProjectRoleRepository) container.registerSingleton(PROJECT_REPOSITORY, HttpProjectRepository) container.registerSingleton(ORGANIZATION_REPOSITORY, HttpOrganizationRepository) container.registerSingleton(ACTIVITY_REPOSITORY, HttpActivityRepository) -container.registerSingleton(ADMINISTRATION_PROJECT_REPOSITORY, HttpAdministrationProjectRepository) diff --git a/src/test-utils/di/integration-di.ts b/src/test-utils/di/integration-di.ts index 9b6abe8c..1a160af4 100644 --- a/src/test-utils/di/integration-di.ts +++ b/src/test-utils/di/integration-di.ts @@ -2,7 +2,6 @@ import 'reflect-metadata' import { container } from 'tsyringe' import { ACTIVITY_REPOSITORY, - ADMINISTRATION_PROJECT_REPOSITORY, AUTH_REPOSITORY, HOLIDAY_REPOSITORY, ORGANIZATION_REPOSITORY, @@ -23,12 +22,11 @@ import { FakeVacationRepository } from '../../features/binnacle/features/vacatio import { FakeHolidayRepository } from '../../features/binnacle/features/holiday/infrastructure/fake-holiday-repository' import { FakeSearchRepository } from '../../features/binnacle/features/search/infrastructure/fake-search-repository' import { FakeProjectRoleRepository } from '../../features/binnacle/features/project-role/infrastructure/fake-project-role-repository' -import { FakeProjectRepository } from '../../features/binnacle/features/project/infrastructure/fake-project-repository' import { FakeOrganizationRepository } from '../../features/binnacle/features/organization/infrastructure/fake-organization-repository' import { FakeActivityRepository } from '../../features/binnacle/features/activity/infrastructure/fake-activity-repository' -import { FakeProjectRepository as FakeProjectRepositoryAdministration } from '../../features/administration/features/project/infrastructure/fake-project-repository' import { toast, ToastType } from '../../shared/notification/toast' import { FakeUserSettingsRepository } from '../../features/shared/user/features/settings/infrastructure/fake-user-settings-repository' +import { FakeProjectRepository } from '../../features/shared/project/infrastructure/fake-project-repository' container.register(STORAGE, { useValue: localStorage }) container.register(TOAST, { useValue: toast }) @@ -43,4 +41,3 @@ container.registerSingleton(PROJECT_ROLE_REPOSITORY, FakeProjectRoleRepository) container.registerSingleton(PROJECT_REPOSITORY, FakeProjectRepository) container.registerSingleton(ORGANIZATION_REPOSITORY, FakeOrganizationRepository) container.registerSingleton(ACTIVITY_REPOSITORY, FakeActivityRepository) -container.registerSingleton(ADMINISTRATION_PROJECT_REPOSITORY, FakeProjectRepositoryAdministration) diff --git a/src/test-utils/di/unit-di.ts b/src/test-utils/di/unit-di.ts index 18d4db1e..9de299be 100644 --- a/src/test-utils/di/unit-di.ts +++ b/src/test-utils/di/unit-di.ts @@ -1,5 +1,4 @@ import 'reflect-metadata' -import { ProjectRepository } from '../../features/administration/features/project/domain/project-repository' import { AuthRepository } from '../../features/auth/domain/auth-repository' import { ActivityRepository } from '../../features/binnacle/features/activity/domain/activity-repository' import { OrganizationRepository } from '../../features/binnacle/features/organization/domain/organization-repository' @@ -9,19 +8,20 @@ import { mock } from 'jest-mock-extended' import { container } from 'tsyringe' import { ACTIVITY_REPOSITORY, - ADMINISTRATION_PROJECT_REPOSITORY, AUTH_REPOSITORY, ORGANIZATION_REPOSITORY, + PROJECT_REPOSITORY, TOAST, USER_REPOSITORY, USER_SETTINGS_REPOSITORY } from '../../shared/di/container-tokens' import { toast, ToastType } from '../../shared/notification/toast' +import { ProjectRepository } from '../../features/shared/project/domain/project-repository' container.register(TOAST, { useValue: toast }) container.register(USER_SETTINGS_REPOSITORY, { useValue: mock() }) container.register(AUTH_REPOSITORY, { useValue: mock() }) container.register(USER_REPOSITORY, { useValue: mock() }) container.register(ORGANIZATION_REPOSITORY, { useValue: mock() }) -container.register(ADMINISTRATION_PROJECT_REPOSITORY, { useValue: mock() }) +container.register(PROJECT_REPOSITORY, { useValue: mock() }) container.register(ACTIVITY_REPOSITORY, { useValue: mock() }) From 14d7d654848f5460bd44031762b8a26518adcf9a Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 16:48:59 +0200 Subject: [PATCH 19/99] refactor: project repository unification (binnacle part), substitution of binnacle project --- .../activity-form/activity-form.schema.ts | 6 +++--- .../components/combos/project-roles-combo.tsx | 2 +- .../components/combos/projects-combo.tsx | 6 +++--- .../domain/non-hydrated-project-role.ts | 2 +- .../project/application/get-projects-qry.ts | 17 ----------------- .../binnacle}/get-projects-qry.test.ts | 0 .../application/binnacle/get-projects-qry.ts | 18 ++++++++++++++++++ src/features/shared/project/domain/project.ts | 2 +- 8 files changed, 27 insertions(+), 26 deletions(-) delete mode 100644 src/features/binnacle/features/project/application/get-projects-qry.ts rename src/features/{binnacle/features/project/application => shared/project/application/binnacle}/get-projects-qry.test.ts (100%) create mode 100644 src/features/shared/project/application/binnacle/get-projects-qry.ts diff --git a/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.schema.ts b/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.schema.ts index 709b33b4..85805a43 100644 --- a/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.schema.ts +++ b/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.schema.ts @@ -1,10 +1,10 @@ import { Organization } from '../../../../organization/domain/organization' import { NonHydratedProjectRole } from '../../../../project-role/domain/non-hydrated-project-role' import { ProjectRole } from '../../../../project-role/domain/project-role' -import { Project } from '../../../../project/domain/project' import { i18n } from '../../../../../../../shared/i18n/i18n' import { chrono, parse } from '../../../../../../../shared/utils/chrono' import * as yup from 'yup' +import { Project } from '../../../../../../shared/project/domain/project' export interface ActivityFormSchema { showRecentRole: boolean @@ -15,7 +15,7 @@ export interface ActivityFormSchema { billable: boolean description: string organization?: Organization - project?: Project & { organizationId: number } + project?: Project projectRole?: NonHydratedProjectRole recentProjectRole?: ProjectRole file?: File @@ -68,7 +68,7 @@ export const ActivityFormValidationSchema: yup.ObjectSchema is: true, then: (schema) => schema.nullable(), otherwise: (schema) => schema.required(i18n.t('form_errors.select_an_option')) - }) as yup.ObjectSchema, + }) as yup.ObjectSchema, projectRole: yup.object().when('showRecentRole', { is: true, then: (schema) => schema.nullable(), diff --git a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/project-roles-combo.tsx b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/project-roles-combo.tsx index 015fa19f..7db2fbac 100644 --- a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/project-roles-combo.tsx +++ b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/project-roles-combo.tsx @@ -7,8 +7,8 @@ import { Id } from '../../../../../../../../../shared/types/id' import { GetProjectRolesQry } from '../../../../../../project-role/application/get-project-roles-qry' import { NonHydratedProjectRole } from '../../../../../../project-role/domain/non-hydrated-project-role' import { ProjectRole } from '../../../../../../project-role/domain/project-role' -import { Project } from '../../../../../../project/domain/project' import { useCalendarContext } from '../../../../contexts/calendar-context' +import { Project } from '../../../../../../../../shared/project/domain/project' interface ComboProps { name?: string diff --git a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/projects-combo.tsx b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/projects-combo.tsx index 8bc51201..54bf1a9d 100644 --- a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/projects-combo.tsx +++ b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/projects-combo.tsx @@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next' import { useGetUseCase } from '../../../../../../../../../shared/arch/hooks/use-get-use-case' import { ComboField } from '../../../../../../../../../shared/components/form-fields/combo-field' import { Organization } from '../../../../../../organization/domain/organization' -import { GetProjectsQry } from '../../../../../../project/application/get-projects-qry' -import { Project } from '../../../../../../project/domain/project' +import { Project } from '../../../../../../../../shared/project/domain/project' +import { GetProjectsQry } from '../../../../../../../../shared/project/application/binnacle/get-projects-qry' interface ComboProps { name?: string @@ -28,7 +28,7 @@ export const ProjectsCombo = forwardRef((props, re return } - executeUseCase(organization.id).then((projects) => { + executeUseCase({ organizationIds: [organization.id], open: true }).then((projects) => { setItems(projects) }) }, [executeUseCase, organization]) diff --git a/src/features/binnacle/features/project-role/domain/non-hydrated-project-role.ts b/src/features/binnacle/features/project-role/domain/non-hydrated-project-role.ts index c2a11822..615e1737 100644 --- a/src/features/binnacle/features/project-role/domain/non-hydrated-project-role.ts +++ b/src/features/binnacle/features/project-role/domain/non-hydrated-project-role.ts @@ -1,7 +1,7 @@ import { Id } from '../../../../../shared/types/id' import { Organization } from '../../organization/domain/organization' -import { Project } from '../../project/domain/project' import { ProjectRole } from './project-role' +import { Project } from '../../../../shared/project/domain/project' export type NonHydratedProjectRole = Omit & { organizationId: Organization['id'] diff --git a/src/features/binnacle/features/project/application/get-projects-qry.ts b/src/features/binnacle/features/project/application/get-projects-qry.ts deleted file mode 100644 index aa0595dd..00000000 --- a/src/features/binnacle/features/project/application/get-projects-qry.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Id, Query, UseCaseKey } from '@archimedes/arch' -import { inject, singleton } from 'tsyringe' -import { Project } from '../domain/project' -import type { ProjectRepository } from '../domain/project-repository' -import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' - -@UseCaseKey('GetProjectsQry') -@singleton() -export class GetProjectsQry extends Query { - constructor(@inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository) { - super() - } - - internalExecute(organizationId: Id): Promise { - return this.projectRepository.getAll(organizationId) - } -} diff --git a/src/features/binnacle/features/project/application/get-projects-qry.test.ts b/src/features/shared/project/application/binnacle/get-projects-qry.test.ts similarity index 100% rename from src/features/binnacle/features/project/application/get-projects-qry.test.ts rename to src/features/shared/project/application/binnacle/get-projects-qry.test.ts diff --git a/src/features/shared/project/application/binnacle/get-projects-qry.ts b/src/features/shared/project/application/binnacle/get-projects-qry.ts new file mode 100644 index 00000000..0c31bf2d --- /dev/null +++ b/src/features/shared/project/application/binnacle/get-projects-qry.ts @@ -0,0 +1,18 @@ +import { Query, UseCaseKey } from '@archimedes/arch' +import { inject, singleton } from 'tsyringe' +import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' +import { Project } from '../../domain/project' +import type { ProjectRepository } from '../../domain/project-repository' +import { OrganizationFilters } from '../../domain/organization-filters' + +@UseCaseKey('GetProjectsQry') +@singleton() +export class GetProjectsQry extends Query { + constructor(@inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository) { + super() + } + + internalExecute(organizationStatus?: OrganizationFilters): Promise { + return this.projectRepository.getProjects(organizationStatus) + } +} diff --git a/src/features/shared/project/domain/project.ts b/src/features/shared/project/domain/project.ts index 2f41ff04..29214f6d 100644 --- a/src/features/shared/project/domain/project.ts +++ b/src/features/shared/project/domain/project.ts @@ -1,4 +1,4 @@ -import { Id } from '../../../../../shared/types/id' +import { Id } from '../../../../shared/types/id' export interface Project { id: Id From 480500b960daedf924e747bab56504d2eb1129eb Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 17:31:49 +0200 Subject: [PATCH 20/99] refactor: remove project from binnacle part and update mother files --- .../services/generate-year-balance.test.ts | 4 +-- .../activity-form/activity-form.test.tsx | 4 +-- .../features/project/domain/lite-project.ts | 2 +- .../project/domain/project-repository.ts | 6 ---- .../features/project/domain/project.ts | 8 ----- .../infrastructure/fake-project-repository.ts | 9 ----- .../http-project-repository.test.ts | 27 --------------- .../infrastructure/http-project-repository.ts | 17 ---------- .../binnacle/get-projects-qry.test.ts | 15 +++++---- .../project/domain/tests/project-mother.ts | 28 +++++++++++++++- src/test-utils/mothers/activity-mother.ts | 8 ++--- ...oject-mother.ts => lite-project-mother.ts} | 33 +++++-------------- src/test-utils/mothers/project-role-mother.ts | 14 ++++---- src/test-utils/mothers/search-mother.ts | 6 ++-- 14 files changed, 63 insertions(+), 118 deletions(-) delete mode 100644 src/features/binnacle/features/project/domain/project-repository.ts delete mode 100644 src/features/binnacle/features/project/domain/project.ts delete mode 100644 src/features/binnacle/features/project/infrastructure/fake-project-repository.ts delete mode 100644 src/features/binnacle/features/project/infrastructure/http-project-repository.test.ts delete mode 100644 src/features/binnacle/features/project/infrastructure/http-project-repository.ts rename src/test-utils/mothers/{project-mother.ts => lite-project-mother.ts} (62%) diff --git a/src/features/binnacle/features/activity/domain/services/generate-year-balance.test.ts b/src/features/binnacle/features/activity/domain/services/generate-year-balance.test.ts index 39b4c092..e328df7e 100644 --- a/src/features/binnacle/features/activity/domain/services/generate-year-balance.test.ts +++ b/src/features/binnacle/features/activity/domain/services/generate-year-balance.test.ts @@ -1,4 +1,4 @@ -import { ProjectMother } from '../../../../../../test-utils/mothers/project-mother' +import { LiteProjectMother } from '../../../../../../test-utils/mothers/lite-project-mother' import { OrganizationMother } from '../../../../../../test-utils/mothers/organization-mother' import { ProjectRoleMother } from '../../../../../../test-utils/mothers/project-role-mother' import { SearchMother } from '../../../../../../test-utils/mothers/search-mother' @@ -177,7 +177,7 @@ describe('GenerateYearBalance', () => { it('should generate the balance role list when', async () => { const { generateYearBalance } = setup() const organization = OrganizationMother.organization() - const project = ProjectMother.billableLiteProjectWithOrganizationId() + const project = LiteProjectMother.billableLiteProjectWithOrganizationId() const projectRole = ProjectRoleMother.liteProjectRoleInMinutes() const timeSummaryWithRoles = ActivityMother.timeSummary() const searchRolesResponseWithRole = SearchMother.customRoles({ diff --git a/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.test.tsx b/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.test.tsx index 13c91c9e..69d6f32a 100644 --- a/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.test.tsx +++ b/src/features/binnacle/features/activity/ui/components/activity-form/activity-form.test.tsx @@ -9,7 +9,7 @@ import { waitForElementToBeRemoved } from '../../../../../../../test-utils/render' import { OrganizationMother } from '../../../../../../../test-utils/mothers/organization-mother' -import { ProjectMother } from '../../../../../../../test-utils/mothers/project-mother' +import { LiteProjectMother } from '../../../../../../../test-utils/mothers/lite-project-mother' import { ProjectRoleMother } from '../../../../../../../test-utils/mothers/project-role-mother' import { useExecuteUseCaseOnMount } from '../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' @@ -566,7 +566,7 @@ function setup( if (arg.prototype.key === 'GetProjectsQry') { return { isLoading: false, - executeUseCase: jest.fn().mockResolvedValue(ProjectMother.projects()) + executeUseCase: jest.fn().mockResolvedValue(LiteProjectMother.projects()) } } if (arg.prototype.key === 'GetProjectRolesQry') { diff --git a/src/features/binnacle/features/project/domain/lite-project.ts b/src/features/binnacle/features/project/domain/lite-project.ts index a80a5935..1730a810 100644 --- a/src/features/binnacle/features/project/domain/lite-project.ts +++ b/src/features/binnacle/features/project/domain/lite-project.ts @@ -1,3 +1,3 @@ -import { Project } from './project' +import { Project } from '../../../../shared/project/domain/project' export type LiteProject = Pick diff --git a/src/features/binnacle/features/project/domain/project-repository.ts b/src/features/binnacle/features/project/domain/project-repository.ts deleted file mode 100644 index b5ea9e02..00000000 --- a/src/features/binnacle/features/project/domain/project-repository.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Id } from '../../../../../shared/types/id' -import { Project } from './project' - -export interface ProjectRepository { - getAll(organizationId: Id): Promise -} diff --git a/src/features/binnacle/features/project/domain/project.ts b/src/features/binnacle/features/project/domain/project.ts deleted file mode 100644 index d931be30..00000000 --- a/src/features/binnacle/features/project/domain/project.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Id } from '../../../../../shared/types/id' - -export interface Project { - id: Id - name: string - billable: boolean - open: boolean -} diff --git a/src/features/binnacle/features/project/infrastructure/fake-project-repository.ts b/src/features/binnacle/features/project/infrastructure/fake-project-repository.ts deleted file mode 100644 index e95cd0d8..00000000 --- a/src/features/binnacle/features/project/infrastructure/fake-project-repository.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ProjectMother } from '../../../../../test-utils/mothers/project-mother' -import { Project } from '../domain/project' -import { ProjectRepository } from '../domain/project-repository' - -export class FakeProjectRepository implements ProjectRepository { - async getAll(): Promise { - return ProjectMother.projects() - } -} diff --git a/src/features/binnacle/features/project/infrastructure/http-project-repository.test.ts b/src/features/binnacle/features/project/infrastructure/http-project-repository.test.ts deleted file mode 100644 index 7333ad24..00000000 --- a/src/features/binnacle/features/project/infrastructure/http-project-repository.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { mock } from 'jest-mock-extended' -import { HttpClient } from '../../../../../shared/http/http-client' -import { HttpProjectRepository } from './http-project-repository' -import { ProjectMother } from '../../../../../test-utils/mothers/project-mother' -import { OrganizationMother } from '../../../../../test-utils/mothers/organization-mother' - -describe('HttpProjectRepository', () => { - it('should call http client for projects', async () => { - const { httpClient, httpProjectRepository } = setup() - const organizations = ProjectMother.projects() - httpClient.get.mockResolvedValue(ProjectMother.projects()) - - const result = await httpProjectRepository.getAll(OrganizationMother.organization().id) - - expect(httpClient.get).toHaveBeenCalled() - expect(result).toEqual(organizations) - }) -}) - -function setup() { - const httpClient = mock() - - return { - httpClient, - httpProjectRepository: new HttpProjectRepository(httpClient) - } -} diff --git a/src/features/binnacle/features/project/infrastructure/http-project-repository.ts b/src/features/binnacle/features/project/infrastructure/http-project-repository.ts deleted file mode 100644 index a95c43b4..00000000 --- a/src/features/binnacle/features/project/infrastructure/http-project-repository.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { HttpClient } from '../../../../../shared/http/http-client' -import { Id } from '../../../../../shared/types/id' -import { singleton } from 'tsyringe' -import { Project } from '../domain/project' -import { ProjectRepository } from '../domain/project-repository' - -@singleton() -export class HttpProjectRepository implements ProjectRepository { - protected static projectPath = (organizationId: Id) => - `/api/organizations/${organizationId}/projects` - - constructor(private httpClient: HttpClient) {} - - getAll(organizationId: Id): Promise { - return this.httpClient.get(HttpProjectRepository.projectPath(organizationId)) - } -} diff --git a/src/features/shared/project/application/binnacle/get-projects-qry.test.ts b/src/features/shared/project/application/binnacle/get-projects-qry.test.ts index 99150da1..1cf54042 100644 --- a/src/features/shared/project/application/binnacle/get-projects-qry.test.ts +++ b/src/features/shared/project/application/binnacle/get-projects-qry.test.ts @@ -1,18 +1,21 @@ import { mock } from 'jest-mock-extended' import { GetProjectsQry } from './get-projects-qry' -import { ProjectRepository } from '../domain/project-repository' -import { ProjectMother } from '../../../../../test-utils/mothers/project-mother' +import { LiteProjectMother } from '../../../../../test-utils/mothers/lite-project-mother' import { OrganizationMother } from '../../../../../test-utils/mothers/organization-mother' +import { ProjectRepository } from '../../domain/project-repository' describe('GetProjectsQry', () => { it('should get projects from repository', async () => { const { getProjectQry, projectRepository } = setup() - const projects = ProjectMother.projects() - projectRepository.getAll.mockResolvedValue(projects) + const projects = LiteProjectMother.projects() + projectRepository.getProjects.mockResolvedValue(projects) - const response = await getProjectQry.internalExecute(OrganizationMother.organization().id) + const response = await getProjectQry.internalExecute({ + organizationIds: [OrganizationMother.organization().id], + open: true + }) - expect(projectRepository.getAll).toHaveBeenCalled() + expect(projectRepository.getProjects).toHaveBeenCalled() expect(response).toEqual(projects) }) }) diff --git a/src/features/shared/project/domain/tests/project-mother.ts b/src/features/shared/project/domain/tests/project-mother.ts index c2aee2e6..aa40b36f 100644 --- a/src/features/shared/project/domain/tests/project-mother.ts +++ b/src/features/shared/project/domain/tests/project-mother.ts @@ -1,8 +1,34 @@ -import { parseISO } from '../../../../../../shared/utils/chrono' import { ProjectDto } from '../project-dto' import { Project } from '../project' +import { parseISO } from '../../../../../shared/utils/chrono' export class ProjectMother { + static billableProject(): Project { + return { + id: 2, + name: 'Billable project', + billable: true, + open: true, + startDate: parseISO('2023-01-01'), + blockDate: parseISO('2023-06-01'), + blockedByUser: null, + organizationId: 1 + } + } + + static notBillableProject(): Project { + return { + id: 1, + name: 'No billable project', + billable: false, + open: true, + startDate: parseISO('2023-01-01'), + blockDate: parseISO('2023-06-01'), + blockedByUser: null, + organizationId: 1 + } + } + static projectsFilteredByOrganization(): ProjectDto[] { return [ { diff --git a/src/test-utils/mothers/activity-mother.ts b/src/test-utils/mothers/activity-mother.ts index fc0b7d3c..abb46b17 100644 --- a/src/test-utils/mothers/activity-mother.ts +++ b/src/test-utils/mothers/activity-mother.ts @@ -13,7 +13,7 @@ import { } from '../../features/binnacle/features/activity/domain/year-balance' import { Serialized } from '../../shared/types/serialized' import { OrganizationMother } from './organization-mother' -import { ProjectMother } from './project-mother' +import { LiteProjectMother } from './lite-project-mother' import { ProjectRoleMother } from './project-role-mother' export class ActivityMother { @@ -91,7 +91,7 @@ export class ActivityMother { billable: true, hasEvidences: false, organization: OrganizationMother.organization(), - project: ProjectMother.billableLiteProjectWithOrganizationId(), + project: LiteProjectMother.billableLiteProjectWithOrganizationId(), projectRole: ProjectRoleMother.liteProjectRoleInMinutes(), approval: { canBeApproved: true, @@ -178,7 +178,7 @@ export class ActivityMother { billable: false, hasEvidences: true, organization: OrganizationMother.organization(), - project: ProjectMother.billableLiteProjectWithOrganizationId(), + project: LiteProjectMother.billableLiteProjectWithOrganizationId(), projectRole: ProjectRoleMother.liteProjectRoleInDaysRequireApproval(), approval: { canBeApproved: true, @@ -204,7 +204,7 @@ export class ActivityMother { billable: false, hasEvidences: false, organization: OrganizationMother.organization(), - project: ProjectMother.billableLiteProjectWithOrganizationId(), + project: LiteProjectMother.billableLiteProjectWithOrganizationId(), projectRole: ProjectRoleMother.liteProjectRoleInDaysRequireApproval(), approval: { canBeApproved: false, diff --git a/src/test-utils/mothers/project-mother.ts b/src/test-utils/mothers/lite-project-mother.ts similarity index 62% rename from src/test-utils/mothers/project-mother.ts rename to src/test-utils/mothers/lite-project-mother.ts index 599697fd..02f0f838 100644 --- a/src/test-utils/mothers/project-mother.ts +++ b/src/test-utils/mothers/lite-project-mother.ts @@ -1,11 +1,12 @@ import { LiteProject } from '../../features/binnacle/features/project/domain/lite-project' -import { Project } from '../../features/binnacle/features/project/domain/project' import { LiteProjectWithOrganizationId } from '../../features/binnacle/features/search/domain/lite-project-with-organization-id' import { OrganizationMother } from './organization-mother' +import { ProjectMother } from '../../features/shared/project/domain/tests/project-mother' +import { Project } from '../../features/shared/project/domain/project' -export class ProjectMother { +export class LiteProjectMother { static projects(): Project[] { - return [this.notBillableProject(), this.billableProject()] + return [ProjectMother.notBillableProject(), ProjectMother.billableProject()] } static liteProjectsWithOrganizationId(): LiteProjectWithOrganizationId[] { @@ -15,17 +16,8 @@ export class ProjectMother { ] } - static notBillableProject(): Project { - return { - id: 1, - name: 'No billable project', - billable: false, - open: true - } - } - static notBillableLiteProjectWithOrganizationId(): LiteProjectWithOrganizationId { - const { id, name } = this.notBillableProject() + const { id, name } = ProjectMother.notBillableProject() return { id, @@ -36,7 +28,7 @@ export class ProjectMother { } static notBillableLiteProject(): LiteProject { - const { id, name, billable } = this.notBillableProject() + const { id, name, billable } = ProjectMother.notBillableProject() return { id, @@ -45,17 +37,8 @@ export class ProjectMother { } } - static billableProject(): Project { - return { - id: 2, - name: 'Billable project', - billable: true, - open: true - } - } - static billableLiteProject(): LiteProject { - const { id, name, billable } = this.billableProject() + const { id, name, billable } = ProjectMother.billableProject() return { id, @@ -65,7 +48,7 @@ export class ProjectMother { } static billableLiteProjectWithOrganizationId(): LiteProjectWithOrganizationId { - const { id, name } = this.billableProject() + const { id, name } = ProjectMother.billableProject() return { billable: false, diff --git a/src/test-utils/mothers/project-role-mother.ts b/src/test-utils/mothers/project-role-mother.ts index abe57169..0dd940bc 100644 --- a/src/test-utils/mothers/project-role-mother.ts +++ b/src/test-utils/mothers/project-role-mother.ts @@ -3,7 +3,7 @@ import { ProjectRole } from '../../features/binnacle/features/project-role/domai import { LiteProjectRoleWithProjectId } from '../../features/binnacle/features/search/domain/lite-project-role-with-project-id' import { TimeUnits } from '../../shared/types/time-unit' import { OrganizationMother } from './organization-mother' -import { ProjectMother } from './project-mother' +import { LiteProjectMother } from './lite-project-mother' export class ProjectRoleMother { static projectRoles(): ProjectRole[] { @@ -35,7 +35,7 @@ export class ProjectRoleMother { id: 1, name: 'Project in minutes', organization: OrganizationMother.organization(), - project: ProjectMother.billableLiteProject(), + project: LiteProjectMother.billableLiteProject(), userId: 1, requireEvidence: 'NO', requireApproval: false, @@ -55,7 +55,7 @@ export class ProjectRoleMother { id: 2, name: 'Project in days', organization: OrganizationMother.organization(), - project: ProjectMother.notBillableLiteProject(), + project: LiteProjectMother.notBillableLiteProject(), userId: 1, requireEvidence: 'NO', requireApproval: false, @@ -75,7 +75,7 @@ export class ProjectRoleMother { id: 3, name: 'Project in days 2', organization: OrganizationMother.organization(), - project: ProjectMother.notBillableLiteProject(), + project: LiteProjectMother.notBillableLiteProject(), userId: 1, requireEvidence: 'NO', requireApproval: true, @@ -93,21 +93,21 @@ export class ProjectRoleMother { static liteProjectRoleInDays(): LiteProjectRoleWithProjectId { return { ...this.projectRoleInDays(), - projectId: ProjectMother.notBillableProject().id + projectId: LiteProjectMother.notBillableProject().id } } static liteProjectRoleInDaysRequireApproval(): LiteProjectRoleWithProjectId { return { ...this.projectRoleInDaysRequireApproval(), - projectId: ProjectMother.notBillableProject().id + projectId: LiteProjectMother.notBillableProject().id } } static liteProjectRoleInMinutes(): LiteProjectRoleWithProjectId { return { ...this.projectRoleInMinutes(), - projectId: ProjectMother.billableProject().id + projectId: LiteProjectMother.billableProject().id } } diff --git a/src/test-utils/mothers/search-mother.ts b/src/test-utils/mothers/search-mother.ts index 5d47e8dd..14fdc2ae 100644 --- a/src/test-utils/mothers/search-mother.ts +++ b/src/test-utils/mothers/search-mother.ts @@ -1,13 +1,13 @@ import { SearchProjectRolesResult } from '../../features/binnacle/features/search/domain/search-project-roles-result' import { OrganizationMother } from './organization-mother' -import { ProjectMother } from './project-mother' +import { LiteProjectMother } from './lite-project-mother' import { ProjectRoleMother } from './project-role-mother' export class SearchMother { static roles(): SearchProjectRolesResult { return { organizations: OrganizationMother.organizations(), - projects: ProjectMother.liteProjectsWithOrganizationId(), + projects: LiteProjectMother.liteProjectsWithOrganizationId(), projectRoles: ProjectRoleMother.liteProjectRoles() } } @@ -15,7 +15,7 @@ export class SearchMother { static customRoles(override?: Partial): SearchProjectRolesResult { return { organizations: OrganizationMother.organizations(), - projects: ProjectMother.liteProjectsWithOrganizationId(), + projects: LiteProjectMother.liteProjectsWithOrganizationId(), projectRoles: ProjectRoleMother.liteProjectRoles(), ...override } From d862354c62f37087cbdf16d0c4b7a06c798f6017 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 17:49:44 +0200 Subject: [PATCH 21/99] refactor: rename function name --- .../project/application/get-projects-list-qry.ts | 11 +++++------ .../domain/services/projects-with-user-name.ts | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-list-qry.ts index dfb4dee3..e6db8c46 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.ts @@ -1,19 +1,18 @@ import { InvalidateCache, Query, UseCaseKey } from '@archimedes/arch' import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' -import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' -import { inject, singleton } from 'tsyringe' +import { singleton } from 'tsyringe' import { Id } from '../../../../../shared/types/id' import { Project } from '../../../../shared/project/domain/project' import { OrganizationFilters } from '../../../../shared/project/domain/organization-filters' -import type { ProjectRepository } from '../../../../shared/project/domain/project-repository' import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' +import { GetProjectsQry } from '../../../../shared/project/application/binnacle/get-projects-qry' @UseCaseKey('GetProjectsListQry') @InvalidateCache @singleton() export class GetProjectsListQry extends Query { constructor( - @inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository, + private getProjectsQry: GetProjectsQry, private getUsersListQry: GetUsersListQry, private projectsWithUserName: ProjectsWithUserName ) { @@ -22,11 +21,11 @@ export class GetProjectsListQry extends Query { async internalExecute(organizationStatus?: OrganizationFilters): Promise { if (organizationStatus) { - const projects = await this.projectRepository.getProjects(organizationStatus) + const projects = await this.getProjectsQry.execute(organizationStatus) const usersList = await this.getUsersListQry.execute({ ids: projects.map((project) => project.blockedByUser).filter((id) => id !== null) as Id[] }) - return this.projectsWithUserName.addUserNameToProjects(projects, usersList) + return this.projectsWithUserName.addProjectBlockerUserName(projects, usersList) } return [] } diff --git a/src/features/administration/features/project/domain/services/projects-with-user-name.ts b/src/features/administration/features/project/domain/services/projects-with-user-name.ts index a4f9c0ed..31addc74 100644 --- a/src/features/administration/features/project/domain/services/projects-with-user-name.ts +++ b/src/features/administration/features/project/domain/services/projects-with-user-name.ts @@ -2,10 +2,9 @@ import { singleton } from 'tsyringe' import { UserInfo } from '../../../../../shared/user/domain/user-info' import { Project } from '../../../../../shared/project/domain/project' -//TODO Renombrar correctamente @singleton() export class ProjectsWithUserName { - addUserNameToProjects(projectsWithoutUserName: Project[], usersList: UserInfo[]): Project[] { + addProjectBlockerUserName(projectsWithoutUserName: Project[], usersList: UserInfo[]): Project[] { return projectsWithoutUserName.map((projectWithoutUserName) => { const { blockedByUser, ...projectDetails } = projectWithoutUserName From f6ff70a30d2c780eac4647e1eb8317cc741c0586 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 19 Sep 2023 18:04:53 +0200 Subject: [PATCH 22/99] fix: resolve role mother issues with project changes --- .../application/get-projects-list-qry.test.ts | 12 ++++++------ src/test-utils/mothers/project-role-mother.ts | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/features/administration/features/project/application/get-projects-list-qry.test.ts b/src/features/administration/features/project/application/get-projects-list-qry.test.ts index 608397cc..53f6c760 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.test.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.test.ts @@ -1,9 +1,9 @@ import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { mock } from 'jest-mock-extended' import { GetProjectsListQry } from './get-projects-list-qry' -import { ProjectRepository } from '../../../../shared/project/domain/project-repository' import { ProjectMother } from '../../../../shared/project/domain/tests/project-mother' import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' +import { GetProjectsQry } from '../../../../shared/project/application/binnacle/get-projects-qry' describe('GetProjectsListQry', () => { it('should get the project list', async () => { @@ -13,29 +13,29 @@ describe('GetProjectsListQry', () => { open: true } - projectRepository.getProjects.mockResolvedValue( + projectRepository.execute.mockResolvedValue( ProjectMother.projectsFilteredByOrganizationDateIso() ) await getProjectsListQry.internalExecute(organizationWithStatus) - expect(projectRepository.getProjects).toBeCalledWith(organizationWithStatus) + expect(projectRepository.execute).toBeCalledWith(organizationWithStatus) expect(getUsersListQry.execute).toHaveBeenCalledWith({ ids: [2, 1] }) }) }) function setup() { - const projectRepository = mock() + const getProjectQry = mock() const getUsersListQry = mock() const projectsWithUserName = mock() return { getProjectsListQry: new GetProjectsListQry( - projectRepository, + getProjectQry, getUsersListQry, projectsWithUserName ), - projectRepository, + projectRepository: getProjectQry, getUsersListQry, projectsWithUserName } diff --git a/src/test-utils/mothers/project-role-mother.ts b/src/test-utils/mothers/project-role-mother.ts index 0dd940bc..bf4bf432 100644 --- a/src/test-utils/mothers/project-role-mother.ts +++ b/src/test-utils/mothers/project-role-mother.ts @@ -4,6 +4,7 @@ import { LiteProjectRoleWithProjectId } from '../../features/binnacle/features/s import { TimeUnits } from '../../shared/types/time-unit' import { OrganizationMother } from './organization-mother' import { LiteProjectMother } from './lite-project-mother' +import { ProjectMother } from '../../features/shared/project/domain/tests/project-mother' export class ProjectRoleMother { static projectRoles(): ProjectRole[] { @@ -93,21 +94,21 @@ export class ProjectRoleMother { static liteProjectRoleInDays(): LiteProjectRoleWithProjectId { return { ...this.projectRoleInDays(), - projectId: LiteProjectMother.notBillableProject().id + projectId: ProjectMother.notBillableProject().id } } static liteProjectRoleInDaysRequireApproval(): LiteProjectRoleWithProjectId { return { ...this.projectRoleInDaysRequireApproval(), - projectId: LiteProjectMother.notBillableProject().id + projectId: ProjectMother.notBillableProject().id } } static liteProjectRoleInMinutes(): LiteProjectRoleWithProjectId { return { ...this.projectRoleInMinutes(), - projectId: LiteProjectMother.billableProject().id + projectId: ProjectMother.billableProject().id } } From 36016397684a0e5fc2a2df1186cd1bfd6cfcad35 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 20 Sep 2023 14:35:45 +0200 Subject: [PATCH 23/99] refactor: rename organization filters --- .../features/project/application/get-projects-list-qry.ts | 6 +++--- .../features/project/ui/components/projects-table.tsx | 6 +++--- .../shared/project/application/binnacle/get-projects-qry.ts | 6 +++--- ...anization-filters.ts => project-organization-filters.ts} | 2 +- src/features/shared/project/domain/project-repository.ts | 4 ++-- .../project/infrastructure/http-project-repository.ts | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) rename src/features/shared/project/domain/{organization-filters.ts => project-organization-filters.ts} (64%) diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-list-qry.ts index e6db8c46..c722280a 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.ts @@ -3,14 +3,14 @@ import { GetUsersListQry } from '../../../../shared/user/application/get-users-l import { singleton } from 'tsyringe' import { Id } from '../../../../../shared/types/id' import { Project } from '../../../../shared/project/domain/project' -import { OrganizationFilters } from '../../../../shared/project/domain/organization-filters' +import { ProjectOrganizationFilters } from '../../../../shared/project/domain/project-organization-filters' import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { GetProjectsQry } from '../../../../shared/project/application/binnacle/get-projects-qry' @UseCaseKey('GetProjectsListQry') @InvalidateCache @singleton() -export class GetProjectsListQry extends Query { +export class GetProjectsListQry extends Query { constructor( private getProjectsQry: GetProjectsQry, private getUsersListQry: GetUsersListQry, @@ -19,7 +19,7 @@ export class GetProjectsListQry extends Query { super() } - async internalExecute(organizationStatus?: OrganizationFilters): Promise { + async internalExecute(organizationStatus?: ProjectOrganizationFilters): Promise { if (organizationStatus) { const projects = await this.getProjectsQry.execute(organizationStatus) const usersList = await this.getUsersListQry.execute({ diff --git a/src/features/administration/features/project/ui/components/projects-table.tsx b/src/features/administration/features/project/ui/components/projects-table.tsx index 81106e35..98db5926 100644 --- a/src/features/administration/features/project/ui/components/projects-table.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.tsx @@ -15,7 +15,7 @@ import { ProjectsFilterFormCombos } from './combos/projects-combos' import { StatusBadge } from './status-badge' import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' import { Project } from '../../../../../shared/project/domain/project' -import { OrganizationFilters } from '../../../../../shared/project/domain/organization-filters' +import { ProjectOrganizationFilters } from '../../../../../shared/project/domain/project-organization-filters' interface Props { onProjectClicked(project: Project): void @@ -26,7 +26,7 @@ export const ProjectsTable: FC = (props) => { const { t } = useTranslation() const [organizationName, setOrganizationName] = useState('') const [lastSelectedOrganizationWithStatus, setLastSelectedOrganizationWithStatus] = - useState() + useState() const [tableProjects, setTableProjects] = useState([]) const isMobile = useIsMobile() @@ -57,7 +57,7 @@ export const ProjectsTable: FC = (props) => { async (organization: Organization, status: ProjectStatus) => { if (organization?.id) { setOrganizationName(organization.name) - const organizationWithStatus: OrganizationFilters = { + const organizationWithStatus: ProjectOrganizationFilters = { organizationIds: [organization.id], open: status.value } diff --git a/src/features/shared/project/application/binnacle/get-projects-qry.ts b/src/features/shared/project/application/binnacle/get-projects-qry.ts index 0c31bf2d..c2ee4ad1 100644 --- a/src/features/shared/project/application/binnacle/get-projects-qry.ts +++ b/src/features/shared/project/application/binnacle/get-projects-qry.ts @@ -3,16 +3,16 @@ import { inject, singleton } from 'tsyringe' import { PROJECT_REPOSITORY } from '../../../../../shared/di/container-tokens' import { Project } from '../../domain/project' import type { ProjectRepository } from '../../domain/project-repository' -import { OrganizationFilters } from '../../domain/organization-filters' +import { ProjectOrganizationFilters } from '../../domain/project-organization-filters' @UseCaseKey('GetProjectsQry') @singleton() -export class GetProjectsQry extends Query { +export class GetProjectsQry extends Query { constructor(@inject(PROJECT_REPOSITORY) private projectRepository: ProjectRepository) { super() } - internalExecute(organizationStatus?: OrganizationFilters): Promise { + internalExecute(organizationStatus?: ProjectOrganizationFilters): Promise { return this.projectRepository.getProjects(organizationStatus) } } diff --git a/src/features/shared/project/domain/organization-filters.ts b/src/features/shared/project/domain/project-organization-filters.ts similarity index 64% rename from src/features/shared/project/domain/organization-filters.ts rename to src/features/shared/project/domain/project-organization-filters.ts index 5b9fe70a..915daccc 100644 --- a/src/features/shared/project/domain/organization-filters.ts +++ b/src/features/shared/project/domain/project-organization-filters.ts @@ -1,6 +1,6 @@ import { Id } from '@archimedes/arch' -export interface OrganizationFilters { +export interface ProjectOrganizationFilters { organizationIds?: Id[] open?: boolean } diff --git a/src/features/shared/project/domain/project-repository.ts b/src/features/shared/project/domain/project-repository.ts index 1692c87e..a0753986 100644 --- a/src/features/shared/project/domain/project-repository.ts +++ b/src/features/shared/project/domain/project-repository.ts @@ -1,9 +1,9 @@ -import { OrganizationFilters } from './organization-filters' +import { ProjectOrganizationFilters } from './project-organization-filters' import { Project } from './project' import { Id } from '../../../../shared/types/id' export interface ProjectRepository { - getProjects(organizationStatus?: OrganizationFilters): Promise + getProjects(organizationStatus?: ProjectOrganizationFilters): Promise blockProject(projectId: Id, date: Date): Promise diff --git a/src/features/shared/project/infrastructure/http-project-repository.ts b/src/features/shared/project/infrastructure/http-project-repository.ts index a94388ee..fc1f6419 100644 --- a/src/features/shared/project/infrastructure/http-project-repository.ts +++ b/src/features/shared/project/infrastructure/http-project-repository.ts @@ -1,5 +1,5 @@ import { singleton } from 'tsyringe' -import { OrganizationFilters } from '../domain/organization-filters' +import { ProjectOrganizationFilters } from '../domain/project-organization-filters' import { Project } from '../domain/project' import { ProjectDto } from '../domain/project-dto' import { ProjectRepository } from '../domain/project-repository' @@ -16,7 +16,7 @@ export class HttpProjectRepository implements ProjectRepository { constructor(private httpClient: HttpClient) {} - async getProjects(organizationWithStatus: OrganizationFilters): Promise { + async getProjects(organizationWithStatus: ProjectOrganizationFilters): Promise { const data = await this.httpClient.get(HttpProjectRepository.projectPath, { params: organizationWithStatus }) From 8afb546aacc37f0e33489afa914aa89e47ef36c7 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 20 Sep 2023 15:25:58 +0200 Subject: [PATCH 24/99] feat: add organization filter to use case --- .../organization/application/get-organizations-qry.ts | 7 ++++--- .../features/organization/domain/organization-filters.ts | 6 ++++++ .../organization/domain/organization-repository.ts | 3 ++- .../features/organization/domain/organization-type.ts | 7 +++++++ .../infrastructure/http-organization-repository.ts | 7 +++++-- 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 src/features/binnacle/features/organization/domain/organization-filters.ts create mode 100644 src/features/binnacle/features/organization/domain/organization-type.ts diff --git a/src/features/binnacle/features/organization/application/get-organizations-qry.ts b/src/features/binnacle/features/organization/application/get-organizations-qry.ts index 7541f743..f9d029db 100644 --- a/src/features/binnacle/features/organization/application/get-organizations-qry.ts +++ b/src/features/binnacle/features/organization/application/get-organizations-qry.ts @@ -3,17 +3,18 @@ import { ORGANIZATION_REPOSITORY } from '../../../../../shared/di/container-toke import { inject, singleton } from 'tsyringe' import { Organization } from '../domain/organization' import type { OrganizationRepository } from '../domain/organization-repository' +import { OrganizationFilters } from '../domain/organization-filters' @UseCaseKey('GetOrganizationsQry') @singleton() -export class GetOrganizationsQry extends Query { +export class GetOrganizationsQry extends Query { constructor( @inject(ORGANIZATION_REPOSITORY) private organizationRepository: OrganizationRepository ) { super() } - internalExecute(): Promise { - return this.organizationRepository.getAll() + internalExecute(organizationFilters: OrganizationFilters): Promise { + return this.organizationRepository.getAll(organizationFilters) } } diff --git a/src/features/binnacle/features/organization/domain/organization-filters.ts b/src/features/binnacle/features/organization/domain/organization-filters.ts new file mode 100644 index 00000000..a4dba36a --- /dev/null +++ b/src/features/binnacle/features/organization/domain/organization-filters.ts @@ -0,0 +1,6 @@ +import { OrganizationType } from './organization-type' + +export interface OrganizationFilters { + imputable: boolean + types?: OrganizationType[] +} diff --git a/src/features/binnacle/features/organization/domain/organization-repository.ts b/src/features/binnacle/features/organization/domain/organization-repository.ts index efc93239..be9b78ee 100644 --- a/src/features/binnacle/features/organization/domain/organization-repository.ts +++ b/src/features/binnacle/features/organization/domain/organization-repository.ts @@ -1,5 +1,6 @@ import { Organization } from './organization' +import { OrganizationFilters } from './organization-filters' export interface OrganizationRepository { - getAll(): Promise + getAll(organizationFilters: OrganizationFilters): Promise } diff --git a/src/features/binnacle/features/organization/domain/organization-type.ts b/src/features/binnacle/features/organization/domain/organization-type.ts new file mode 100644 index 00000000..3d4cf409 --- /dev/null +++ b/src/features/binnacle/features/organization/domain/organization-type.ts @@ -0,0 +1,7 @@ +export const OrganizationTypes = { + CLIENT: 'CLIENT', + PROVIDER: 'PROVIDER', + PROSPECT: 'PROSPECT' +} + +export type OrganizationType = keyof typeof OrganizationTypes diff --git a/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts b/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts index edf12606..679679b4 100644 --- a/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts +++ b/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts @@ -2,6 +2,7 @@ import { HttpClient } from '../../../../../shared/http/http-client' import { singleton } from 'tsyringe' import { Organization } from '../domain/organization' import { OrganizationRepository } from '../domain/organization-repository' +import { OrganizationFilters } from '../domain/organization-filters' @singleton() export class HttpOrganizationRepository implements OrganizationRepository { @@ -9,7 +10,9 @@ export class HttpOrganizationRepository implements OrganizationRepository { constructor(private httpClient: HttpClient) {} - async getAll(): Promise { - return this.httpClient.get(HttpOrganizationRepository.organizationPath) + async getAll(organizationFilters: OrganizationFilters): Promise { + return this.httpClient.get(HttpOrganizationRepository.organizationPath, { + params: organizationFilters + }) } } From 39cefe7634937afb1f48b0c632e9c9c64dcf172f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 20 Sep 2023 15:28:07 +0200 Subject: [PATCH 25/99] feat: add is imputable prop --- .../project/ui/components/combos/projects-combos.tsx | 2 +- .../activity-form/components/combos/activity-form-combos.tsx | 1 + .../activity-form/components/combos/organizations-combo.tsx | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx index 364c48b4..da03aaa5 100644 --- a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx +++ b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx @@ -52,7 +52,7 @@ export const ProjectsFilterFormCombos: FC = (props) => { marginBottom={5} marginTop={4} > - + ) diff --git a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx index 378d01f1..f8a0d6f8 100644 --- a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx +++ b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx @@ -59,6 +59,7 @@ export const ActivityFormCombos = forwardRef( if (item?.name !== organization?.name) onOrganizationChange() }} isReadOnly={isReadOnly} + imputableOrganizations={true} /> onChange?: (item: Organization) => void isReadOnly?: boolean + imputableOrganizations: boolean } export const OrganizationsCombo = forwardRef((props, ref) => { const { name = 'organization', control, onChange, isReadOnly } = props const { t } = useTranslation() - const { isLoading, result: organizations } = useExecuteUseCaseOnMount(GetOrganizationsQry) + const { isLoading, result: organizations } = useExecuteUseCaseOnMount(GetOrganizationsQry, { + imputable: props.imputableOrganizations + }) return ( Date: Wed, 20 Sep 2023 15:33:29 +0200 Subject: [PATCH 26/99] fix: resolve test issue --- .../project/application/get-projects-list-qry.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/administration/features/project/application/get-projects-list-qry.test.ts b/src/features/administration/features/project/application/get-projects-list-qry.test.ts index 9352ef7d..02624776 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.test.ts +++ b/src/features/administration/features/project/application/get-projects-list-qry.test.ts @@ -30,13 +30,13 @@ describe('GetProjectsListQry', () => { open: true } - projectRepository.getProjects.mockResolvedValue([ + projectRepository.execute.mockResolvedValue([ ProjectMother.projectsFilteredByOrganizationDateIso()[2] ]) await getProjectsListQry.internalExecute(organizationWithStatus) - expect(projectRepository.getProjects).toBeCalledWith(organizationWithStatus) + expect(projectRepository.execute).toBeCalledWith(organizationWithStatus) expect(getUsersListQry.execute).not.toHaveBeenCalled() }) }) From 4f57b692bfdc25f4bd8dabf676e4db9b7a925761 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 20 Sep 2023 15:51:30 +0200 Subject: [PATCH 27/99] refactor: rename get-project-list-qry --- ....ts => get-projects-with-blocker-user-name.test.ts} | 6 +++--- ...t-qry.ts => get-projects-with-blocker-user-name.ts} | 4 ++-- .../features/project/ui/components/projects-table.tsx | 4 ++-- src/shared/archimedes/archimedes.ts | 10 +++++++--- 4 files changed, 14 insertions(+), 10 deletions(-) rename src/features/administration/features/project/application/{get-projects-list-qry.test.ts => get-projects-with-blocker-user-name.test.ts} (90%) rename src/features/administration/features/project/application/{get-projects-list-qry.ts => get-projects-with-blocker-user-name.ts} (91%) diff --git a/src/features/administration/features/project/application/get-projects-list-qry.test.ts b/src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts similarity index 90% rename from src/features/administration/features/project/application/get-projects-list-qry.test.ts rename to src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts index 02624776..1e8e7d4c 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.test.ts +++ b/src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts @@ -1,11 +1,11 @@ import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { mock } from 'jest-mock-extended' -import { GetProjectsListQry } from './get-projects-list-qry' +import { GetProjectsWithBlockerUserName } from './get-projects-with-blocker-user-name' import { ProjectMother } from '../../../../shared/project/domain/tests/project-mother' import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { GetProjectsQry } from '../../../../shared/project/application/binnacle/get-projects-qry' -describe('GetProjectsListQry', () => { +describe('GetProjectsWithBlockerUserName', () => { it('should get the project list', async () => { const { getProjectsListQry, projectRepository, getUsersListQry } = setup() const organizationWithStatus = { @@ -47,7 +47,7 @@ function setup() { const projectsWithUserName = mock() return { - getProjectsListQry: new GetProjectsListQry( + getProjectsListQry: new GetProjectsWithBlockerUserName( getProjectQry, getUsersListQry, projectsWithUserName diff --git a/src/features/administration/features/project/application/get-projects-list-qry.ts b/src/features/administration/features/project/application/get-projects-with-blocker-user-name.ts similarity index 91% rename from src/features/administration/features/project/application/get-projects-list-qry.ts rename to src/features/administration/features/project/application/get-projects-with-blocker-user-name.ts index 03cd3a33..f6f48950 100644 --- a/src/features/administration/features/project/application/get-projects-list-qry.ts +++ b/src/features/administration/features/project/application/get-projects-with-blocker-user-name.ts @@ -7,10 +7,10 @@ import { ProjectOrganizationFilters } from '../../../../shared/project/domain/pr import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { GetProjectsQry } from '../../../../shared/project/application/binnacle/get-projects-qry' -@UseCaseKey('GetProjectsListQry') +@UseCaseKey('GetProjectsWithBlockerUserName') @InvalidateCache @singleton() -export class GetProjectsListQry extends Query { +export class GetProjectsWithBlockerUserName extends Query { constructor( private getProjectsQry: GetProjectsQry, private getUsersListQry: GetUsersListQry, diff --git a/src/features/administration/features/project/ui/components/projects-table.tsx b/src/features/administration/features/project/ui/components/projects-table.tsx index 98db5926..8bcae882 100644 --- a/src/features/administration/features/project/ui/components/projects-table.tsx +++ b/src/features/administration/features/project/ui/components/projects-table.tsx @@ -7,7 +7,7 @@ import { useSubscribeToUseCase } from '../../../../../../shared/arch/hooks/use-s import { Table } from '../../../../../../shared/components/table/table' import { ColumnsProps } from '../../../../../../shared/components/table/table.types' import { BlockProjectCmd } from '../../application/block-project-cmd' -import { GetProjectsListQry } from '../../application/get-projects-list-qry' +import { GetProjectsWithBlockerUserName } from '../../application/get-projects-with-blocker-user-name' import { UnblockProjectCmd } from '../../application/unblock-project-cmd' import { ProjectStatus } from '../../domain/project-status' import { AdaptedProjects, adaptProjectsToTable } from '../projects-page-utils' @@ -34,7 +34,7 @@ export const ProjectsTable: FC = (props) => { isLoading: isLoadingProjectsList, result: projectList = [], executeUseCase: getProjectsListQry - } = useExecuteUseCaseOnMount(GetProjectsListQry) + } = useExecuteUseCaseOnMount(GetProjectsWithBlockerUserName) useSubscribeToUseCase( BlockProjectCmd, diff --git a/src/shared/archimedes/archimedes.ts b/src/shared/archimedes/archimedes.ts index 88d29df9..e8a9bd6d 100644 --- a/src/shared/archimedes/archimedes.ts +++ b/src/shared/archimedes/archimedes.ts @@ -34,7 +34,7 @@ import { SaveUserSettingsCmd } from '../../features/shared/user/features/setting import { TOAST } from '../di/container-tokens' import { container } from 'tsyringe' import { BlockProjectCmd } from '../../features/administration/features/project/application/block-project-cmd' -import { GetProjectsListQry } from '../../features/administration/features/project/application/get-projects-list-qry' +import { GetProjectsWithBlockerUserName } from '../../features/administration/features/project/application/get-projects-with-blocker-user-name' import { UnblockProjectCmd } from '../../features/administration/features/project/application/unblock-project-cmd' import { ToastNotificationLink } from './links/toast-notification-link' import { ToastType } from '../notification/toast' @@ -103,5 +103,9 @@ CacheInvalidations.set(UpdateVacationCmd.prototype.key, [ GetCalendarDataQry.prototype.key, GetActivitySummaryQry.prototype.key ]) -CacheInvalidations.set(BlockProjectCmd.prototype.key, [GetProjectsListQry.prototype.key]) -CacheInvalidations.set(UnblockProjectCmd.prototype.key, [GetProjectsListQry.prototype.key]) +CacheInvalidations.set(BlockProjectCmd.prototype.key, [ + GetProjectsWithBlockerUserName.prototype.key +]) +CacheInvalidations.set(UnblockProjectCmd.prototype.key, [ + GetProjectsWithBlockerUserName.prototype.key +]) From 8d010b993f181b1fb1212890b0f96b34f7175204 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 20 Sep 2023 16:45:56 +0200 Subject: [PATCH 28/99] test: remove unit test from projects table and create integration test --- .../project/tests/view-projects.int.tsx | 40 ++++++++++ .../ui/components/projects-table.test.tsx | 76 ------------------- 2 files changed, 40 insertions(+), 76 deletions(-) create mode 100644 src/features/administration/features/project/tests/view-projects.int.tsx delete mode 100644 src/features/administration/features/project/ui/components/projects-table.test.tsx diff --git a/src/features/administration/features/project/tests/view-projects.int.tsx b/src/features/administration/features/project/tests/view-projects.int.tsx new file mode 100644 index 00000000..c93f4e0e --- /dev/null +++ b/src/features/administration/features/project/tests/view-projects.int.tsx @@ -0,0 +1,40 @@ +import ProjectsPage from '../ui/projects-page' + +describe('View projects', () => { + it('should not view any project if there is no organization', () => { + setup() + + cy.findByTestId('organization_field').should('contain.text', '') + + cy.get('[data-testid="empty-desktop-view"]').should('contain.text', '') + + cy.findByText('It is necessary to filter by organization to obtain the projects.').should( + 'exist' + ) + }) + + it('should view project is organization', () => { + setup() + + cy.findByTestId('organization_field').type('Test') + cy.findByText('Test organization').click() + + cy.findByText('Proyecto A').should('exist') + }) + + it('should show toast after blocking project', () => { + setup() + + cy.findByTestId('organization_field').type('Test') + cy.findByText('Test organization').click() + + cy.findByText('Block').click() + cy.get('form').submit() + + cy.findByText('The project has been blocked.').should('exist') + }) +}) + +const setup = () => { + cy.mount() +} diff --git a/src/features/administration/features/project/ui/components/projects-table.test.tsx b/src/features/administration/features/project/ui/components/projects-table.test.tsx deleted file mode 100644 index 9de52b6e..00000000 --- a/src/features/administration/features/project/ui/components/projects-table.test.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import userEvent from '@testing-library/user-event' -import { OrganizationRepository } from '../../../../../binnacle/features/organization/domain/organization-repository' -import { UserRepository } from '../../../../../shared/user/domain/user-repository' -import { - ORGANIZATION_REPOSITORY, - PROJECT_REPOSITORY, - USER_REPOSITORY -} from '../../../../../../shared/di/container-tokens' -import { OrganizationMother } from '../../../../../../test-utils/mothers/organization-mother' -import { UserMother } from '../../../../../../test-utils/mothers/user-mother' -import { container } from 'tsyringe' -import { ProjectsTable } from './projects-table' -import { act, render, screen, waitFor } from '../../../../../../test-utils/render' -import { useIsMobile } from '../../../../../../shared/hooks/use-is-mobile' -import { ProjectMother } from '../../../../../shared/project/domain/tests/project-mother' -import { ProjectRepository } from '../../../../../shared/project/domain/project-repository' - -jest.mock('../../../../../../shared/hooks/use-is-mobile') - -describe('ProjectsTable', () => { - it('should show all projects when organization filter is changed', async () => { - setup() - const projects = ProjectMother.projectsFilteredByOrganizationDateIsoWithName() - - await act(async () => { - const organizationCombo = screen.getByTestId('organization_field') - await userEvent.type(organizationCombo, OrganizationMother.organization().name) - }) - - await waitFor(() => { - projects.map((p) => { - expect(screen.getByText(p.name)).toBeInTheDocument() - }) - }) - }) - - it('should execute onProjectClicked method when project block action is pressed', async () => { - const { onProjectClicked } = setup() - - await act(async () => { - const organizationCombo = screen.getByTestId('organization_field') - await userEvent.type(organizationCombo, OrganizationMother.organization().name) - }) - - await act(async () => { - const blockButtons = screen.getAllByRole('button') - await userEvent.click(blockButtons[0]) - }) - expect(onProjectClicked).toBeCalledTimes(1) - }) -}) - -function setup() { - const projectRepository = container.resolve>(PROJECT_REPOSITORY) - const userRepository = container.resolve>(USER_REPOSITORY) - const organizationRepository = - container.resolve>(ORGANIZATION_REPOSITORY) - organizationRepository.getAll.mockResolvedValue(OrganizationMother.organizations()) - - projectRepository.getProjects.mockResolvedValue( - ProjectMother.projectsFilteredByOrganizationDateIsoWithName() - ) - userRepository.getUsers.mockResolvedValue(UserMother.userList()) - - const onProjectClicked = jest.fn() - ;(useIsMobile as jest.Mock).mockReturnValue(false) - - render() - - return { - projectRepository, - userRepository, - organizationRepository, - onProjectClicked - } -} From 78168c2ec3edc47216615cc550b91663fe613787 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 09:43:51 +0200 Subject: [PATCH 29/99] feat: add styling --- .../binnacle/features/availability/ui/availability-page.tsx | 5 ++++- .../availability/ui/components/availability-table.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/availability-page.tsx b/src/features/binnacle/features/availability/ui/availability-page.tsx index 4527deff..fe3b6033 100644 --- a/src/features/binnacle/features/availability/ui/availability-page.tsx +++ b/src/features/binnacle/features/availability/ui/availability-page.tsx @@ -2,13 +2,16 @@ import { FC } from 'react' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' import { useTranslation } from 'react-i18next' import { AvailabilityTable } from './components/availability-table' +import { Box } from '@chakra-ui/react' const AvailabilityPage: FC = () => { const { t } = useTranslation() return ( - + + + ) } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 13b789f9..0dcd5f60 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -47,7 +47,7 @@ export const AvailabilityTable: FC = () => { ) return ( - +
{tableHeaders} {tableRows}
From d01bbb0620d83ac4566fadfbb0bbeb60ab243b9f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 10:21:30 +0200 Subject: [PATCH 30/99] feat: create cell component --- .../ui/components/availability-table-cell.tsx | 21 +++++++++++++++ .../ui/components/availability-table.tsx | 27 +++++++++++-------- 2 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx new file mode 100644 index 00000000..c7509fb2 --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx @@ -0,0 +1,21 @@ +import { FC } from 'react' +import { Td, useColorModeValue } from '@chakra-ui/react' +import { isWeekend } from '../../../../../../shared/utils/chrono' + +export const AvailabilityTableCell: FC<{ day: Date; isHoliday: boolean }> = ({ + day, + isHoliday +}) => { + const borderColor = useColorModeValue('gray.300', 'gray.700') + + return ( + + {day.getDate()} + + ) +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 0dcd5f60..0918fd54 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,10 +1,11 @@ import { FC } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' -import { Table, Th, Thead, Tr } from '@chakra-ui/react' +import { Table, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { GetHolidaysQry } from '../../../holiday/application/get-holidays-qry' +import { AvailabilityTableCell } from './availability-table-cell' export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() @@ -23,7 +24,7 @@ export const AvailabilityTable: FC = () => { const tableHeaders = ( - + {daysOfMonth.map((day, index) => ( { ) const tableRows = ( - - Lorem ipsum dolor sit amet. - {daysOfMonth.map((day, index) => ( - - {day.getDate()} - - ))}{' '} - + + + Lorem ipsum dolor sit amet. + {daysOfMonth.map((day, index) => ( + + ))} + + ) return ( - +
{tableHeaders} {tableRows}
From 0615de087be75b28315f14eaf4e5382fe63f68ce Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 10:21:46 +0200 Subject: [PATCH 31/99] feat: update background color styling --- .../ui/components/availability-table-cell-header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index b13d6c0a..61a12329 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -24,7 +24,7 @@ export const AvailabilityTableCellHeader: FC = ({ day, isHoliday }) => { {weekDays[getWeekDay(day) - 1]} From 71bb24a3125ca155b5c1500fbe149ad9b84bca19 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 11:22:22 +0200 Subject: [PATCH 32/99] feat: add navigation component to availability page --- .../availability/ui/availability-page.tsx | 22 +++++++++++++++---- .../ui/components/availability-table.tsx | 18 ++++++++++----- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/availability-page.tsx b/src/features/binnacle/features/availability/ui/availability-page.tsx index fe3b6033..1e89be17 100644 --- a/src/features/binnacle/features/availability/ui/availability-page.tsx +++ b/src/features/binnacle/features/availability/ui/availability-page.tsx @@ -2,16 +2,30 @@ import { FC } from 'react' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' import { useTranslation } from 'react-i18next' import { AvailabilityTable } from './components/availability-table' -import { Box } from '@chakra-ui/react' +import { Box, Flex } from '@chakra-ui/react' +import { CalendarControls } from '../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' +import { CalendarProvider } from '../../activity/ui/contexts/calendar-context' const AvailabilityPage: FC = () => { const { t } = useTranslation() return ( - - - + + + + + + + + + ) } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 0918fd54..76e21632 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, useMemo } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' import { Table, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' @@ -9,13 +9,21 @@ import { AvailabilityTableCell } from './availability-table-cell' export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() - const daysOfMonth = chrono(chrono(selectedDate).startOf('month').getDate()).eachDayUntil( - chrono(selectedDate).endOf('month').getDate() + + const selectedDateInterval = useMemo(() => { + return { + startOfMonth: chrono(selectedDate).startOf('month').getDate(), + endOfMonth: chrono(selectedDate).endOf('month').getDate() + } + }, [selectedDate]) + + const daysOfMonth = chrono(selectedDateInterval.startOfMonth).eachDayUntil( + selectedDateInterval.endOfMonth ) const { result: holidays = [] } = useExecuteUseCaseOnMount(GetHolidaysQry, { - start: chrono(selectedDate).startOf('month').getDate(), - end: chrono(selectedDate).endOf('month').getDate() + start: selectedDateInterval.startOfMonth, + end: selectedDateInterval.endOfMonth }) const checkIfHoliday = (day: Date) => From b2dabfa6a8c6909e5720364ba390c3a57b4c3540 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 12:28:23 +0200 Subject: [PATCH 33/99] feat: create absence model --- .../features/availability/domain/absence-type.ts | 6 ++++++ .../binnacle/features/availability/domain/absence.ts | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/features/binnacle/features/availability/domain/absence-type.ts create mode 100644 src/features/binnacle/features/availability/domain/absence.ts diff --git a/src/features/binnacle/features/availability/domain/absence-type.ts b/src/features/binnacle/features/availability/domain/absence-type.ts new file mode 100644 index 00000000..374f1ba5 --- /dev/null +++ b/src/features/binnacle/features/availability/domain/absence-type.ts @@ -0,0 +1,6 @@ +const AbsenceTypes = { + VACATION: 'VACATION', + PAID_LEAVE: 'PAID_LEAVE' +} + +export type AbsenceType = keyof typeof AbsenceTypes diff --git a/src/features/binnacle/features/availability/domain/absence.ts b/src/features/binnacle/features/availability/domain/absence.ts new file mode 100644 index 00000000..884702db --- /dev/null +++ b/src/features/binnacle/features/availability/domain/absence.ts @@ -0,0 +1,10 @@ +import { Id } from '../../../../../shared/types/id' +import { AbsenceType } from './absence-type' + +export interface Absence { + userId: Id + userName: string + type: AbsenceType + startDate: Date + endDate: Date +} From 28d19202f742d0a77a2efc5b76f0fda4caa496c4 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 12:35:36 +0200 Subject: [PATCH 34/99] feat: create absence repository --- .../features/availability/domain/absence-filters.ts | 9 +++++++++ .../features/availability/domain/absence-repository.ts | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100644 src/features/binnacle/features/availability/domain/absence-filters.ts create mode 100644 src/features/binnacle/features/availability/domain/absence-repository.ts diff --git a/src/features/binnacle/features/availability/domain/absence-filters.ts b/src/features/binnacle/features/availability/domain/absence-filters.ts new file mode 100644 index 00000000..74b9ef57 --- /dev/null +++ b/src/features/binnacle/features/availability/domain/absence-filters.ts @@ -0,0 +1,9 @@ +import { Id } from '../../../../../shared/types/id' + +export interface AbsenceFilters { + userId: Id + organizationId: Id + projectId: Id + startDate: Date + endDate: Date +} diff --git a/src/features/binnacle/features/availability/domain/absence-repository.ts b/src/features/binnacle/features/availability/domain/absence-repository.ts new file mode 100644 index 00000000..89585ccb --- /dev/null +++ b/src/features/binnacle/features/availability/domain/absence-repository.ts @@ -0,0 +1,6 @@ +import { Absence } from './absence' +import { AbsenceFilters } from './absence-filters' + +export interface AbsenceRepository { + getAbsences(absenceFilters: AbsenceFilters): Promise +} From 902a48923dd110aaa35ec9200b6e800b2b530543 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 12:43:27 +0200 Subject: [PATCH 35/99] refactor: move project mother to test-utils file --- .../application/get-projects-with-blocker-user-name.test.ts | 2 +- .../project/infrastructure/fake-project-repository.ts | 2 +- .../project/infrastructure/http-project-repository.test.ts | 2 +- src/test-utils/mothers/lite-project-mother.ts | 2 +- .../domain/tests => test-utils/mothers}/project-mother.ts | 6 +++--- src/test-utils/mothers/project-role-mother.ts | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename src/{features/shared/project/domain/tests => test-utils/mothers}/project-mother.ts (93%) diff --git a/src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts b/src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts index 1e8e7d4c..8c29b80d 100644 --- a/src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts +++ b/src/features/administration/features/project/application/get-projects-with-blocker-user-name.test.ts @@ -1,9 +1,9 @@ import { GetUsersListQry } from '../../../../shared/user/application/get-users-list-qry' import { mock } from 'jest-mock-extended' import { GetProjectsWithBlockerUserName } from './get-projects-with-blocker-user-name' -import { ProjectMother } from '../../../../shared/project/domain/tests/project-mother' import { ProjectsWithUserName } from '../domain/services/projects-with-user-name' import { GetProjectsQry } from '../../../../shared/project/application/binnacle/get-projects-qry' +import { ProjectMother } from '../../../../../test-utils/mothers/project-mother' describe('GetProjectsWithBlockerUserName', () => { it('should get the project list', async () => { diff --git a/src/features/shared/project/infrastructure/fake-project-repository.ts b/src/features/shared/project/infrastructure/fake-project-repository.ts index 8ad4f1cc..ac75c09d 100644 --- a/src/features/shared/project/infrastructure/fake-project-repository.ts +++ b/src/features/shared/project/infrastructure/fake-project-repository.ts @@ -1,7 +1,7 @@ import { singleton } from 'tsyringe' import { Project } from '../domain/project' import { ProjectRepository } from '../domain/project-repository' -import { ProjectMother } from '../domain/tests/project-mother' +import { ProjectMother } from '../../../../test-utils/mothers/project-mother' @singleton() export class FakeProjectRepository implements ProjectRepository { diff --git a/src/features/shared/project/infrastructure/http-project-repository.test.ts b/src/features/shared/project/infrastructure/http-project-repository.test.ts index 8bccee68..e6615df8 100644 --- a/src/features/shared/project/infrastructure/http-project-repository.test.ts +++ b/src/features/shared/project/infrastructure/http-project-repository.test.ts @@ -1,8 +1,8 @@ import { mock } from 'jest-mock-extended' -import { ProjectMother } from '../domain/tests/project-mother' import { HttpProjectRepository } from './http-project-repository' import { chrono } from '../../../../shared/utils/chrono' import { HttpClient } from '../../../../shared/http/http-client' +import { ProjectMother } from '../../../../test-utils/mothers/project-mother' describe('HttpProjectRepository', () => { test('should get projects by organizationId', async () => { diff --git a/src/test-utils/mothers/lite-project-mother.ts b/src/test-utils/mothers/lite-project-mother.ts index 02f0f838..eed873f4 100644 --- a/src/test-utils/mothers/lite-project-mother.ts +++ b/src/test-utils/mothers/lite-project-mother.ts @@ -1,8 +1,8 @@ import { LiteProject } from '../../features/binnacle/features/project/domain/lite-project' import { LiteProjectWithOrganizationId } from '../../features/binnacle/features/search/domain/lite-project-with-organization-id' import { OrganizationMother } from './organization-mother' -import { ProjectMother } from '../../features/shared/project/domain/tests/project-mother' import { Project } from '../../features/shared/project/domain/project' +import { ProjectMother } from './project-mother' export class LiteProjectMother { static projects(): Project[] { diff --git a/src/features/shared/project/domain/tests/project-mother.ts b/src/test-utils/mothers/project-mother.ts similarity index 93% rename from src/features/shared/project/domain/tests/project-mother.ts rename to src/test-utils/mothers/project-mother.ts index aa40b36f..e415020b 100644 --- a/src/features/shared/project/domain/tests/project-mother.ts +++ b/src/test-utils/mothers/project-mother.ts @@ -1,6 +1,6 @@ -import { ProjectDto } from '../project-dto' -import { Project } from '../project' -import { parseISO } from '../../../../../shared/utils/chrono' +import { Project } from '../../features/shared/project/domain/project' +import { parseISO } from '../../shared/utils/chrono' +import { ProjectDto } from '../../features/shared/project/domain/project-dto' export class ProjectMother { static billableProject(): Project { diff --git a/src/test-utils/mothers/project-role-mother.ts b/src/test-utils/mothers/project-role-mother.ts index bf4bf432..f05a5f18 100644 --- a/src/test-utils/mothers/project-role-mother.ts +++ b/src/test-utils/mothers/project-role-mother.ts @@ -4,7 +4,7 @@ import { LiteProjectRoleWithProjectId } from '../../features/binnacle/features/s import { TimeUnits } from '../../shared/types/time-unit' import { OrganizationMother } from './organization-mother' import { LiteProjectMother } from './lite-project-mother' -import { ProjectMother } from '../../features/shared/project/domain/tests/project-mother' +import { ProjectMother } from './project-mother' export class ProjectRoleMother { static projectRoles(): ProjectRole[] { From b452720b819452779f8a5a367200721befd9fa55 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 12:47:04 +0200 Subject: [PATCH 36/99] feat: create fake absence repository --- .../availability/domain/absence-repository.ts | 2 +- .../infrastructure/fake-absence-repository.ts | 9 ++++++ src/test-utils/mothers/absence-mother.ts | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts create mode 100644 src/test-utils/mothers/absence-mother.ts diff --git a/src/features/binnacle/features/availability/domain/absence-repository.ts b/src/features/binnacle/features/availability/domain/absence-repository.ts index 89585ccb..4183ae80 100644 --- a/src/features/binnacle/features/availability/domain/absence-repository.ts +++ b/src/features/binnacle/features/availability/domain/absence-repository.ts @@ -2,5 +2,5 @@ import { Absence } from './absence' import { AbsenceFilters } from './absence-filters' export interface AbsenceRepository { - getAbsences(absenceFilters: AbsenceFilters): Promise + getAbsences(absenceFilters: AbsenceFilters): Promise } diff --git a/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts b/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts new file mode 100644 index 00000000..2770aec5 --- /dev/null +++ b/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts @@ -0,0 +1,9 @@ +import { AbsenceRepository } from '../domain/absence-repository' +import { Absence } from '../domain/absence' +import { AbsenceMother } from '../../../../../test-utils/mothers/absence-mother' + +export class FakeAbsenceRepository implements AbsenceRepository { + async getAbsences(): Promise { + return AbsenceMother.absences() + } +} diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts new file mode 100644 index 00000000..5105d308 --- /dev/null +++ b/src/test-utils/mothers/absence-mother.ts @@ -0,0 +1,28 @@ +import { Absence } from '../../features/binnacle/features/availability/domain/absence' +import { parseISO } from '../../shared/utils/chrono' + +export class AbsenceMother { + static absences(): Absence[] { + return [this.paidLeaveAbsence(), this.vacationAbsence()] + } + + static paidLeaveAbsence(): Absence { + return { + userId: 1, + userName: 'Paid leave user', + type: 'PAID_LEAVE', + startDate: parseISO('2023-01-01'), + endDate: parseISO('2023-01-02') + } + } + + static vacationAbsence(): Absence { + return { + userId: 1, + userName: 'Vacation user', + type: 'VACATION', + startDate: parseISO('2023-01-01'), + endDate: parseISO('2023-01-02') + } + } +} From d10f95bd73f70ba4eeee304dd1b360145866601c Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 13:00:16 +0200 Subject: [PATCH 37/99] feat: add get absences qry and add absence repository to container --- .../application/get-absences-qry.ts | 18 ++++++++++++++++++ .../infrastructure/fake-absence-repository.ts | 2 ++ src/shared/di/container-tokens.ts | 1 + src/shared/di/container.ts | 3 +++ 4 files changed, 24 insertions(+) create mode 100644 src/features/binnacle/features/availability/application/get-absences-qry.ts diff --git a/src/features/binnacle/features/availability/application/get-absences-qry.ts b/src/features/binnacle/features/availability/application/get-absences-qry.ts new file mode 100644 index 00000000..cfada668 --- /dev/null +++ b/src/features/binnacle/features/availability/application/get-absences-qry.ts @@ -0,0 +1,18 @@ +import { Query, UseCaseKey } from '@archimedes/arch' +import { Absence } from '../domain/absence' +import { AbsenceFilters } from '../domain/absence-filters' +import type { AbsenceRepository } from '../domain/absence-repository' +import { inject, singleton } from 'tsyringe' +import { ABSENCE_REPOSITORY } from '../../../../../shared/di/container-tokens' + +@UseCaseKey('GetAbsencesQry') +@singleton() +export class GetAbsencesQry extends Query { + constructor(@inject(ABSENCE_REPOSITORY) private absenceRepository: AbsenceRepository) { + super() + } + + internalExecute(absenceFilters: AbsenceFilters): Promise { + return this.absenceRepository.getAbsences(absenceFilters) + } +} diff --git a/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts b/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts index 2770aec5..f90b073f 100644 --- a/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts +++ b/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts @@ -1,7 +1,9 @@ import { AbsenceRepository } from '../domain/absence-repository' import { Absence } from '../domain/absence' import { AbsenceMother } from '../../../../../test-utils/mothers/absence-mother' +import { singleton } from 'tsyringe' +@singleton() export class FakeAbsenceRepository implements AbsenceRepository { async getAbsences(): Promise { return AbsenceMother.absences() diff --git a/src/shared/di/container-tokens.ts b/src/shared/di/container-tokens.ts index f5d6c21d..c58bcfe7 100644 --- a/src/shared/di/container-tokens.ts +++ b/src/shared/di/container-tokens.ts @@ -11,3 +11,4 @@ export const VERSION_REPOSITORY = Symbol('VERSION_REPOSITORY') export const ORGANIZATION_REPOSITORY = Symbol('ORGANIZATION_REPOSITORY') export const PROJECT_REPOSITORY = Symbol('PROJECT_REPOSITORY') export const PROJECT_ROLE_REPOSITORY = Symbol('PROJECT_ROLE_REPOSITORY') +export const ABSENCE_REPOSITORY = Symbol('ABSENCE_REPOSITORY') diff --git a/src/shared/di/container.ts b/src/shared/di/container.ts index 4ddaa26a..36e505bd 100644 --- a/src/shared/di/container.ts +++ b/src/shared/di/container.ts @@ -10,6 +10,7 @@ import { HttpUserRepository } from '../../features/shared/user/infrastructure/ht import { HttpVersionRepository } from '../../features/version/infrastructure/http-version-repository' import { container } from 'tsyringe' import { + ABSENCE_REPOSITORY, ACTIVITY_REPOSITORY, AUTH_REPOSITORY, HOLIDAY_REPOSITORY, @@ -26,6 +27,7 @@ import { } from './container-tokens' import { toast, ToastType } from '../notification/toast' import { HttpProjectRepository } from '../../features/shared/project/infrastructure/http-project-repository' +import { FakeAbsenceRepository } from '../../features/binnacle/features/availability/infrastructure/fake-absence-repository' container.register(STORAGE, { useValue: localStorage }) container.register(TOAST, { useValue: toast }) @@ -40,3 +42,4 @@ container.registerSingleton(PROJECT_ROLE_REPOSITORY, HttpProjectRoleRepository) container.registerSingleton(PROJECT_REPOSITORY, HttpProjectRepository) container.registerSingleton(ORGANIZATION_REPOSITORY, HttpOrganizationRepository) container.registerSingleton(ACTIVITY_REPOSITORY, HttpActivityRepository) +container.registerSingleton(ABSENCE_REPOSITORY, FakeAbsenceRepository) From b6a97f8bf610ed54716e75dea3ccb6e2be6e5653 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 13:08:01 +0200 Subject: [PATCH 38/99] feat: add absence qry to table --- .../ui/components/availability-table.tsx | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 76e21632..cfa67443 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -6,6 +6,7 @@ import { AvailabilityTableCellHeader } from './availability-table-cell-header' import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { GetHolidaysQry } from '../../../holiday/application/get-holidays-qry' import { AvailabilityTableCell } from './availability-table-cell' +import { GetAbsencesQry } from '../../application/get-absences-qry' export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() @@ -17,6 +18,14 @@ export const AvailabilityTable: FC = () => { } }, [selectedDate]) + const { result: absences } = useExecuteUseCaseOnMount(GetAbsencesQry, { + userId: 1, + organizationId: 1, + projectId: 1, + startDate: selectedDateInterval.startOfMonth, + endDate: selectedDateInterval.endOfMonth + }) + const daysOfMonth = chrono(selectedDateInterval.startOfMonth).eachDayUntil( selectedDateInterval.endOfMonth ) @@ -46,16 +55,18 @@ export const AvailabilityTable: FC = () => { const tableRows = ( - - Lorem ipsum dolor sit amet. - {daysOfMonth.map((day, index) => ( - - ))} - + {absences?.map((absence, index) => ( + + {absence.userName} + {daysOfMonth.map((day, index) => ( + + ))} + + ))} ) From 08b86fd8d43c2ef8d1780cab333b3240100f1f67 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 13:39:15 +0200 Subject: [PATCH 39/99] feat: add absence item to show absences --- .../ui/components/absence-item.tsx | 34 +++++++++++++++++++ .../ui/components/availability-table-cell.tsx | 25 ++++++++++---- .../ui/components/availability-table.tsx | 1 + 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 src/features/binnacle/features/availability/ui/components/absence-item.tsx diff --git a/src/features/binnacle/features/availability/ui/components/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/absence-item.tsx new file mode 100644 index 00000000..aac73352 --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/absence-item.tsx @@ -0,0 +1,34 @@ +import { Box } from '@chakra-ui/react' +import { FC } from 'react' + +interface Props { + duration: number + type: string +} + +export const AbsenceItem: FC = ({ duration, type }) => { + return ( + + {type} + + ) +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx index c7509fb2..ce61b818 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx @@ -1,21 +1,34 @@ import { FC } from 'react' import { Td, useColorModeValue } from '@chakra-ui/react' -import { isWeekend } from '../../../../../../shared/utils/chrono' +import { chrono, isWeekend } from '../../../../../../shared/utils/chrono' +import { Absence } from '../../domain/absence' +import { AbsenceItem } from './absence-item' -export const AvailabilityTableCell: FC<{ day: Date; isHoliday: boolean }> = ({ - day, - isHoliday -}) => { +interface Props { + day: Date + isHoliday: boolean + absence: Absence +} + +export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') + const isSameDay = () => chrono(day).isSameDay(absence.startDate) + const getDurationInDays = () => chrono(absence.endDate).diff(absence.startDate, 'day') + return ( - {day.getDate()} + {isSameDay() ? ( + + ) : ( + '' + )} ) } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index cfa67443..19171799 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -62,6 +62,7 @@ export const AvailabilityTable: FC = () => { ))} From 736861c222be23a8878873bc07bd5f6bf99da730 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 13:42:09 +0200 Subject: [PATCH 40/99] feat: update user mother data --- src/test-utils/mothers/absence-mother.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts index 5105d308..71a9b536 100644 --- a/src/test-utils/mothers/absence-mother.ts +++ b/src/test-utils/mothers/absence-mother.ts @@ -21,8 +21,8 @@ export class AbsenceMother { userId: 1, userName: 'Vacation user', type: 'VACATION', - startDate: parseISO('2023-01-01'), - endDate: parseISO('2023-01-02') + startDate: parseISO('2023-01-03'), + endDate: parseISO('2023-01-05') } } } From 2bb0816b502af8245059c3794aa0245e23c65778 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 21 Sep 2023 14:32:18 +0200 Subject: [PATCH 41/99] feat: add logic for absence item width and put interval with 5 days of difference --- .../ui/components/absence-item.tsx | 6 ++--- .../ui/components/availability-table-cell.tsx | 14 ++++++----- .../ui/components/availability-table.tsx | 24 ++++++++++--------- src/test-utils/mothers/absence-mother.ts | 2 +- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/absence-item.tsx index aac73352..965dba24 100644 --- a/src/features/binnacle/features/availability/ui/components/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/absence-item.tsx @@ -2,11 +2,11 @@ import { Box } from '@chakra-ui/react' import { FC } from 'react' interface Props { - duration: number + durationInDays: number type: string } -export const AbsenceItem: FC = ({ duration, type }) => { +export const AbsenceItem: FC = ({ durationInDays, type }) => { return ( = ({ duration, type }) => { overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" - width={`calc(${duration * 100}% - 1em)`} + width={`calc(${durationInDays * 100}% + 48px - 1em)`} border="none" display="flex" bgColor={'gray.400'} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx index ce61b818..3d1b4d09 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx @@ -1,5 +1,5 @@ import { FC } from 'react' -import { Td, useColorModeValue } from '@chakra-ui/react' +import { Box, Td, useColorModeValue } from '@chakra-ui/react' import { chrono, isWeekend } from '../../../../../../shared/utils/chrono' import { Absence } from '../../domain/absence' import { AbsenceItem } from './absence-item' @@ -24,11 +24,13 @@ export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => position={'relative'} backgroundColor={isWeekend(day) || isHoliday ? 'rgba(0, 0, 0, 0.10)' : ''} > - {isSameDay() ? ( - - ) : ( - '' - )} + + {isSameDay() ? ( + + ) : ( + '' + )} + ) } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 19171799..3bb4217b 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,6 +1,6 @@ import { FC, useMemo } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' -import { Table, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react' +import { Table, Tbody, Td, Text, Th, Thead, Tr } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' @@ -13,8 +13,8 @@ export const AvailabilityTable: FC = () => { const selectedDateInterval = useMemo(() => { return { - startOfMonth: chrono(selectedDate).startOf('month').getDate(), - endOfMonth: chrono(selectedDate).endOf('month').getDate() + start: chrono(selectedDate).startOf('month').minus(5, 'day').getDate(), + end: chrono(selectedDate).endOf('month').plus(5, 'day').getDate() } }, [selectedDate]) @@ -22,17 +22,15 @@ export const AvailabilityTable: FC = () => { userId: 1, organizationId: 1, projectId: 1, - startDate: selectedDateInterval.startOfMonth, - endDate: selectedDateInterval.endOfMonth + startDate: selectedDateInterval.start, + endDate: selectedDateInterval.end }) - const daysOfMonth = chrono(selectedDateInterval.startOfMonth).eachDayUntil( - selectedDateInterval.endOfMonth - ) + const daysOfMonth = chrono(selectedDateInterval.start).eachDayUntil(selectedDateInterval.end) const { result: holidays = [] } = useExecuteUseCaseOnMount(GetHolidaysQry, { - start: selectedDateInterval.startOfMonth, - end: selectedDateInterval.endOfMonth + start: selectedDateInterval.start, + end: selectedDateInterval.end }) const checkIfHoliday = (day: Date) => @@ -57,7 +55,11 @@ export const AvailabilityTable: FC = () => { {absences?.map((absence, index) => ( - {absence.userName} + + + {absence.userName} + + {daysOfMonth.map((day, index) => ( Date: Thu, 21 Sep 2023 17:11:55 +0200 Subject: [PATCH 42/99] feat: add logic to put absences in same row --- .../ui/components/availability-table-cell.tsx | 14 +++++---- .../ui/components/availability-table.tsx | 29 ++++++++++++++++--- src/test-utils/mothers/absence-mother.ts | 23 ++++++++++----- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx index 3d1b4d09..364849b7 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx @@ -7,14 +7,15 @@ import { AbsenceItem } from './absence-item' interface Props { day: Date isHoliday: boolean - absence: Absence + absence?: Absence } export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') - const isSameDay = () => chrono(day).isSameDay(absence.startDate) - const getDurationInDays = () => chrono(absence.endDate).diff(absence.startDate, 'day') + const isSameDay = (absenceDay: Date) => chrono(day).isSameDay(absenceDay) + const getDurationInDays = (absence: Absence) => + chrono(absence.endDate).diff(absence.startDate, 'day') return ( = ({ day, isHoliday, absence }) => backgroundColor={isWeekend(day) || isHoliday ? 'rgba(0, 0, 0, 0.10)' : ''} > - {isSameDay() ? ( - + {absence && isSameDay(absence.startDate) ? ( + ) : ( '' )} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 3bb4217b..04fea455 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,4 +1,4 @@ -import { FC, useMemo } from 'react' +import { FC, useEffect, useMemo, useState } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' import { Table, Tbody, Td, Text, Th, Thead, Tr } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' @@ -7,9 +7,17 @@ import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/us import { GetHolidaysQry } from '../../../holiday/application/get-holidays-qry' import { AvailabilityTableCell } from './availability-table-cell' import { GetAbsencesQry } from '../../application/get-absences-qry' +import { Absence } from '../../domain/absence' + +interface UserAbsences { + userId: number + userName: string + absences: Absence[] +} export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() + const [userAbsences, setUserAbsences] = useState([]) const selectedDateInterval = useMemo(() => { return { @@ -33,6 +41,19 @@ export const AvailabilityTable: FC = () => { end: selectedDateInterval.end }) + useEffect(() => { + const test = absences?.reduce((acc: { [key: number]: UserAbsences }, item: Absence) => { + const { userId, userName } = item + if (!acc[userId]) { + acc[userId] = { userId, userName, absences: [] } + } + acc[userId].absences.push(item) + return acc + }, {}) + + if (test !== undefined) setUserAbsences(Object.values(test)) + }, [absences]) + const checkIfHoliday = (day: Date) => holidays.some((holiday) => chrono(day).isSameDay(holiday.date)) @@ -53,18 +74,18 @@ export const AvailabilityTable: FC = () => { const tableRows = ( - {absences?.map((absence, index) => ( + {userAbsences?.map((userAbsence, index) => ( - {absence.userName} + {userAbsence.userName} {daysOfMonth.map((day, index) => ( chrono(day).isSameDay(x.startDate))} isHoliday={checkIfHoliday(day)} > ))} diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts index e677c3be..3a87b2fe 100644 --- a/src/test-utils/mothers/absence-mother.ts +++ b/src/test-utils/mothers/absence-mother.ts @@ -3,26 +3,35 @@ import { parseISO } from '../../shared/utils/chrono' export class AbsenceMother { static absences(): Absence[] { - return [this.paidLeaveAbsence(), this.vacationAbsence()] + return [ + this.paidLeaveAbsence(), + this.paidLeaveAbsence({ + startDate: parseISO('2023-09-05'), + endDate: parseISO('2023-09-10') + }), + this.vacationAbsence() + ] } - static paidLeaveAbsence(): Absence { + static paidLeaveAbsence(override?: Partial): Absence { return { userId: 1, userName: 'Paid leave user', type: 'PAID_LEAVE', - startDate: parseISO('2023-01-01'), - endDate: parseISO('2023-01-02') + startDate: parseISO('2023-09-01'), + endDate: parseISO('2023-09-02'), + ...override } } - static vacationAbsence(): Absence { + static vacationAbsence(override?: Partial): Absence { return { userId: 2, userName: 'Vacation user', type: 'VACATION', - startDate: parseISO('2023-01-03'), - endDate: parseISO('2023-01-05') + startDate: parseISO('2023-09-03'), + endDate: parseISO('2023-09-05'), + ...override } } } From f5c6fa90afbfd8032d50bbeb35d51418f70195cf Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 22 Sep 2023 11:36:57 +0200 Subject: [PATCH 43/99] feat: add logic styling to keep the names in sticky position --- .../ui/components/availability-table.module.css | 6 ++++++ .../availability/ui/components/availability-table.tsx | 8 +++++++- src/styles/misc.css | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table.module.css diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.module.css b/src/features/binnacle/features/availability/ui/components/availability-table.module.css new file mode 100644 index 00000000..b9f2043f --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table.module.css @@ -0,0 +1,6 @@ +.data-table tbody tr td:first-child { + position: sticky; + left: 0; + background: var(--bg-color); + z-index: 2; +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 04fea455..4bade227 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -8,6 +8,7 @@ import { GetHolidaysQry } from '../../../holiday/application/get-holidays-qry' import { AvailabilityTableCell } from './availability-table-cell' import { GetAbsencesQry } from '../../application/get-absences-qry' import { Absence } from '../../domain/absence' +import styles from './availability-table.module.css' interface UserAbsences { userId: number @@ -72,6 +73,11 @@ export const AvailabilityTable: FC = () => { ) + useEffect(() => { + const element = document.getElementById('is-today') + if (element !== null) element.scrollIntoView({ inline: 'center' }) + }, [selectedDate]) + const tableRows = ( {userAbsences?.map((userAbsence, index) => ( @@ -95,7 +101,7 @@ export const AvailabilityTable: FC = () => { ) return ( - +
{tableHeaders} {tableRows}
diff --git a/src/styles/misc.css b/src/styles/misc.css index 2ff491e0..4cf19606 100644 --- a/src/styles/misc.css +++ b/src/styles/misc.css @@ -3,6 +3,7 @@ --body-bg-color: var(--chakra-colors-gray-700); --private-holiday-color: var(--chakra-colors-blue-400); --public-holiday-color: var(--chakra-colors-yellow-400); + --bg-color: #1a202c; } .chakra-ui-light { @@ -10,6 +11,7 @@ --body-bg-color: white; --private-holiday-color: var(--chakra-colors-blue-400); --public-holiday-color: var(--chakra-colors-yellow-400); + --bg-color: white; } /* From 82f709313e8f01da5f0aeca1ef00b466f6336e81 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 22 Sep 2023 11:37:27 +0200 Subject: [PATCH 44/99] feat: update css and add logic to scroll to is-today --- .../ui/components/availability-table-cell-header.css | 2 +- .../ui/components/availability-table-cell-header.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css index 53bf902d..b5effd0a 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css @@ -1,4 +1,4 @@ -.is-today { +#is-today { border-radius: 50%; font-weight: bold; color: white; diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx index 61a12329..dbf1a01b 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx @@ -28,7 +28,7 @@ export const AvailabilityTableCellHeader: FC = ({ day, isHoliday }) => { > {weekDays[getWeekDay(day) - 1]} - + {day.getDate()} From fdae4f83de62cc1ab3f750f437e04fc407a3ee88 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 22 Sep 2023 11:54:23 +0200 Subject: [PATCH 45/99] feat: add translations --- src/shared/i18n/en.json | 4 ++++ src/shared/i18n/es.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index 4fa6730e..62424873 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -162,6 +162,10 @@ "open": "Open", "closed": "Closed" }, + "absences": { + "vacation": "Vacation", + "paidLeave": "Paid leave" + }, "actions": { "approve": "Approve", "save": "Save", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index 228679b6..22fe4e18 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -162,6 +162,10 @@ "open": "Abierto", "closed": "Cerrado" }, + "absences": { + "vacation": "Vacaciones", + "paidLeave": "Permiso" + }, "actions": { "approve": "Aprobar", "save": "Guardar", From 4ae31b2c2a6f9a0660612666a9ad98e499f548ae Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 22 Sep 2023 12:15:15 +0200 Subject: [PATCH 46/99] feat: add translations and update styling --- .../availability/ui/components/absence-item.tsx | 13 ++++++++++--- .../ui/components/availability-table.tsx | 6 ++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/absence-item.tsx index 965dba24..7d3abe7d 100644 --- a/src/features/binnacle/features/availability/ui/components/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/absence-item.tsx @@ -1,12 +1,19 @@ import { Box } from '@chakra-ui/react' import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { AbsenceType } from '../../domain/absence-type' interface Props { durationInDays: number - type: string + type: AbsenceType } export const AbsenceItem: FC = ({ durationInDays, type }) => { + const { t } = useTranslation() + + const getAbsenceTypeName = () => + type === 'VACATION' ? 'absences.vacation' : 'absences.paidLeave' + return ( = ({ durationInDays, type }) => { textOverflow="ellipsis" whiteSpace="nowrap" width={`calc(${durationInDays * 100}% + 48px - 1em)`} - border="none" + border="1px solid" display="flex" bgColor={'gray.400'} borderRadius="14px" @@ -28,7 +35,7 @@ export const AbsenceItem: FC = ({ durationInDays, type }) => { transform: 'translate(0, -50%)' }} > - {type} + {t(`${getAbsenceTypeName()}`)} ) } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table.tsx index 4bade227..ad3e7cdb 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table.tsx @@ -1,6 +1,6 @@ import { FC, useEffect, useMemo, useState } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' -import { Table, Tbody, Td, Text, Th, Thead, Tr } from '@chakra-ui/react' +import { Table, Tbody, Td, Text, Th, Thead, Tr, useColorModeValue } from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' @@ -58,6 +58,8 @@ export const AvailabilityTable: FC = () => { const checkIfHoliday = (day: Date) => holidays.some((holiday) => chrono(day).isSameDay(holiday.date)) + const borderColor = useColorModeValue('gray.300', 'gray.700') + const tableHeaders = ( @@ -82,7 +84,7 @@ export const AvailabilityTable: FC = () => { {userAbsences?.map((userAbsence, index) => ( - + {userAbsence.userName} From 7efc72de1b6a2642ed2738bc97830e63c7f541bf Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 22 Sep 2023 12:51:48 +0200 Subject: [PATCH 47/99] feat: move files --- .../availability/ui/availability-page.tsx | 16 +--------- .../{ => availability-table}/absence-item.tsx | 0 .../availability-table-cell-header.css | 0 .../availability-table-cell-header.tsx | 0 .../availability-table-cell.tsx | 4 +-- .../availability-table.module.css | 0 .../availability-table.tsx | 32 ++++++++++++++++--- 7 files changed, 30 insertions(+), 22 deletions(-) rename src/features/binnacle/features/availability/ui/components/{ => availability-table}/absence-item.tsx (100%) rename src/features/binnacle/features/availability/ui/components/{ => availability-table}/availability-table-cell-header.css (100%) rename src/features/binnacle/features/availability/ui/components/{ => availability-table}/availability-table-cell-header.tsx (100%) rename src/features/binnacle/features/availability/ui/components/{ => availability-table}/availability-table-cell.tsx (89%) rename src/features/binnacle/features/availability/ui/components/{ => availability-table}/availability-table.module.css (100%) rename src/features/binnacle/features/availability/ui/components/{ => availability-table}/availability-table.tsx (81%) diff --git a/src/features/binnacle/features/availability/ui/availability-page.tsx b/src/features/binnacle/features/availability/ui/availability-page.tsx index 1e89be17..b7b56326 100644 --- a/src/features/binnacle/features/availability/ui/availability-page.tsx +++ b/src/features/binnacle/features/availability/ui/availability-page.tsx @@ -2,8 +2,6 @@ import { FC } from 'react' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' import { useTranslation } from 'react-i18next' import { AvailabilityTable } from './components/availability-table' -import { Box, Flex } from '@chakra-ui/react' -import { CalendarControls } from '../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' import { CalendarProvider } from '../../activity/ui/contexts/calendar-context' const AvailabilityPage: FC = () => { @@ -12,19 +10,7 @@ const AvailabilityPage: FC = () => { return ( - - - - - - - + ) diff --git a/src/features/binnacle/features/availability/ui/components/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx similarity index 100% rename from src/features/binnacle/features/availability/ui/components/absence-item.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.css similarity index 100% rename from src/features/binnacle/features/availability/ui/components/availability-table-cell-header.css rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.css diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx similarity index 100% rename from src/features/binnacle/features/availability/ui/components/availability-table-cell-header.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx diff --git a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx similarity index 89% rename from src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx index 364849b7..2800322d 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx @@ -1,8 +1,8 @@ import { FC } from 'react' import { Box, Td, useColorModeValue } from '@chakra-ui/react' -import { chrono, isWeekend } from '../../../../../../shared/utils/chrono' -import { Absence } from '../../domain/absence' import { AbsenceItem } from './absence-item' +import { Absence } from '../../../domain/absence' +import { chrono, isWeekend } from '../../../../../../../shared/utils/chrono' interface Props { day: Date diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.module.css b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css similarity index 100% rename from src/features/binnacle/features/availability/ui/components/availability-table.module.css rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css diff --git a/src/features/binnacle/features/availability/ui/components/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx similarity index 81% rename from src/features/binnacle/features/availability/ui/components/availability-table.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index ad3e7cdb..551b4960 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -1,6 +1,17 @@ import { FC, useEffect, useMemo, useState } from 'react' import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' -import { Table, Tbody, Td, Text, Th, Thead, Tr, useColorModeValue } from '@chakra-ui/react' +import { + Box, + Flex, + Table, + Tbody, + Td, + Text, + Th, + Thead, + Tr, + useColorModeValue +} from '@chakra-ui/react' import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' @@ -9,6 +20,8 @@ import { AvailabilityTableCell } from './availability-table-cell' import { GetAbsencesQry } from '../../application/get-absences-qry' import { Absence } from '../../domain/absence' import styles from './availability-table.module.css' +import { AvailabilityTableFilters } from './availability-table-filters/availability-table-filters' +import { CalendarControls } from '../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' interface UserAbsences { userId: number @@ -103,9 +116,18 @@ export const AvailabilityTable: FC = () => { ) return ( - - {tableHeaders} - {tableRows} -
+ <> + + console.log(e)} /> + + + + + + {tableHeaders} + {tableRows} +
+
+ ) } From 46a0a5fa9a0568c1cdcdca55013b281ac46f120d Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 22 Sep 2023 13:44:41 +0200 Subject: [PATCH 48/99] feat: change organization combo prop --- .../project/ui/components/combos/projects-combos.tsx | 6 +++++- .../components/combos/activity-form-combos.tsx | 2 +- .../components/combos/organizations-combo.tsx | 10 ++++++---- .../ui/components/availability-table/absence-item.tsx | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx index da03aaa5..cfec4e6f 100644 --- a/src/features/administration/features/project/ui/components/combos/projects-combos.tsx +++ b/src/features/administration/features/project/ui/components/combos/projects-combos.tsx @@ -52,7 +52,11 @@ export const ProjectsFilterFormCombos: FC = (props) => { marginBottom={5} marginTop={4} > - + ) diff --git a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx index f8a0d6f8..02b81bc2 100644 --- a/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx +++ b/src/features/binnacle/features/activity/ui/components/activity-form/components/combos/activity-form-combos.tsx @@ -59,7 +59,7 @@ export const ActivityFormCombos = forwardRef( if (item?.name !== organization?.name) onOrganizationChange() }} isReadOnly={isReadOnly} - imputableOrganizations={true} + organizationFilters={{ imputable: true }} /> onChange?: (item: Organization) => void isReadOnly?: boolean - imputableOrganizations: boolean + organizationFilters: OrganizationFilters } export const OrganizationsCombo = forwardRef((props, ref) => { const { name = 'organization', control, onChange, isReadOnly } = props const { t } = useTranslation() - const { isLoading, result: organizations } = useExecuteUseCaseOnMount(GetOrganizationsQry, { - imputable: props.imputableOrganizations - }) + const { isLoading, result: organizations } = useExecuteUseCaseOnMount( + GetOrganizationsQry, + props.organizationFilters + ) return ( Date: Fri, 22 Sep 2023 13:48:22 +0200 Subject: [PATCH 49/99] feat: add user literals --- src/shared/i18n/en.json | 3 +++ src/shared/i18n/es.json | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index 62424873..5f5aca7b 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -82,6 +82,9 @@ "projects_filter": { "status": "Status" }, + "users_filter": { + "user": "User" + }, "activity": { "filter": "Filter", "create": "Create", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index 22fe4e18..c4ed484f 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -82,6 +82,9 @@ "projects_filter": { "status": "Estado" }, + "users_filter": { + "user": "Usuario" + }, "activity": { "filter": "Filtrar", "create": "Crear", From 18545269cfd6a4eac5e0faabe54a82b6240e4dde Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 08:55:05 +0200 Subject: [PATCH 50/99] feat: move files --- .../features/availability/ui/availability-page.tsx | 2 +- .../availability-table-cell-header.tsx | 4 ++-- .../availability-table/availability-table.tsx | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/availability-page.tsx b/src/features/binnacle/features/availability/ui/availability-page.tsx index b7b56326..b81884f4 100644 --- a/src/features/binnacle/features/availability/ui/availability-page.tsx +++ b/src/features/binnacle/features/availability/ui/availability-page.tsx @@ -1,8 +1,8 @@ import { FC } from 'react' import { PageWithTitle } from '../../../../../shared/components/page-with-title/page-with-title' import { useTranslation } from 'react-i18next' -import { AvailabilityTable } from './components/availability-table' import { CalendarProvider } from '../../activity/ui/contexts/calendar-context' +import { AvailabilityTable } from './components/availability-table/availability-table' const AvailabilityPage: FC = () => { const { t } = useTranslation() diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx index dbf1a01b..ae39569f 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx @@ -1,9 +1,9 @@ import { FC } from 'react' import { Box, Text, Th, useColorModeValue } from '@chakra-ui/react' -import { chrono, isWeekend } from '../../../../../../shared/utils/chrono' -import { getWeekdaysName } from '../../../activity/utils/get-weekdays-name' import { isToday } from 'date-fns' import './availability-table-cell-header.css' +import { getWeekdaysName } from '../../../../activity/utils/get-weekdays-name' +import { chrono, isWeekend } from '../../../../../../../shared/utils/chrono' interface Props { day: Date diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 551b4960..9e888935 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -1,5 +1,4 @@ import { FC, useEffect, useMemo, useState } from 'react' -import { useCalendarContext } from '../../../activity/ui/contexts/calendar-context' import { Box, Flex, @@ -12,16 +11,17 @@ import { Tr, useColorModeValue } from '@chakra-ui/react' -import { chrono } from '../../../../../../shared/utils/chrono' import { AvailabilityTableCellHeader } from './availability-table-cell-header' -import { useExecuteUseCaseOnMount } from '../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' -import { GetHolidaysQry } from '../../../holiday/application/get-holidays-qry' import { AvailabilityTableCell } from './availability-table-cell' -import { GetAbsencesQry } from '../../application/get-absences-qry' -import { Absence } from '../../domain/absence' import styles from './availability-table.module.css' import { AvailabilityTableFilters } from './availability-table-filters/availability-table-filters' -import { CalendarControls } from '../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' +import { Absence } from '../../../domain/absence' +import { chrono } from '../../../../../../../shared/utils/chrono' +import { useExecuteUseCaseOnMount } from '../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' +import { GetAbsencesQry } from '../../../application/get-absences-qry' +import { GetHolidaysQry } from '../../../../holiday/application/get-holidays-qry' +import { CalendarControls } from '../../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' +import { useCalendarContext } from '../../../../activity/ui/contexts/calendar-context' interface UserAbsences { userId: number From 65f6e00eb197c968f9ee1e42280383474d4af677 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 08:55:23 +0200 Subject: [PATCH 51/99] feat: add availability table filters --- .../availability-table-filters.tsx | 30 ++++++++++++++ .../user-filter/user-filter.tsx | 41 +++++++++++++++++++ .../components/form-fields/combo-field.tsx | 2 + 3 files changed, 73 insertions(+) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx new file mode 100644 index 00000000..51d73860 --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx @@ -0,0 +1,30 @@ +import { Flex } from '@chakra-ui/react' +import { FC } from 'react' +import { UserInfo } from '../../../../../../../shared/user/domain/user-info' +import { AbsenceFilters } from '../../../../domain/absence-filters' +import { OrganizationsCombo } from '../../../../../activity/ui/components/activity-form/components/combos/organizations-combo' +import { ProjectsCombo } from '../../../../../activity/ui/components/activity-form/components/combos/projects-combo' +import { useForm } from 'react-hook-form' +import { UserFilter } from './user-filter/user-filter' + +interface Props { + onChange: (params: Partial) => void +} + +export const AvailabilityTableFilters: FC = ({ onChange }) => { + const { control } = useForm() + const handleChange = (params: Partial) => onChange(params) + + return ( + + + + handleChange({ userId: userInfo?.id })} + > + + ) +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx new file mode 100644 index 00000000..d71e17b2 --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx @@ -0,0 +1,41 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' +import { useForm } from 'react-hook-form' +import { useExecuteUseCaseOnMount } from '../../../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' +import { GetUsersListQry } from '../../../../../../../../shared/user/application/get-users-list-qry' +import { UserInfo } from '../../../../../../../../shared/user/domain/user-info' +import { ComboField } from '../../../../../../../../../shared/components/form-fields/combo-field' + +interface Props { + onChange: (user: UserInfo) => void +} + +export const UserFilter: FC = (props) => { + const { t } = useTranslation() + const { control } = useForm() + const { isLoading, result: users } = useExecuteUseCaseOnMount(GetUsersListQry, { active: true }) + + const handleChange = (user: UserInfo) => { + props.onChange(user) + } + + const handleInputChange = (event: any) => { + if (event.target.value.length > 3) { + console.log(event.target.value) + } + } + + return ( + + ) +} diff --git a/src/shared/components/form-fields/combo-field.tsx b/src/shared/components/form-fields/combo-field.tsx index 21812e63..b183388f 100644 --- a/src/shared/components/form-fields/combo-field.tsx +++ b/src/shared/components/form-fields/combo-field.tsx @@ -12,6 +12,7 @@ interface Props extends InputProps { items: any[] isLoading: boolean onChange?: (value: any) => void + onInputChange?: (value: any) => void isDisabled: boolean } @@ -47,6 +48,7 @@ export const ComboField = forwardRef( Date: Mon, 25 Sep 2023 09:18:05 +0200 Subject: [PATCH 52/99] feat: update user filter params --- .../shared/user/application/get-users-list-qry.ts | 13 ++++--------- src/features/shared/user/domain/user-filters.ts | 8 ++++++++ src/features/shared/user/domain/user-repository.ts | 4 ++-- .../infrastructure/http-user-repository.test.ts | 5 ++++- .../user/infrastructure/http-user-repository.ts | 6 +++--- 5 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 src/features/shared/user/domain/user-filters.ts diff --git a/src/features/shared/user/application/get-users-list-qry.ts b/src/features/shared/user/application/get-users-list-qry.ts index e4f93843..fd5354f5 100644 --- a/src/features/shared/user/application/get-users-list-qry.ts +++ b/src/features/shared/user/application/get-users-list-qry.ts @@ -3,21 +3,16 @@ import { USER_REPOSITORY } from '../../../../shared/di/container-tokens' import { inject, singleton } from 'tsyringe' import type { UserRepository } from '../domain/user-repository' import { UserInfo } from '../domain/user-info' -import { Id } from '../../../../shared/types/id' - -interface GetUsersParams { - ids?: Id[] - active?: boolean -} +import { UserFilters } from '../domain/user-filters' @UseCaseKey('GetUsersListQry') @singleton() -export class GetUsersListQry extends Query { +export class GetUsersListQry extends Query { constructor(@inject(USER_REPOSITORY) private userRepository: UserRepository) { super() } - internalExecute(params: GetUsersParams): Promise { - return this.userRepository.getUsers(params?.ids, params?.active) + internalExecute(filters: UserFilters): Promise { + return this.userRepository.getUsers(filters) } } diff --git a/src/features/shared/user/domain/user-filters.ts b/src/features/shared/user/domain/user-filters.ts new file mode 100644 index 00000000..6e7092db --- /dev/null +++ b/src/features/shared/user/domain/user-filters.ts @@ -0,0 +1,8 @@ +import { Id } from '../../../../shared/types/id' + +export interface UserFilters { + ids?: Id[] + active?: boolean + nameLike?: string + limit?: number +} diff --git a/src/features/shared/user/domain/user-repository.ts b/src/features/shared/user/domain/user-repository.ts index 07ebe79f..fcd4fb7d 100644 --- a/src/features/shared/user/domain/user-repository.ts +++ b/src/features/shared/user/domain/user-repository.ts @@ -1,9 +1,9 @@ import { User } from './user' import { UserInfo } from './user-info' -import { Id } from '../../../../shared/types/id' +import { UserFilters } from './user-filters' export interface UserRepository { getUser(): Promise - getUsers(ids?: Id[], active?: boolean): Promise + getUsers(filters?: UserFilters): Promise } diff --git a/src/features/shared/user/infrastructure/http-user-repository.test.ts b/src/features/shared/user/infrastructure/http-user-repository.test.ts index 6cf00f4f..1a6c9e46 100644 --- a/src/features/shared/user/infrastructure/http-user-repository.test.ts +++ b/src/features/shared/user/infrastructure/http-user-repository.test.ts @@ -44,7 +44,10 @@ describe('UserRepository', () => { httpClient.get.mockResolvedValue(UserMother.userList()) - const result = await userRepository.getUsers([1, 2], true) + const result = await userRepository.getUsers({ + ids: [1, 2], + active: true + }) expect(httpClient.get).toHaveBeenCalledWith('/api/user', { params: { diff --git a/src/features/shared/user/infrastructure/http-user-repository.ts b/src/features/shared/user/infrastructure/http-user-repository.ts index 065dcf4f..389017ee 100644 --- a/src/features/shared/user/infrastructure/http-user-repository.ts +++ b/src/features/shared/user/infrastructure/http-user-repository.ts @@ -4,7 +4,7 @@ import { AnonymousUserError } from '../domain/anonymous-user-error' import { UserRepository } from '../domain/user-repository' import { User } from '../domain/user' import { UserInfo } from '../domain/user-info' -import { Id } from '../../../../shared/types/id' +import { UserFilters } from '../domain/user-filters' @singleton() export class HttpUserRepository implements UserRepository { @@ -25,10 +25,10 @@ export class HttpUserRepository implements UserRepository { } } - async getUsers(ids?: Id[], active?: boolean): Promise { + async getUsers(filters?: UserFilters): Promise { try { return await this.httpClient.get(HttpUserRepository.usersPath, { - params: { ids, active } + params: filters }) } catch (error) { if (error.response?.status === 404 || error.response?.status === 401) { From 135e3d335f22ef4964fa8b834ac1019925f5118f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 10:32:47 +0200 Subject: [PATCH 53/99] feat: add debounce time to make get users list query based on input value --- .../user-filter/user-filter.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx index d71e17b2..e56376ba 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/user-filter/user-filter.tsx @@ -13,16 +13,29 @@ interface Props { export const UserFilter: FC = (props) => { const { t } = useTranslation() const { control } = useForm() - const { isLoading, result: users } = useExecuteUseCaseOnMount(GetUsersListQry, { active: true }) + const { + isLoading, + result: users, + executeUseCase: getUsersListQry + } = useExecuteUseCaseOnMount(GetUsersListQry, { + active: true, + limit: 100 + }) const handleChange = (user: UserInfo) => { props.onChange(user) } const handleInputChange = (event: any) => { - if (event.target.value.length > 3) { - console.log(event.target.value) - } + const timeoutId = setTimeout(async () => { + await getUsersListQry({ + active: true, + limit: 100, + nameLike: event.target.value + }) + + return () => clearTimeout(timeoutId) + }, 2000) } return ( From 1eec2af1ae6bec33cefbcc2c553f84927ec57064 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 13:38:23 +0200 Subject: [PATCH 54/99] feat: add filters schema and emit when input changes --- .../availability-table-filters.schema.ts | 9 ++++++ .../availability-table-filters.tsx | 28 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.schema.ts diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.schema.ts b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.schema.ts new file mode 100644 index 00000000..b9c6548d --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.schema.ts @@ -0,0 +1,9 @@ +import { UserInfo } from '../../../../../../../shared/user/domain/user-info' +import { Project } from '../../../../../../../shared/project/domain/project' +import { Organization } from '../../../../../organization/domain/organization' + +export interface AvailabilityTableFiltersSchema { + organization: Organization + project: Project + user: UserInfo +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx index 51d73860..2ac956e6 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx @@ -1,27 +1,47 @@ import { Flex } from '@chakra-ui/react' -import { FC } from 'react' +import { FC, useMemo } from 'react' import { UserInfo } from '../../../../../../../shared/user/domain/user-info' import { AbsenceFilters } from '../../../../domain/absence-filters' import { OrganizationsCombo } from '../../../../../activity/ui/components/activity-form/components/combos/organizations-combo' import { ProjectsCombo } from '../../../../../activity/ui/components/activity-form/components/combos/projects-combo' -import { useForm } from 'react-hook-form' +import { useForm, useWatch } from 'react-hook-form' import { UserFilter } from './user-filter/user-filter' +import { AvailabilityTableFiltersSchema } from './availability-table-filters.schema' interface Props { onChange: (params: Partial) => void } export const AvailabilityTableFilters: FC = ({ onChange }) => { - const { control } = useForm() + const { control } = useForm() + const [organization] = useWatch({ + control, + name: ['organization'] + }) + + const projectDisabled = useMemo(() => organization === undefined, [organization]) + const handleChange = (params: Partial) => onChange(params) return ( { + handleChange({ organizationId: item?.id }) + }} + /> + { + handleChange({ projectId: item?.id }) + }} + isDisabled={projectDisabled} /> - handleChange({ userId: userInfo?.id })} > From e5c313fc783885dc6e94715c5b4f67e311798911 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 13:39:22 +0200 Subject: [PATCH 55/99] feat: make absence when filter changes --- .../availability/domain/absence-filters.ts | 6 +- .../availability-table/availability-table.tsx | 72 ++++++++++++------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/features/binnacle/features/availability/domain/absence-filters.ts b/src/features/binnacle/features/availability/domain/absence-filters.ts index 74b9ef57..f1db8cb3 100644 --- a/src/features/binnacle/features/availability/domain/absence-filters.ts +++ b/src/features/binnacle/features/availability/domain/absence-filters.ts @@ -1,9 +1,9 @@ import { Id } from '../../../../../shared/types/id' export interface AbsenceFilters { - userId: Id - organizationId: Id - projectId: Id + userId?: Id + organizationId?: Id + projectId?: Id startDate: Date endDate: Date } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 9e888935..edfdbadb 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -22,6 +22,8 @@ import { GetAbsencesQry } from '../../../application/get-absences-qry' import { GetHolidaysQry } from '../../../../holiday/application/get-holidays-qry' import { CalendarControls } from '../../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' import { useCalendarContext } from '../../../../activity/ui/contexts/calendar-context' +import { AbsenceFilters } from '../../../domain/absence-filters' +import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' interface UserAbsences { userId: number @@ -32,6 +34,10 @@ interface UserAbsences { export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() const [userAbsences, setUserAbsences] = useState([]) + const [absenceFilters, setAbsenceFilters] = useState({ + startDate: new Date(), + endDate: new Date() + }) const selectedDateInterval = useMemo(() => { return { @@ -39,14 +45,7 @@ export const AvailabilityTable: FC = () => { end: chrono(selectedDate).endOf('month').plus(5, 'day').getDate() } }, [selectedDate]) - - const { result: absences } = useExecuteUseCaseOnMount(GetAbsencesQry, { - userId: 1, - organizationId: 1, - projectId: 1, - startDate: selectedDateInterval.start, - endDate: selectedDateInterval.end - }) + const { useCase: getAbsencesQry } = useGetUseCase(GetAbsencesQry) const daysOfMonth = chrono(selectedDateInterval.start).eachDayUntil(selectedDateInterval.end) @@ -56,23 +55,42 @@ export const AvailabilityTable: FC = () => { }) useEffect(() => { - const test = absences?.reduce((acc: { [key: number]: UserAbsences }, item: Absence) => { - const { userId, userName } = item - if (!acc[userId]) { - acc[userId] = { userId, userName, absences: [] } - } - acc[userId].absences.push(item) - return acc - }, {}) + if (absenceFilters.organizationId !== undefined) { + getAbsencesQry + .execute({ + ...absenceFilters, + startDate: selectedDateInterval.start, + endDate: selectedDateInterval.end + }) + .then((absences) => { + const userAbsencesObject = absences?.reduce( + (acc: { [key: number]: UserAbsences }, item: Absence) => { + const { userId, userName } = item + if (!acc[userId]) { + acc[userId] = { userId, userName, absences: [] } + } + acc[userId].absences.push(item) + return acc + }, + {} + ) - if (test !== undefined) setUserAbsences(Object.values(test)) - }, [absences]) + if (userAbsencesObject !== undefined) setUserAbsences(Object.values(userAbsencesObject)) + }) + } else { + setUserAbsences([]) + } + }, [absenceFilters]) const checkIfHoliday = (day: Date) => holidays.some((holiday) => chrono(day).isSameDay(holiday.date)) const borderColor = useColorModeValue('gray.300', 'gray.700') + const onFilterChange = (updatedFilter: Partial) => { + setAbsenceFilters({ ...absenceFilters, ...updatedFilter }) + } + const tableHeaders = ( @@ -118,16 +136,20 @@ export const AvailabilityTable: FC = () => { return ( <> - console.log(e)} /> + - - - {tableHeaders} - {tableRows} -
-
+ {userAbsences.length === 0 ? ( + Empty + ) : ( + + + {tableHeaders} + {tableRows} +
+
+ )} ) } From b13967378dcaee1dce29759f3f27c6cf08f81502 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 15:21:33 +0200 Subject: [PATCH 56/99] fix: issue with test and new props --- .../organization/application/get-organizations-qry.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/binnacle/features/organization/application/get-organizations-qry.test.ts b/src/features/binnacle/features/organization/application/get-organizations-qry.test.ts index 9e1fa711..99f7c76a 100644 --- a/src/features/binnacle/features/organization/application/get-organizations-qry.test.ts +++ b/src/features/binnacle/features/organization/application/get-organizations-qry.test.ts @@ -9,7 +9,7 @@ describe('GetHolidaysQry', () => { const organizations = OrganizationMother.organizations() organizationRepository.getAll.mockResolvedValue(organizations) - const response = await getOrganizationQry.internalExecute() + const response = await getOrganizationQry.internalExecute({ imputable: true }) expect(organizationRepository.getAll).toHaveBeenCalled() expect(response).toEqual(organizations) From 841735b9b6d196fe2731a0d5fbeae9ccf3caa75f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 15:22:14 +0200 Subject: [PATCH 57/99] feat: add new vacation qry to get holidays by year --- .../application/get-holidays-by-year-qry.ts | 17 +++++++++++++++++ .../holiday/domain/holiday-repository.ts | 2 ++ .../infrastructure/fake-holiday-repository.ts | 4 ++++ .../infrastructure/http-holiday-repository.ts | 15 +++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 src/features/binnacle/features/holiday/application/get-holidays-by-year-qry.ts diff --git a/src/features/binnacle/features/holiday/application/get-holidays-by-year-qry.ts b/src/features/binnacle/features/holiday/application/get-holidays-by-year-qry.ts new file mode 100644 index 00000000..fc8e6735 --- /dev/null +++ b/src/features/binnacle/features/holiday/application/get-holidays-by-year-qry.ts @@ -0,0 +1,17 @@ +import { Query, UseCaseKey } from '@archimedes/arch' +import { inject, singleton } from 'tsyringe' +import { Holiday } from '../domain/holiday' +import { HOLIDAY_REPOSITORY } from '../../../../../shared/di/container-tokens' +import type { HolidayRepository } from '../domain/holiday-repository' + +@UseCaseKey('GetHolidayByYearQry') +@singleton() +export class GetHolidaysByYearQry extends Query { + constructor(@inject(HOLIDAY_REPOSITORY) private holidayRepository: HolidayRepository) { + super() + } + + internalExecute(year: number): Promise { + return this.holidayRepository.getHolidaysByYear(year) + } +} diff --git a/src/features/binnacle/features/holiday/domain/holiday-repository.ts b/src/features/binnacle/features/holiday/domain/holiday-repository.ts index 66eeffd7..5c0bbfbe 100644 --- a/src/features/binnacle/features/holiday/domain/holiday-repository.ts +++ b/src/features/binnacle/features/holiday/domain/holiday-repository.ts @@ -3,4 +3,6 @@ import { Holiday } from './holiday' export interface HolidayRepository { getAll(interval: DateInterval): Promise + + getHolidaysByYear(year: number): Promise } diff --git a/src/features/binnacle/features/holiday/infrastructure/fake-holiday-repository.ts b/src/features/binnacle/features/holiday/infrastructure/fake-holiday-repository.ts index 61c8ec5f..952640ee 100644 --- a/src/features/binnacle/features/holiday/infrastructure/fake-holiday-repository.ts +++ b/src/features/binnacle/features/holiday/infrastructure/fake-holiday-repository.ts @@ -8,4 +8,8 @@ export class FakeHolidayRepository implements HolidayRepository { async getAll(): Promise { return HolidayMother.holidays() } + + async getHolidaysByYear(): Promise { + return HolidayMother.holidays() + } } diff --git a/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts b/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts index e717c911..dbb4e7f6 100644 --- a/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts +++ b/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts @@ -9,6 +9,7 @@ import { HolidayRepository } from '../domain/holiday-repository' @singleton() export class HttpHolidayRepository implements HolidayRepository { protected static holidayPath = '/api/holidays' + protected static newHolidayPath = '/api/holiday' constructor(private httpClient: HttpClient) {} @@ -27,4 +28,18 @@ export class HttpHolidayRepository implements HolidayRepository { return holidays.map((holiday) => ({ ...holiday, date: new Date(holiday.date) })) } + + async getHolidaysByYear(year: number): Promise { + const data = await this.httpClient.get>( + HttpHolidayRepository.newHolidayPath, + { + params: { + year + } + } + ) + const { holidays } = data + + return holidays.map((holiday) => ({ ...holiday, date: new Date(holiday.date) })) + } } From bdac3247bb7e75eca2c37316e300b5f1db9bed80 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 15:56:48 +0200 Subject: [PATCH 58/99] feat: update availability table with new holidays query --- .../availability-table/availability-table.tsx | 10 +++++----- .../infrastructure/http-holiday-repository.ts | 14 ++++---------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index edfdbadb..b867d36c 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -19,11 +19,11 @@ import { Absence } from '../../../domain/absence' import { chrono } from '../../../../../../../shared/utils/chrono' import { useExecuteUseCaseOnMount } from '../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { GetAbsencesQry } from '../../../application/get-absences-qry' -import { GetHolidaysQry } from '../../../../holiday/application/get-holidays-qry' import { CalendarControls } from '../../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' import { useCalendarContext } from '../../../../activity/ui/contexts/calendar-context' import { AbsenceFilters } from '../../../domain/absence-filters' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' +import { GetHolidaysByYearQry } from '../../../../holiday/application/get-holidays-by-year-qry' interface UserAbsences { userId: number @@ -49,10 +49,10 @@ export const AvailabilityTable: FC = () => { const daysOfMonth = chrono(selectedDateInterval.start).eachDayUntil(selectedDateInterval.end) - const { result: holidays = [] } = useExecuteUseCaseOnMount(GetHolidaysQry, { - start: selectedDateInterval.start, - end: selectedDateInterval.end - }) + const { result: holidays = [] } = useExecuteUseCaseOnMount( + GetHolidaysByYearQry, + selectedDateInterval.start.getFullYear() + ) useEffect(() => { if (absenceFilters.organizationId !== undefined) { diff --git a/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts b/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts index dbb4e7f6..4affc22e 100644 --- a/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts +++ b/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts @@ -30,16 +30,10 @@ export class HttpHolidayRepository implements HolidayRepository { } async getHolidaysByYear(year: number): Promise { - const data = await this.httpClient.get>( - HttpHolidayRepository.newHolidayPath, - { - params: { - year - } + return await this.httpClient.get(HttpHolidayRepository.newHolidayPath, { + params: { + year } - ) - const { holidays } = data - - return holidays.map((holiday) => ({ ...holiday, date: new Date(holiday.date) })) + }) } } From 98343617a68db3c6e0ed1090f7b7a918f8686eb8 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 25 Sep 2023 16:03:25 +0200 Subject: [PATCH 59/99] feat: add empty message to table --- .../ui/components/availability-table/availability-table.tsx | 4 +++- src/shared/i18n/en.json | 3 ++- src/shared/i18n/es.json | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index b867d36c..aaecc5e6 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -24,6 +24,7 @@ import { useCalendarContext } from '../../../../activity/ui/contexts/calendar-co import { AbsenceFilters } from '../../../domain/absence-filters' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' import { GetHolidaysByYearQry } from '../../../../holiday/application/get-holidays-by-year-qry' +import { useTranslation } from 'react-i18next' interface UserAbsences { userId: number @@ -38,6 +39,7 @@ export const AvailabilityTable: FC = () => { startDate: new Date(), endDate: new Date() }) + const { t } = useTranslation() const selectedDateInterval = useMemo(() => { return { @@ -141,7 +143,7 @@ export const AvailabilityTable: FC = () => {
{userAbsences.length === 0 ? ( - Empty + {t('absences.emptyMessage')} ) : ( diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index 5f5aca7b..ac8b3ff8 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -167,7 +167,8 @@ }, "absences": { "vacation": "Vacation", - "paidLeave": "Paid leave" + "paidLeave": "Paid leave", + "emptyMessage": "It is necessary to filter by organization to obtain availability." }, "actions": { "approve": "Approve", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index c4ed484f..7df28a4d 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -167,7 +167,8 @@ }, "absences": { "vacation": "Vacaciones", - "paidLeave": "Permiso" + "paidLeave": "Permiso", + "emptyMessage": "Es necesario filtrar por organización para obtener la disponibilidad." }, "actions": { "approve": "Aprobar", From e928b14a9967d1bfc5a449db6cf2d3c3e4f554ed Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 10:49:47 +0200 Subject: [PATCH 60/99] feat: update availability repository --- .../holiday/infrastructure/http-holiday-repository.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts b/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts index 4affc22e..a0894fd2 100644 --- a/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts +++ b/src/features/binnacle/features/holiday/infrastructure/http-holiday-repository.ts @@ -30,10 +30,12 @@ export class HttpHolidayRepository implements HolidayRepository { } async getHolidaysByYear(year: number): Promise { - return await this.httpClient.get(HttpHolidayRepository.newHolidayPath, { + const data = await this.httpClient.get(HttpHolidayRepository.newHolidayPath, { params: { year } }) + + return data.map((holiday) => ({ ...holiday, date: new Date(holiday.date) })) } } From 7a83d4b42734fde0854d97917337d00b332c1155 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 12:58:56 +0200 Subject: [PATCH 61/99] feat: add new absence to mother --- src/test-utils/mothers/absence-mother.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts index 3a87b2fe..fd1b2e8b 100644 --- a/src/test-utils/mothers/absence-mother.ts +++ b/src/test-utils/mothers/absence-mother.ts @@ -6,7 +6,7 @@ export class AbsenceMother { return [ this.paidLeaveAbsence(), this.paidLeaveAbsence({ - startDate: parseISO('2023-09-05'), + startDate: parseISO('2023-09-02'), endDate: parseISO('2023-09-10') }), this.vacationAbsence() From 958ebddfa3fbcea3848c456dcc46e0f4d0d95c13 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 13:03:08 +0200 Subject: [PATCH 62/99] feat: add background to activity table --- .../components/availability-table/availability-table.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css index b9f2043f..505d9a4a 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css @@ -1,3 +1,4 @@ +.data-table thead tr th:first-child, .data-table tbody tr td:first-child { position: sticky; left: 0; From 8c303eeaa1bd12ade82ee6d7b6f2a7054e8c9cc7 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 15:25:21 +0200 Subject: [PATCH 63/99] feat: add logic to print absences with days in start date or end date outside of interval --- .../availability-table/absence-item.tsx | 42 +++++++++++++++---- .../availability-table-cell.tsx | 18 ++++---- .../availability-table/availability-table.tsx | 24 ++++++++++- 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx index b3733790..21ab018b 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx @@ -1,18 +1,43 @@ import { Box } from '@chakra-ui/react' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { AbsenceType } from '../../../domain/absence-type' +import { Absence } from '../../../domain/absence' +import { chrono } from '../../../../../../../shared/utils/chrono' interface Props { - durationInDays: number - type: AbsenceType + absence: Absence + situation: string + interval: { start: Date; end: Date } } -export const AbsenceItem: FC = ({ durationInDays, type }) => { +export const AbsenceItem: FC = ({ absence, interval, situation }) => { const { t } = useTranslation() + const getDurationInDays = () => { + const duration = chrono(absence.endDate).diff(absence.startDate, 'day') + + if (absence.endDate > interval.end) { + const diff = chrono(absence.endDate).diff(interval.end, 'day') + return `calc(${(duration - diff) * 100}% - 20px)` + } + + return `calc(${duration * 100}% + 48px)` + } + + const getBorderRadius = () => { + if (absence.endDate > interval.end) { + return '14px 0 0 14px ' + } + + if (situation === 'start') { + return '0 14px 14px 0' + } + + return '14px' + } + const getAbsenceTypeName = () => - type === 'VACATION' ? 'absences.vacation' : 'absences.paidLeave' + absence.type === 'VACATION' ? 'absences.vacation' : 'absences.paidLeave' return ( = ({ durationInDays, type }) => { overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" - width={`calc(${durationInDays * 100}% + 48px - 1em)`} + width={getDurationInDays()} border="1px solid" display="flex" bgColor={'gray.400'} - borderRadius="14px" + borderRadius={getBorderRadius()} zIndex={1} style={{ position: 'absolute', top: '50%', - transform: 'translate(0, -50%)' + transform: 'translate(0, -50%)', + ...(situation === 'start' && { left: 0 }) }} > {t(`${getAbsenceTypeName()}`)} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx index 2800322d..24c732ad 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx @@ -2,21 +2,18 @@ import { FC } from 'react' import { Box, Td, useColorModeValue } from '@chakra-ui/react' import { AbsenceItem } from './absence-item' import { Absence } from '../../../domain/absence' -import { chrono, isWeekend } from '../../../../../../../shared/utils/chrono' +import { isWeekend } from '../../../../../../../shared/utils/chrono' interface Props { day: Date isHoliday: boolean - absence?: Absence + absence?: Absence & { situation: string } + interval: { start: Date; end: Date } } -export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => { +export const AvailabilityTableCell: FC = ({ day, isHoliday, absence, interval }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') - const isSameDay = (absenceDay: Date) => chrono(day).isSameDay(absenceDay) - const getDurationInDays = (absence: Absence) => - chrono(absence.endDate).diff(absence.startDate, 'day') - return ( {userAbsences?.map((userAbsence, index) => ( @@ -126,8 +143,11 @@ export const AvailabilityTable: FC = () => { chrono(day).isSameDay(x.startDate))} + absence={updateAbsencesBasedOnInterval(userAbsence).find((x) => + chrono(day).isSameDay(x.startDate) + )} isHoliday={checkIfHoliday(day)} + interval={selectedDateInterval} > ))} From 9ae0418b221b38068de72358246242bb43cfa6ed Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 15:27:49 +0200 Subject: [PATCH 64/99] feat: update styling --- .../ui/components/availability-table/absence-item.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx index 21ab018b..0002c059 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx @@ -21,7 +21,9 @@ export const AbsenceItem: FC = ({ absence, interval, situation }) => { return `calc(${(duration - diff) * 100}% - 20px)` } - return `calc(${duration * 100}% + 48px)` + return situation === 'start' + ? `calc(${duration * 100}% + 72px)` + : `calc(${duration * 100}% + 48px)` } const getBorderRadius = () => { From 006e7fa6b37c4ccc538bce098dad80101444a4bb Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 15:36:22 +0200 Subject: [PATCH 65/99] feat: create chrono function --- .../components/availability-table/availability-table.tsx | 7 +++---- src/shared/utils/chrono.ts | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 0a2c3332..d95155c6 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -25,7 +25,6 @@ import { AbsenceFilters } from '../../../domain/absence-filters' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' import { GetHolidaysByYearQry } from '../../../../holiday/application/get-holidays-by-year-qry' import { useTranslation } from 'react-i18next' -import { isWithinInterval } from 'date-fns' interface UserAbsences { userId: number @@ -118,13 +117,13 @@ export const AvailabilityTable: FC = () => { const absencesWithStartDateOutside = userAbsence.absences .filter( (x) => - !isWithinInterval(x.startDate, selectedDateInterval) && - isWithinInterval(x.endDate, selectedDateInterval) + !chrono(x.startDate).isDateWithinInterval(selectedDateInterval) && + chrono(x.endDate).isDateWithinInterval(selectedDateInterval) ) .map((x) => ({ ...x, startDate: selectedDateInterval.start, situation: 'start' })) const absencesWithStartDateNotOutside = userAbsence.absences - .filter((x) => isWithinInterval(x.startDate, selectedDateInterval)) + .filter((x) => chrono(x.startDate).isDateWithinInterval(selectedDateInterval)) .map((x) => ({ ...x, situation: 'normal' })) return [...absencesWithStartDateOutside, ...absencesWithStartDateNotOutside] diff --git a/src/shared/utils/chrono.ts b/src/shared/utils/chrono.ts index 0e1a1ab9..6cf72ecb 100644 --- a/src/shared/utils/chrono.ts +++ b/src/shared/utils/chrono.ts @@ -1,4 +1,5 @@ import * as fns from 'date-fns' +import { isWithinInterval } from 'date-fns' import { es } from 'date-fns/locale' import { i18n } from '../i18n/i18n' import { TimeUnit, TimeUnits } from '../types/time-unit' @@ -237,6 +238,10 @@ class Chrono { }) } + isDateWithinInterval = (interval: Interval) => { + return isWithinInterval(this.date, interval) + } + diff = (date: Date, unit: UnitType) => { switch (unit) { case 'day': From 01a8945b6e0c63d0f6af2c3b33dcf4e13357dcd4 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 16:23:03 +0200 Subject: [PATCH 66/99] feat: update css --- .../components/availability-table/availability-table.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index d95155c6..95d25a09 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -133,7 +133,13 @@ export const AvailabilityTable: FC = () => { {userAbsences?.map((userAbsence, index) => ( - ) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index a80640a0..b6289609 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -116,8 +116,16 @@ export const AvailabilityTable: FC = () => { const updateAbsencesBasedOnInterval = (userAbsence: UserAbsences) => { return userAbsence.absences .map((absence) => { - if (chrono(absence.startDate).isDateWithinInterval(selectedDateInterval)) { + if ( + chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) + ) { return { ...absence, situation: 'normal' } + } else if ( + chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) + ) { + return { ...absence, endDate: selectedDateInterval.end, situation: 'end' } } else if ( !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) @@ -208,7 +216,6 @@ export const AvailabilityTable: FC = () => { chrono(day).isSameDay(x.startDate) )} isHoliday={checkIfHoliday(day)} - interval={selectedDateInterval} > ))} From 0a57fcf73591b3b66001714c75d67d8175dc6fcc Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 10:25:49 +0200 Subject: [PATCH 71/99] refactor: move files --- .../availability-table/{ => absence-item}/absence-item.tsx | 5 ++--- .../availability-table-cell-header.css | 0 .../availability-table-cell-header.tsx | 4 ++-- .../availability-table-cell.tsx | 6 +++--- .../ui/components/availability-table/availability-table.tsx | 4 ++-- src/test-utils/mothers/absence-mother.ts | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) rename src/features/binnacle/features/availability/ui/components/availability-table/{ => absence-item}/absence-item.tsx (91%) rename src/features/binnacle/features/availability/ui/components/availability-table/{ => availability-table-cell}/availability-table-cell-header.css (100%) rename src/features/binnacle/features/availability/ui/components/availability-table/{ => availability-table-cell}/availability-table-cell-header.tsx (85%) rename src/features/binnacle/features/availability/ui/components/availability-table/{ => availability-table-cell}/availability-table-cell.tsx (79%) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx similarity index 91% rename from src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index fa2fe70f..c2c7d2c1 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -1,8 +1,8 @@ import { Box } from '@chakra-ui/react' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { Absence } from '../../../domain/absence' -import { chrono } from '../../../../../../../shared/utils/chrono' +import { Absence } from '../../../../domain/absence' +import { chrono } from '../../../../../../../../shared/utils/chrono' interface Props { absence: Absence @@ -16,7 +16,6 @@ export const AbsenceItem: FC = ({ absence, situation }) => { const duration = chrono(absence.endDate).diff(absence.startDate, 'day') if (situation === 'end') { - console.log(duration, absence) return `calc(${(duration + 1) * 100}% + ${duration}px - 24px)` } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.css b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.css similarity index 100% rename from src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.css rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.css diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx similarity index 85% rename from src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx index ae39569f..342044a6 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx @@ -2,8 +2,8 @@ import { FC } from 'react' import { Box, Text, Th, useColorModeValue } from '@chakra-ui/react' import { isToday } from 'date-fns' import './availability-table-cell-header.css' -import { getWeekdaysName } from '../../../../activity/utils/get-weekdays-name' -import { chrono, isWeekend } from '../../../../../../../shared/utils/chrono' +import { chrono, isWeekend } from '../../../../../../../../shared/utils/chrono' +import { getWeekdaysName } from '../../../../../activity/utils/get-weekdays-name' interface Props { day: Date diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx similarity index 79% rename from src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx rename to src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx index 2ef7a039..ec9f47d1 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx @@ -1,8 +1,8 @@ import { FC } from 'react' import { Box, Td, useColorModeValue } from '@chakra-ui/react' -import { AbsenceItem } from './absence-item' -import { Absence } from '../../../domain/absence' -import { isWeekend } from '../../../../../../../shared/utils/chrono' +import { Absence } from '../../../../domain/absence' +import { isWeekend } from '../../../../../../../../shared/utils/chrono' +import { AbsenceItem } from '../absence-item/absence-item' interface Props { day: Date diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index b6289609..c775be9e 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -11,8 +11,6 @@ import { Tr, useColorModeValue } from '@chakra-ui/react' -import { AvailabilityTableCellHeader } from './availability-table-cell-header' -import { AvailabilityTableCell } from './availability-table-cell' import styles from './availability-table.module.css' import { AvailabilityTableFilters } from './availability-table-filters/availability-table-filters' import { Absence } from '../../../domain/absence' @@ -25,6 +23,8 @@ import { AbsenceFilters } from '../../../domain/absence-filters' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' import { GetHolidaysByYearQry } from '../../../../holiday/application/get-holidays-by-year-qry' import { useTranslation } from 'react-i18next' +import { AvailabilityTableCellHeader } from './availability-table-cell/availability-table-cell-header' +import { AvailabilityTableCell } from './availability-table-cell/availability-table-cell' interface UserAbsences { userId: number diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts index 21ff9abe..81e2c302 100644 --- a/src/test-utils/mothers/absence-mother.ts +++ b/src/test-utils/mothers/absence-mother.ts @@ -23,7 +23,7 @@ export class AbsenceMother { userName: 'Paid leave user', type: 'PAID_LEAVE', startDate: parseISO('2023-09-01'), - endDate: parseISO('2023-09-02'), + endDate: parseISO('2023-09-01'), ...override } } From c2e0e3b5251ad348d4d755cf267c03271ed6e5c5 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 10:26:06 +0200 Subject: [PATCH 72/99] feat: control text overflow --- .../availability-table/absence-item/absence-item.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index c2c7d2c1..c7e3b85d 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -1,4 +1,4 @@ -import { Box } from '@chakra-ui/react' +import { Box, Text } from '@chakra-ui/react' import { FC } from 'react' import { useTranslation } from 'react-i18next' import { Absence } from '../../../../domain/absence' @@ -71,7 +71,9 @@ export const AbsenceItem: FC = ({ absence, situation }) => { ...((situation === 'start' || situation === 'both') && { left: 0 }) }} > - {t(`${getAbsenceTypeName()}`)} + + {t(`${getAbsenceTypeName()}`)} + ) } From 8b7e4d25ef60e8af0a2baada6455309d7f685656 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 10:55:51 +0200 Subject: [PATCH 73/99] refactor: add types --- .../binnacle/features/availability/domain/absence-overflow.ts | 1 + .../availability/domain/absence-with-overflow-info.tsx | 4 ++++ .../availability-table/absence-item/absence-item.tsx | 3 ++- .../availability-table-cell/availability-table-cell.tsx | 4 ++-- .../ui/components/availability-table/availability-table.tsx | 3 ++- 5 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 src/features/binnacle/features/availability/domain/absence-overflow.ts create mode 100644 src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx diff --git a/src/features/binnacle/features/availability/domain/absence-overflow.ts b/src/features/binnacle/features/availability/domain/absence-overflow.ts new file mode 100644 index 00000000..af26c5d9 --- /dev/null +++ b/src/features/binnacle/features/availability/domain/absence-overflow.ts @@ -0,0 +1 @@ +export type AbsenceOverflow = 'normal' | 'end' | 'start' | 'both' diff --git a/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx b/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx new file mode 100644 index 00000000..439f385a --- /dev/null +++ b/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx @@ -0,0 +1,4 @@ +import { Absence } from './absence' +import { AbsenceOverflow } from './absence-overflow' + +export type AbsenceWithOverflowInfo = Absence & { situation: AbsenceOverflow } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index c7e3b85d..dd302cf7 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -3,10 +3,11 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import { Absence } from '../../../../domain/absence' import { chrono } from '../../../../../../../../shared/utils/chrono' +import { AbsenceOverflow } from '../../../../domain/absence-overflow' interface Props { absence: Absence - situation: string + situation: AbsenceOverflow } export const AbsenceItem: FC = ({ absence, situation }) => { diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx index ec9f47d1..21d29dcc 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx @@ -1,13 +1,13 @@ import { FC } from 'react' import { Box, Td, useColorModeValue } from '@chakra-ui/react' -import { Absence } from '../../../../domain/absence' import { isWeekend } from '../../../../../../../../shared/utils/chrono' import { AbsenceItem } from '../absence-item/absence-item' +import { AbsenceWithOverflowInfo } from '../../../../domain/absence-with-overflow-info' interface Props { day: Date isHoliday: boolean - absence?: Absence & { situation: string } + absence?: AbsenceWithOverflowInfo } export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => { diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index c775be9e..64213f28 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -25,6 +25,7 @@ import { GetHolidaysByYearQry } from '../../../../holiday/application/get-holida import { useTranslation } from 'react-i18next' import { AvailabilityTableCellHeader } from './availability-table-cell/availability-table-cell-header' import { AvailabilityTableCell } from './availability-table-cell/availability-table-cell' +import { AbsenceWithOverflowInfo } from '../../../domain/absence-with-overflow-info' interface UserAbsences { userId: number @@ -151,7 +152,7 @@ export const AvailabilityTable: FC = () => { } } }) - .filter((x) => x !== undefined) as (Absence & { situation: string })[] + .filter((x) => x !== undefined) as AbsenceWithOverflowInfo[] // const absencesWithStartDateInside = userAbsence.absences // .filter((absence) => chrono(absence.startDate).isDateWithinInterval(selectedDateInterval)) From 93458ce594e8d21efb0842a9035f1c240a960b5a Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 12:58:13 +0200 Subject: [PATCH 74/99] refactor: add constant to different conditions --- .../availability-table/availability-table.tsx | 81 ++++++------------- 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 64213f28..ae04c6e8 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -117,33 +117,42 @@ export const AvailabilityTable: FC = () => { const updateAbsencesBasedOnInterval = (userAbsence: UserAbsences) => { return userAbsence.absences .map((absence) => { - if ( + const checkIfStartDateAndEndDateAreInsideInterval = chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) - ) { - return { ...absence, situation: 'normal' } - } else if ( + + const CheckIfBothDatesAreOutsideOfInterval = + !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) && + chrono(selectedDateInterval.start).isDateWithinInterval({ + start: absence.startDate, + end: absence.endDate + }) + + const checkIfEndDateIsOutsideOfInterval = chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) - ) { - return { ...absence, endDate: selectedDateInterval.end, situation: 'end' } - } else if ( + + const checkIfStartDateIsOutsideOfInterval = !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) - ) { + + if (checkIfStartDateAndEndDateAreInsideInterval) { + return { ...absence, situation: 'normal' } + } + + if (checkIfEndDateIsOutsideOfInterval) { + return { ...absence, endDate: selectedDateInterval.end, situation: 'end' } + } + + if (checkIfStartDateIsOutsideOfInterval) { return { ...absence, startDate: selectedDateInterval.start, situation: 'start' } - } else if ( - !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && - !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) && - chrono(selectedDateInterval.start).isDateWithinInterval({ - start: absence.startDate, - end: absence.endDate - }) - ) { + } + if (CheckIfBothDatesAreOutsideOfInterval) { return { ...absence, startDate: selectedDateInterval.start, @@ -153,45 +162,7 @@ export const AvailabilityTable: FC = () => { } }) .filter((x) => x !== undefined) as AbsenceWithOverflowInfo[] - - // const absencesWithStartDateInside = userAbsence.absences - // .filter((absence) => chrono(absence.startDate).isDateWithinInterval(selectedDateInterval)) - // .map((filteredAbsence) => ({ ...filteredAbsence, situation: 'normal' })) - // - // const absencesWithStartDateOutside = userAbsence.absences - // .filter( - // (absence) => - // !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && - // chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) - // ) - // .map((filteredAbsence) => ({ - // ...filteredAbsence, - // startDate: selectedDateInterval.start, - // situation: 'start' - // })) - // - // const absencesWithBothDatesOutside = userAbsence.absences - // .filter( - // (absence) => - // !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && - // !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) && - // chrono(selectedDateInterval.start).isDateWithinInterval({ - // start: absence.startDate, - // end: absence.endDate - // }) - // ) - // .map((filteredAbsence) => ({ - // ...filteredAbsence, - // startDate: selectedDateInterval.start, - // endDate: selectedDateInterval.end, - // situation: 'both' - // })) - // - // return [ - // ...absencesWithStartDateOutside, - // ...absencesWithStartDateInside, - // ...absencesWithBothDatesOutside - // ] + //TODO revisar filter - situation = overflowType } const tableRows = ( From 01e626ccb09daba0ff3fadd6c82456219cc6647c Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 13:09:59 +0200 Subject: [PATCH 75/99] refactor: add to availability cell the control to overlap absences --- .../domain/absence-with-overflow-info.tsx | 2 +- .../absence-item/absence-item.tsx | 18 +++++++++--------- .../availability-table-cell.tsx | 14 +++++++++++--- .../availability-table/availability-table.tsx | 12 ++++++------ 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx b/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx index 439f385a..2d62f78a 100644 --- a/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx +++ b/src/features/binnacle/features/availability/domain/absence-with-overflow-info.tsx @@ -1,4 +1,4 @@ import { Absence } from './absence' import { AbsenceOverflow } from './absence-overflow' -export type AbsenceWithOverflowInfo = Absence & { situation: AbsenceOverflow } +export type AbsenceWithOverflowInfo = Absence & { overflowType: AbsenceOverflow } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index dd302cf7..622c8e26 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -7,24 +7,24 @@ import { AbsenceOverflow } from '../../../../domain/absence-overflow' interface Props { absence: Absence - situation: AbsenceOverflow + overflowType: AbsenceOverflow } -export const AbsenceItem: FC = ({ absence, situation }) => { +export const AbsenceItem: FC = ({ absence, overflowType }) => { const { t } = useTranslation() const getDurationInDays = () => { const duration = chrono(absence.endDate).diff(absence.startDate, 'day') - if (situation === 'end') { + if (overflowType === 'end') { return `calc(${(duration + 1) * 100}% + ${duration}px - 24px)` } - if (situation === 'both') { + if (overflowType === 'both') { return `calc(${duration * 100}% + 136px)` } - if (situation === 'start') { + if (overflowType === 'start') { return `calc(${duration * 100}% + 72px)` } @@ -32,15 +32,15 @@ export const AbsenceItem: FC = ({ absence, situation }) => { } const getBorderRadius = () => { - if (situation === 'end') { + if (overflowType === 'end') { return '14px 0 0 14px ' } - if (situation === 'start') { + if (overflowType === 'start') { return '0 14px 14px 0' } - if (situation === 'both') { + if (overflowType === 'both') { return '0 0 0 0' } @@ -69,7 +69,7 @@ export const AbsenceItem: FC = ({ absence, situation }) => { position: 'absolute', top: '50%', transform: 'translate(0, -50%)', - ...((situation === 'start' || situation === 'both') && { left: 0 }) + ...((overflowType === 'start' || overflowType === 'both') && { left: 0 }) }} > diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx index 21d29dcc..c3c24b9e 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx @@ -7,10 +7,10 @@ import { AbsenceWithOverflowInfo } from '../../../../domain/absence-with-overflo interface Props { day: Date isHoliday: boolean - absence?: AbsenceWithOverflowInfo + absences?: AbsenceWithOverflowInfo[] } -export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => { +export const AvailabilityTableCell: FC = ({ day, isHoliday, absences }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') return ( @@ -22,7 +22,15 @@ export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => backgroundColor={isWeekend(day) || isHoliday ? 'rgba(0, 0, 0, 0.10)' : ''} > - {absence ? : ''} + {absences + ? absences.map((absence, index) => ( + + )) + : ''} ) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index ae04c6e8..5ca5bbae 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -138,18 +138,18 @@ export const AvailabilityTable: FC = () => { chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) if (checkIfStartDateAndEndDateAreInsideInterval) { - return { ...absence, situation: 'normal' } + return { ...absence, overflowType: 'normal' } } if (checkIfEndDateIsOutsideOfInterval) { - return { ...absence, endDate: selectedDateInterval.end, situation: 'end' } + return { ...absence, endDate: selectedDateInterval.end, overflowType: 'end' } } if (checkIfStartDateIsOutsideOfInterval) { return { ...absence, startDate: selectedDateInterval.start, - situation: 'start' + overflowType: 'start' } } if (CheckIfBothDatesAreOutsideOfInterval) { @@ -157,12 +157,12 @@ export const AvailabilityTable: FC = () => { ...absence, startDate: selectedDateInterval.start, endDate: selectedDateInterval.end, - situation: 'both' + overflowType: 'both' } } }) .filter((x) => x !== undefined) as AbsenceWithOverflowInfo[] - //TODO revisar filter - situation = overflowType + //TODO revisar filter - overflowType = overflowType } const tableRows = ( @@ -184,7 +184,7 @@ export const AvailabilityTable: FC = () => { + absences={updateAbsencesBasedOnInterval(userAbsence).filter((x) => chrono(day).isSameDay(x.startDate) )} isHoliday={checkIfHoliday(day)} From 1bc60bebf718014a8104294b780efdf651b1bd3b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 13:21:11 +0200 Subject: [PATCH 76/99] feat: create http absence repository and update filters to use type string instead of date --- .../availability/domain/absence-filters.ts | 4 ++-- .../infrastructure/http-absence-repository.ts | 18 ++++++++++++++++++ .../availability-table/availability-table.tsx | 8 ++++---- src/shared/di/container.ts | 4 ++-- 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts diff --git a/src/features/binnacle/features/availability/domain/absence-filters.ts b/src/features/binnacle/features/availability/domain/absence-filters.ts index fd4caad3..676ca59d 100644 --- a/src/features/binnacle/features/availability/domain/absence-filters.ts +++ b/src/features/binnacle/features/availability/domain/absence-filters.ts @@ -4,6 +4,6 @@ export interface AbsenceFilters { userIds?: Id[] organizationIds?: Id[] projectIds?: Id[] - startDate: Date - endDate: Date + startDate: string + endDate: string } diff --git a/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts b/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts new file mode 100644 index 00000000..a28fdc3c --- /dev/null +++ b/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts @@ -0,0 +1,18 @@ +import { singleton } from 'tsyringe' +import { AbsenceRepository } from '../domain/absence-repository' +import { Absence } from '../domain/absence' +import { HttpClient } from '../../../../../shared/http/http-client' +import { AbsenceFilters } from '../domain/absence-filters' + +@singleton() +export class HttpAbsenceRepository implements AbsenceRepository { + protected static absencePath = '/api/absence' + + constructor(private httpClient: HttpClient) {} + + async getAbsences(absenceFilters: AbsenceFilters): Promise { + return await this.httpClient.get(HttpAbsenceRepository.absencePath, { + params: absenceFilters + }) + } +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 5ca5bbae..b97183bb 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -37,8 +37,8 @@ export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() const [userAbsences, setUserAbsences] = useState([]) const [absenceFilters, setAbsenceFilters] = useState({ - startDate: new Date(), - endDate: new Date() + startDate: chrono().format(chrono.DATE_FORMAT), + endDate: chrono().format(chrono.DATE_FORMAT) }) const { t } = useTranslation() @@ -62,8 +62,8 @@ export const AvailabilityTable: FC = () => { getAbsencesQry .execute({ ...absenceFilters, - startDate: selectedDateInterval.start, - endDate: selectedDateInterval.end + startDate: chrono(selectedDateInterval.start).format(chrono.DATE_FORMAT), + endDate: chrono(selectedDateInterval.end).format(chrono.DATE_FORMAT) }) .then((absences) => { const userAbsencesObject = absences?.reduce( diff --git a/src/shared/di/container.ts b/src/shared/di/container.ts index 36e505bd..551987b6 100644 --- a/src/shared/di/container.ts +++ b/src/shared/di/container.ts @@ -27,7 +27,7 @@ import { } from './container-tokens' import { toast, ToastType } from '../notification/toast' import { HttpProjectRepository } from '../../features/shared/project/infrastructure/http-project-repository' -import { FakeAbsenceRepository } from '../../features/binnacle/features/availability/infrastructure/fake-absence-repository' +import { HttpAbsenceRepository } from '../../features/binnacle/features/availability/infrastructure/http-absence-repository' container.register(STORAGE, { useValue: localStorage }) container.register(TOAST, { useValue: toast }) @@ -42,4 +42,4 @@ container.registerSingleton(PROJECT_ROLE_REPOSITORY, HttpProjectRoleRepository) container.registerSingleton(PROJECT_REPOSITORY, HttpProjectRepository) container.registerSingleton(ORGANIZATION_REPOSITORY, HttpOrganizationRepository) container.registerSingleton(ACTIVITY_REPOSITORY, HttpActivityRepository) -container.registerSingleton(ABSENCE_REPOSITORY, FakeAbsenceRepository) +container.registerSingleton(ABSENCE_REPOSITORY, HttpAbsenceRepository) From 6f92fb689aa052a52cb9bc0849e216b7c7269e6b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 13:42:03 +0200 Subject: [PATCH 77/99] feat: remove comment --- .../ui/components/availability-table/availability-table.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index b97183bb..77bcc0ea 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -162,7 +162,6 @@ export const AvailabilityTable: FC = () => { } }) .filter((x) => x !== undefined) as AbsenceWithOverflowInfo[] - //TODO revisar filter - overflowType = overflowType } const tableRows = ( From d9ef1f6e61065d7800a1774a4479db6f60ada826 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 13:50:44 +0200 Subject: [PATCH 78/99] feat: resolve type issue --- .../ui/components/availability-table/availability-table.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 77bcc0ea..cd11a453 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -125,8 +125,8 @@ export const AvailabilityTable: FC = () => { !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) && chrono(selectedDateInterval.start).isDateWithinInterval({ - start: absence.startDate, - end: absence.endDate + start: chrono(absence.startDate).getDate(), + end: chrono(absence.endDate).getDate() }) const checkIfEndDateIsOutsideOfInterval = From 75a76c13e7e59ea4b28903ce53b383192625a14a Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 16:01:17 +0200 Subject: [PATCH 79/99] feat: make imputable nullable --- .../availability-table-filters/availability-table-filters.tsx | 2 +- .../features/organization/domain/organization-filters.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx index 39a931c9..3db6dee1 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx @@ -30,7 +30,7 @@ export const AvailabilityTableFilters: FC = ({ onChange }) => { { handleChange({ organizationIds: organization ? [organization?.id] : undefined }) }} diff --git a/src/features/binnacle/features/organization/domain/organization-filters.ts b/src/features/binnacle/features/organization/domain/organization-filters.ts index a4dba36a..e1d5940e 100644 --- a/src/features/binnacle/features/organization/domain/organization-filters.ts +++ b/src/features/binnacle/features/organization/domain/organization-filters.ts @@ -1,6 +1,6 @@ import { OrganizationType } from './organization-type' export interface OrganizationFilters { - imputable: boolean + imputable?: boolean types?: OrganizationType[] } From 6488332be4aee0020bd6a9aeb77f3c833a2feaf4 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 08:48:58 +0200 Subject: [PATCH 80/99] feat: modify absence repository with new type --- .../availability/domain/absence-repository.ts | 4 +- .../features/availability/domain/absence.ts | 3 -- .../availability/domain/user-absence.ts | 8 +++ .../infrastructure/fake-absence-repository.ts | 6 +-- .../infrastructure/http-absence-repository.ts | 6 +-- src/test-utils/mothers/absence-mother.ts | 50 ++++++++++++++----- 6 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 src/features/binnacle/features/availability/domain/user-absence.ts diff --git a/src/features/binnacle/features/availability/domain/absence-repository.ts b/src/features/binnacle/features/availability/domain/absence-repository.ts index 4183ae80..1d891881 100644 --- a/src/features/binnacle/features/availability/domain/absence-repository.ts +++ b/src/features/binnacle/features/availability/domain/absence-repository.ts @@ -1,6 +1,6 @@ -import { Absence } from './absence' import { AbsenceFilters } from './absence-filters' +import { UserAbsence } from './user-absence' export interface AbsenceRepository { - getAbsences(absenceFilters: AbsenceFilters): Promise + getAbsences(absenceFilters: AbsenceFilters): Promise } diff --git a/src/features/binnacle/features/availability/domain/absence.ts b/src/features/binnacle/features/availability/domain/absence.ts index 884702db..4c4ce89a 100644 --- a/src/features/binnacle/features/availability/domain/absence.ts +++ b/src/features/binnacle/features/availability/domain/absence.ts @@ -1,9 +1,6 @@ -import { Id } from '../../../../../shared/types/id' import { AbsenceType } from './absence-type' export interface Absence { - userId: Id - userName: string type: AbsenceType startDate: Date endDate: Date diff --git a/src/features/binnacle/features/availability/domain/user-absence.ts b/src/features/binnacle/features/availability/domain/user-absence.ts new file mode 100644 index 00000000..b42cd691 --- /dev/null +++ b/src/features/binnacle/features/availability/domain/user-absence.ts @@ -0,0 +1,8 @@ +import { Id } from '../../../../../shared/types/id' +import { Absence } from './absence' + +export interface UserAbsence { + userId: Id + userName: string + absences: Absence[] +} diff --git a/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts b/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts index f90b073f..9a396115 100644 --- a/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts +++ b/src/features/binnacle/features/availability/infrastructure/fake-absence-repository.ts @@ -1,11 +1,11 @@ import { AbsenceRepository } from '../domain/absence-repository' -import { Absence } from '../domain/absence' import { AbsenceMother } from '../../../../../test-utils/mothers/absence-mother' import { singleton } from 'tsyringe' +import { UserAbsence } from '../domain/user-absence' @singleton() export class FakeAbsenceRepository implements AbsenceRepository { - async getAbsences(): Promise { - return AbsenceMother.absences() + async getAbsences(): Promise { + return AbsenceMother.userAbsences() } } diff --git a/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts b/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts index a28fdc3c..b7e1333c 100644 --- a/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts +++ b/src/features/binnacle/features/availability/infrastructure/http-absence-repository.ts @@ -1,8 +1,8 @@ import { singleton } from 'tsyringe' import { AbsenceRepository } from '../domain/absence-repository' -import { Absence } from '../domain/absence' import { HttpClient } from '../../../../../shared/http/http-client' import { AbsenceFilters } from '../domain/absence-filters' +import { UserAbsence } from '../domain/user-absence' @singleton() export class HttpAbsenceRepository implements AbsenceRepository { @@ -10,8 +10,8 @@ export class HttpAbsenceRepository implements AbsenceRepository { constructor(private httpClient: HttpClient) {} - async getAbsences(absenceFilters: AbsenceFilters): Promise { - return await this.httpClient.get(HttpAbsenceRepository.absencePath, { + async getAbsences(absenceFilters: AbsenceFilters): Promise { + return await this.httpClient.get(HttpAbsenceRepository.absencePath, { params: absenceFilters }) } diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts index 81e2c302..0e0d13d1 100644 --- a/src/test-utils/mothers/absence-mother.ts +++ b/src/test-utils/mothers/absence-mother.ts @@ -1,26 +1,52 @@ import { Absence } from '../../features/binnacle/features/availability/domain/absence' import { parseISO } from '../../shared/utils/chrono' +import { UserAbsence } from '../../features/binnacle/features/availability/domain/user-absence' export class AbsenceMother { - static absences(): Absence[] { + static userAbsences(): UserAbsence[] { return [ - this.paidLeaveAbsence(), - this.paidLeaveAbsence({ - startDate: parseISO('2023-09-02'), - endDate: parseISO('2023-09-10') + this.vacationUser(), + this.vacationUser({ userId: 3, userName: 'Available user', absences: [] }), + this.paidLeaveUser(), + this.paidLeaveUser({ + absences: [ + this.paidLeaveAbsence({ + startDate: parseISO('2023-09-02'), + endDate: parseISO('2023-09-10') + }) + ] }), - this.paidLeaveAbsence({ - startDate: parseISO('2023-09-25'), - endDate: parseISO('2023-11-10') - }), - this.vacationAbsence() + this.paidLeaveUser({ + absences: [ + this.paidLeaveAbsence({ + startDate: parseISO('2023-09-25'), + endDate: parseISO('2023-11-10') + }) + ] + }) ] } - static paidLeaveAbsence(override?: Partial): Absence { + static paidLeaveUser(override?: Partial): UserAbsence { return { userId: 1, userName: 'Paid leave user', + absences: [this.paidLeaveAbsence()], + ...override + } + } + + static vacationUser(override?: Partial): UserAbsence { + return { + userId: 1, + userName: 'Vacation user', + absences: [this.vacationAbsence()], + ...override + } + } + + static paidLeaveAbsence(override?: Partial): Absence { + return { type: 'PAID_LEAVE', startDate: parseISO('2023-09-01'), endDate: parseISO('2023-09-01'), @@ -30,8 +56,6 @@ export class AbsenceMother { static vacationAbsence(override?: Partial): Absence { return { - userId: 2, - userName: 'Vacation user', type: 'VACATION', startDate: parseISO('2023-09-03'), endDate: parseISO('2023-09-05'), From fe886201b9b467c07bdbe774d960fe369142828d Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 08:55:10 +0200 Subject: [PATCH 81/99] feat: modify table with new type --- .../application/get-absences-qry.ts | 6 ++--- .../availability-table/availability-table.tsx | 26 +++---------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/features/binnacle/features/availability/application/get-absences-qry.ts b/src/features/binnacle/features/availability/application/get-absences-qry.ts index cfada668..d1dd1911 100644 --- a/src/features/binnacle/features/availability/application/get-absences-qry.ts +++ b/src/features/binnacle/features/availability/application/get-absences-qry.ts @@ -1,18 +1,18 @@ import { Query, UseCaseKey } from '@archimedes/arch' -import { Absence } from '../domain/absence' import { AbsenceFilters } from '../domain/absence-filters' import type { AbsenceRepository } from '../domain/absence-repository' import { inject, singleton } from 'tsyringe' import { ABSENCE_REPOSITORY } from '../../../../../shared/di/container-tokens' +import { UserAbsence } from '../domain/user-absence' @UseCaseKey('GetAbsencesQry') @singleton() -export class GetAbsencesQry extends Query { +export class GetAbsencesQry extends Query { constructor(@inject(ABSENCE_REPOSITORY) private absenceRepository: AbsenceRepository) { super() } - internalExecute(absenceFilters: AbsenceFilters): Promise { + internalExecute(absenceFilters: AbsenceFilters): Promise { return this.absenceRepository.getAbsences(absenceFilters) } } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index cd11a453..bf08d9fa 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -13,7 +13,6 @@ import { } from '@chakra-ui/react' import styles from './availability-table.module.css' import { AvailabilityTableFilters } from './availability-table-filters/availability-table-filters' -import { Absence } from '../../../domain/absence' import { chrono } from '../../../../../../../shared/utils/chrono' import { useExecuteUseCaseOnMount } from '../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { GetAbsencesQry } from '../../../application/get-absences-qry' @@ -26,16 +25,11 @@ import { useTranslation } from 'react-i18next' import { AvailabilityTableCellHeader } from './availability-table-cell/availability-table-cell-header' import { AvailabilityTableCell } from './availability-table-cell/availability-table-cell' import { AbsenceWithOverflowInfo } from '../../../domain/absence-with-overflow-info' - -interface UserAbsences { - userId: number - userName: string - absences: Absence[] -} +import { UserAbsence } from '../../../domain/user-absence' export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() - const [userAbsences, setUserAbsences] = useState([]) + const [userAbsences, setUserAbsences] = useState([]) const [absenceFilters, setAbsenceFilters] = useState({ startDate: chrono().format(chrono.DATE_FORMAT), endDate: chrono().format(chrono.DATE_FORMAT) @@ -66,19 +60,7 @@ export const AvailabilityTable: FC = () => { endDate: chrono(selectedDateInterval.end).format(chrono.DATE_FORMAT) }) .then((absences) => { - const userAbsencesObject = absences?.reduce( - (acc: { [key: number]: UserAbsences }, item: Absence) => { - const { userId, userName } = item - if (!acc[userId]) { - acc[userId] = { userId, userName, absences: [] } - } - acc[userId].absences.push(item) - return acc - }, - {} - ) - - if (userAbsencesObject !== undefined) setUserAbsences(Object.values(userAbsencesObject)) + setUserAbsences(absences) }) } else { setUserAbsences([]) @@ -114,7 +96,7 @@ export const AvailabilityTable: FC = () => { if (element !== null) element.scrollIntoView({ inline: 'center' }) }, [selectedDate]) - const updateAbsencesBasedOnInterval = (userAbsence: UserAbsences) => { + const updateAbsencesBasedOnInterval = (userAbsence: UserAbsence) => { return userAbsence.absences .map((absence) => { const checkIfStartDateAndEndDateAreInsideInterval = From 1fb7cea2b9beaee6306e77a8b9aedba2a9ddc437 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 09:18:15 +0200 Subject: [PATCH 82/99] feat: add named constants to absence calculation variables --- .../absence-item/absence-item.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index 622c8e26..42b9ea3c 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -15,20 +15,25 @@ export const AbsenceItem: FC = ({ absence, overflowType }) => { const getDurationInDays = () => { const duration = chrono(absence.endDate).diff(absence.startDate, 'day') + const cellPadding = '24px' + const boxSize = '48px' + const absencePadding = '8px' if (overflowType === 'end') { - return `calc(${(duration + 1) * 100}% + ${duration}px - 24px)` + return `calc(${(duration + 1) * 100}% + ${duration}px - ${cellPadding})` } if (overflowType === 'both') { - return `calc(${duration * 100}% + 136px)` + return `calc(${ + duration * 100 + }% + ${boxSize} + ${cellPadding} * 2 + ${boxSize} - ${absencePadding})` } if (overflowType === 'start') { - return `calc(${duration * 100}% + 72px)` + return `calc(${duration * 100}% + ${boxSize} + ${cellPadding})` } - return `calc(${duration * 100}% + 48px)` + return `calc(${duration * 100}% + ${boxSize})` } const getBorderRadius = () => { From 2070474280542b127ed7a7f83ccada056a8ec051 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 12:23:02 +0200 Subject: [PATCH 83/99] feat: add selected date interval as dependency of useEffect --- .../ui/components/availability-table/availability-table.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index bf08d9fa..3b2cfee1 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -34,6 +34,7 @@ export const AvailabilityTable: FC = () => { startDate: chrono().format(chrono.DATE_FORMAT), endDate: chrono().format(chrono.DATE_FORMAT) }) + const { t } = useTranslation() const selectedDateInterval = useMemo(() => { @@ -42,6 +43,7 @@ export const AvailabilityTable: FC = () => { end: chrono(selectedDate).endOf('month').plus(5, 'day').getDate() } }, [selectedDate]) + const { useCase: getAbsencesQry } = useGetUseCase(GetAbsencesQry) const daysOfMonth = chrono(selectedDateInterval.start).eachDayUntil(selectedDateInterval.end) @@ -65,7 +67,7 @@ export const AvailabilityTable: FC = () => { } else { setUserAbsences([]) } - }, [absenceFilters]) + }, [absenceFilters, selectedDateInterval]) const checkIfHoliday = (day: Date) => holidays.some((holiday) => chrono(day).isSameDay(holiday.date)) @@ -180,7 +182,6 @@ export const AvailabilityTable: FC = () => { <> - {userAbsences.length === 0 ? ( From 2513f67a4edee6411939434e680c93baf219c5dc Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 12:38:25 +0200 Subject: [PATCH 84/99] feat: invalidate get absence qry cache --- src/shared/archimedes/archimedes.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/shared/archimedes/archimedes.ts b/src/shared/archimedes/archimedes.ts index e8a9bd6d..0917d2c5 100644 --- a/src/shared/archimedes/archimedes.ts +++ b/src/shared/archimedes/archimedes.ts @@ -38,6 +38,7 @@ import { GetProjectsWithBlockerUserName } from '../../features/administration/fe import { UnblockProjectCmd } from '../../features/administration/features/project/application/unblock-project-cmd' import { ToastNotificationLink } from './links/toast-notification-link' import { ToastType } from '../notification/toast' +import { GetAbsencesQry } from '../../features/binnacle/features/availability/application/get-absences-qry' const toast = container.resolve(TOAST) Archimedes.createChain([ @@ -103,9 +104,14 @@ CacheInvalidations.set(UpdateVacationCmd.prototype.key, [ GetCalendarDataQry.prototype.key, GetActivitySummaryQry.prototype.key ]) + +//Block CacheInvalidations.set(BlockProjectCmd.prototype.key, [ GetProjectsWithBlockerUserName.prototype.key ]) CacheInvalidations.set(UnblockProjectCmd.prototype.key, [ GetProjectsWithBlockerUserName.prototype.key ]) + +//Absence +CacheInvalidations.set(GetAbsencesQry.prototype.key, [InvalidationPolicy.NO_CACHE]) From e71bf04a7f0ed81e5cae22ddd79788b4aedf4249 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 15:22:32 +0200 Subject: [PATCH 85/99] feat: update item color --- .../availability-table/absence-item/absence-item.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index 42b9ea3c..5804c025 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -1,4 +1,4 @@ -import { Box, Text } from '@chakra-ui/react' +import { Box, Text, useColorModeValue } from '@chakra-ui/react' import { FC } from 'react' import { useTranslation } from 'react-i18next' import { Absence } from '../../../../domain/absence' @@ -55,6 +55,8 @@ export const AbsenceItem: FC = ({ absence, overflowType }) => { const getAbsenceTypeName = () => absence.type === 'VACATION' ? 'absences.vacation' : 'absences.paidLeave' + const backgroundColor = useColorModeValue('gray.300', 'gray.600') + return ( = ({ absence, overflowType }) => { width={getDurationInDays()} border="1px solid" display="flex" - bgColor={'gray.400'} + bgColor={backgroundColor} borderRadius={getBorderRadius()} zIndex={1} style={{ From f50fcb13d580b8418f6b5d7bbbf4851d334fa30b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Thu, 28 Sep 2023 16:49:23 +0200 Subject: [PATCH 86/99] feat: add mobile styling and logic --- .../calendar-controls/calendar-controls.tsx | 9 +++++++- .../calendar-picker/calendar-picker.tsx | 9 +++++--- .../calendar-controls/today-button.tsx | 4 +++- .../absence-item/absence-item.tsx | 16 +++++++------- .../availability-table-cell-header.tsx | 13 +++++++++++- .../availability-table-cell.tsx | 8 ++++++- .../availability-table/availability-table.tsx | 21 +++++++++++-------- 7 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-controls.tsx b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-controls.tsx index 68ab4bc3..ca95ee58 100644 --- a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-controls.tsx +++ b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-controls.tsx @@ -3,10 +3,17 @@ import { CalendarPicker } from './calendar-picker/calendar-picker' import { NextMonthArrow } from './next-month-arrow' import { PrevMonthArrow } from './prev-month-arrow' import { TodayButton } from './today-button' +import { useIsMobile } from '../../../../../../../shared/hooks/use-is-mobile' export const CalendarControls = () => { + const isMobile = useIsMobile() return ( - + diff --git a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-picker/calendar-picker.tsx b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-picker/calendar-picker.tsx index 12ac64d3..d8279221 100644 --- a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-picker/calendar-picker.tsx +++ b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-picker/calendar-picker.tsx @@ -21,6 +21,7 @@ import { MonthsList } from './months-list' import { useCalendarContext } from '../../../contexts/calendar-context' import { useExecuteUseCaseOnMount } from '../../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { GetUserLoggedQry } from '../../../../../../../shared/user/application/get-user-logged-qry' +import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' export const CalendarPicker = () => { const { selectedDate } = useCalendarContext() @@ -35,6 +36,8 @@ export const CalendarPicker = () => { const [selectedMonthName, setSelectedMonthName] = useState('') const [selectedYearNumber, setSelectedYearNumber] = useState(null) + const isMobile = useIsMobile() + useEffect(() => { const monthName = chrono(selectedDate).format('MMMM') const yearName = chrono(selectedDate).format('yyyy') @@ -52,12 +55,12 @@ export const CalendarPicker = () => { return ( - diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index 5804c025..96245f0e 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next' import { Absence } from '../../../../domain/absence' import { chrono } from '../../../../../../../../shared/utils/chrono' import { AbsenceOverflow } from '../../../../domain/absence-overflow' +import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' interface Props { absence: Absence @@ -12,25 +13,24 @@ interface Props { export const AbsenceItem: FC = ({ absence, overflowType }) => { const { t } = useTranslation() + const isMobile = useIsMobile() const getDurationInDays = () => { const duration = chrono(absence.endDate).diff(absence.startDate, 'day') - const cellPadding = '24px' - const boxSize = '48px' - const absencePadding = '8px' + const cellPadding = '12px' + const boxSize = isMobile ? '36px' : '48px' + const durationPlusLast = duration + 1 if (overflowType === 'end') { - return `calc(${(duration + 1) * 100}% + ${duration}px - ${cellPadding})` + return `calc(${durationPlusLast * 100}% + ${durationPlusLast - 1}px - ${cellPadding})` } if (overflowType === 'both') { - return `calc(${ - duration * 100 - }% + ${boxSize} + ${cellPadding} * 2 + ${boxSize} - ${absencePadding})` + return `calc(${durationPlusLast * 100}% + ${durationPlusLast - 1}px)` } if (overflowType === 'start') { - return `calc(${duration * 100}% + ${boxSize} + ${cellPadding})` + return `calc(${durationPlusLast * 100}%)` } return `calc(${duration * 100}% + ${boxSize})` diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx index 342044a6..d86e88b0 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx @@ -4,6 +4,7 @@ import { isToday } from 'date-fns' import './availability-table-cell-header.css' import { chrono, isWeekend } from '../../../../../../../../shared/utils/chrono' import { getWeekdaysName } from '../../../../../activity/utils/get-weekdays-name' +import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' interface Props { day: Date @@ -20,13 +21,23 @@ const getWeekDay = (date: Date) => { export const AvailabilityTableCellHeader: FC = ({ day, isHoliday }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') + const isMobile = useIsMobile() + return ( {userAbsences?.map((userAbsence, index) => ( - @@ -180,7 +176,14 @@ export const AvailabilityTable: FC = () => { return ( <> - + From ddf888908eb3b4f50a08b8dbef929111266c0d4f Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 29 Sep 2023 09:20:53 +0200 Subject: [PATCH 87/99] feat: show different message when there is no results with selected filters --- .../components/availability-table/availability-table.tsx | 9 +++++++-- src/shared/i18n/en.json | 3 ++- src/shared/i18n/es.json | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 21b6c53c..557e074f 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -55,8 +55,11 @@ export const AvailabilityTable: FC = () => { selectedDateInterval.start.getFullYear() ) + const requiredFiltersAreSelected = () => + absenceFilters.organizationIds !== undefined || absenceFilters.userIds !== undefined + useEffect(() => { - if (absenceFilters.organizationIds !== undefined || absenceFilters.userIds !== undefined) { + if (requiredFiltersAreSelected()) { getAbsencesQry .execute({ ...absenceFilters, @@ -187,7 +190,9 @@ export const AvailabilityTable: FC = () => { - {userAbsences.length === 0 ? ( + {!requiredFiltersAreSelected() ? ( + {t('absences.requiredFilters')} + ) : userAbsences.length === 0 ? ( {t('absences.emptyMessage')} ) : ( diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index 01fc8e35..fe5a6bc2 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -168,7 +168,8 @@ "absences": { "vacation": "Vacation", "paidLeave": "Paid leave", - "emptyMessage": "It is necessary to filter by organization or user to obtain availability." + "requiredFilters": "It is necessary to filter by organization or user to obtain availability.", + "emptyMessage": "No results have been obtained within the selected period with the selected filters." }, "actions": { "approve": "Approve", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index ac70e04b..aa85b905 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -168,7 +168,8 @@ "absences": { "vacation": "Vacaciones", "paidLeave": "Permiso", - "emptyMessage": "Es necesario filtrar por organización o usuario para obtener la disponibilidad." + "requiredFilters": "Es necesario filtrar por organización o usuario para obtener la disponibilidad.", + "emptyMessage": "No se han obtenido resultados dentro del periodo seleccionado con los filtros seleccionados." }, "actions": { "approve": "Aprobar", From 7ad249c81df22f83aa7c567cd1d1249f045c1400 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Fri, 29 Sep 2023 10:24:52 +0200 Subject: [PATCH 88/99] feat: add accessibility to absence item to show a resume of the absence --- .../availability-table/absence-item/absence-item.tsx | 10 +++++++++- .../availability-table-cell.tsx | 4 +++- .../availability-table/availability-table.tsx | 1 + src/shared/i18n/en.json | 3 ++- src/shared/i18n/es.json | 3 ++- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx index 96245f0e..350f9938 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item/absence-item.tsx @@ -7,11 +7,12 @@ import { AbsenceOverflow } from '../../../../domain/absence-overflow' import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' interface Props { + userName: string absence: Absence overflowType: AbsenceOverflow } -export const AbsenceItem: FC = ({ absence, overflowType }) => { +export const AbsenceItem: FC = ({ absence, userName, overflowType }) => { const { t } = useTranslation() const isMobile = useIsMobile() @@ -59,6 +60,13 @@ export const AbsenceItem: FC = ({ absence, overflowType }) => { return ( = ({ day, isHoliday, absences }) => { +export const AvailabilityTableCell: FC = ({ day, isHoliday, absences, userName }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') const isMobile = useIsMobile() @@ -32,6 +33,7 @@ export const AvailabilityTableCell: FC = ({ day, isHoliday, absences }) = ? absences.map((absence, index) => ( diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 557e074f..140ec765 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -166,6 +166,7 @@ export const AvailabilityTable: FC = () => { chrono(day).isSameDay(x.startDate) )} diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index fe5a6bc2..fcb49d4d 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -169,7 +169,8 @@ "vacation": "Vacation", "paidLeave": "Paid leave", "requiredFilters": "It is necessary to filter by organization or user to obtain availability.", - "emptyMessage": "No results have been obtained within the selected period with the selected filters." + "emptyMessage": "No results have been obtained within the selected period with the selected filters.", + "absenceItemDescription": "Absence of type {{type}} for user {{name}} from {{startDate}} to {{endDate}}" }, "actions": { "approve": "Approve", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index aa85b905..87a9dae0 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -169,7 +169,8 @@ "vacation": "Vacaciones", "paidLeave": "Permiso", "requiredFilters": "Es necesario filtrar por organización o usuario para obtener la disponibilidad.", - "emptyMessage": "No se han obtenido resultados dentro del periodo seleccionado con los filtros seleccionados." + "emptyMessage": "No se han obtenido resultados dentro del periodo seleccionado con los filtros seleccionados.", + "absenceItemDescription": "Ausencia del tipo {{type}} para el usuario {{name}} desde {{startDate}} hasta {{endDate}}" }, "actions": { "approve": "Aprobar", From ee7f86d0bc648f2299d7a29bb2e58dcdc80db4a0 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 2 Oct 2023 11:48:52 +0200 Subject: [PATCH 89/99] feat: add table header and spinner in loading state --- .../availability-table-header.tsx | 28 +++++ .../availability-table/availability-table.tsx | 105 ++++++++++-------- 2 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 src/features/binnacle/features/availability/ui/components/availability-table/availability-table-header/availability-table-header.tsx diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-header/availability-table-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-header/availability-table-header.tsx new file mode 100644 index 00000000..73954c16 --- /dev/null +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-header/availability-table-header.tsx @@ -0,0 +1,28 @@ +import { AvailabilityTableFilters } from '../availability-table-filters/availability-table-filters' +import { CalendarControls } from '../../../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' +import { Flex } from '@chakra-ui/react' +import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' +import { FC } from 'react' +import { AbsenceFilters } from '../../../../domain/absence-filters' + +interface Props { + onFilterChange: (params: Partial) => void +} + +export const AvailabilityTableHeader: FC = ({ onFilterChange }) => { + const isMobile = useIsMobile() + + return ( + + + + + ) +} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 140ec765..e7fdc1fd 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -2,6 +2,7 @@ import { FC, useEffect, useMemo, useState } from 'react' import { Box, Flex, + Spinner, Table, Tbody, Td, @@ -11,12 +12,9 @@ import { Tr, useColorModeValue } from '@chakra-ui/react' -import styles from './availability-table.module.css' -import { AvailabilityTableFilters } from './availability-table-filters/availability-table-filters' import { chrono } from '../../../../../../../shared/utils/chrono' import { useExecuteUseCaseOnMount } from '../../../../../../../shared/arch/hooks/use-execute-use-case-on-mount' import { GetAbsencesQry } from '../../../application/get-absences-qry' -import { CalendarControls } from '../../../../activity/ui/calendar-desktop/calendar-controls/calendar-controls' import { useCalendarContext } from '../../../../activity/ui/contexts/calendar-context' import { AbsenceFilters } from '../../../domain/absence-filters' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' @@ -27,6 +25,8 @@ import { AvailabilityTableCell } from './availability-table-cell/availability-ta import { AbsenceWithOverflowInfo } from '../../../domain/absence-with-overflow-info' import { UserAbsence } from '../../../domain/user-absence' import { useIsMobile } from '../../../../../../../shared/hooks/use-is-mobile' +import { AvailabilityTableHeader } from './availability-table-header/availability-table-header' +import styles from './availability-table.module.css' export const AvailabilityTable: FC = () => { const { selectedDate } = useCalendarContext() @@ -35,6 +35,7 @@ export const AvailabilityTable: FC = () => { startDate: chrono().format(chrono.DATE_FORMAT), endDate: chrono().format(chrono.DATE_FORMAT) }) + const [previousSelectedDate, setPreviousSelectedDate] = useState(selectedDate) const { t } = useTranslation() const isMobile = useIsMobile() @@ -46,7 +47,7 @@ export const AvailabilityTable: FC = () => { } }, [selectedDate]) - const { useCase: getAbsencesQry } = useGetUseCase(GetAbsencesQry) + const { executeUseCase: getAbsencesQry, isLoading } = useGetUseCase(GetAbsencesQry) const daysOfMonth = chrono(selectedDateInterval.start).eachDayUntil(selectedDateInterval.end) @@ -60,15 +61,13 @@ export const AvailabilityTable: FC = () => { useEffect(() => { if (requiredFiltersAreSelected()) { - getAbsencesQry - .execute({ - ...absenceFilters, - startDate: chrono(selectedDateInterval.start).format(chrono.DATE_FORMAT), - endDate: chrono(selectedDateInterval.end).format(chrono.DATE_FORMAT) - }) - .then((absences) => { - setUserAbsences(absences) - }) + getAbsencesQry({ + ...absenceFilters, + startDate: chrono(selectedDateInterval.start).format(chrono.DATE_FORMAT), + endDate: chrono(selectedDateInterval.end).format(chrono.DATE_FORMAT) + }).then((absences) => { + setUserAbsences(absences) + }) } else { setUserAbsences([]) } @@ -83,24 +82,22 @@ export const AvailabilityTable: FC = () => { setAbsenceFilters({ ...absenceFilters, ...updatedFilter }) } - const tableHeaders = ( - - - - {daysOfMonth.map((day, index) => ( - - ))} - - - ) - useEffect(() => { - const element = document.getElementById('is-today') - if (element !== null) element.scrollIntoView({ inline: 'center' }) + if (previousSelectedDate > selectedDate) { + const lastElement = document.querySelector('thead tr th:last-child') + if (lastElement !== null) lastElement.scrollIntoView({ inline: 'center' }) + } + + if (previousSelectedDate < selectedDate) { + const firstElement = document.querySelector('thead tr th:first-child + th') + if (firstElement !== null) firstElement.scrollIntoView({ inline: 'center' }) + } + + if (chrono(selectedDate).isSameDay(previousSelectedDate)) { + const element = document.getElementById('is-today') + if (element !== null) element.scrollIntoView({ inline: 'center' }) + setPreviousSelectedDate(selectedDate) + } }, [selectedDate]) const updateAbsencesBasedOnInterval = (userAbsence: UserAbsence) => { @@ -153,12 +150,38 @@ export const AvailabilityTable: FC = () => { .filter((x) => x !== undefined) as AbsenceWithOverflowInfo[] } + const tableHeaders = ( + + + + {daysOfMonth.map((day, index) => ( + + ))} + + + ) + const tableRows = ( {userAbsences?.map((userAbsence, index) => ( - @@ -180,18 +203,12 @@ export const AvailabilityTable: FC = () => { return ( <> - - - - - {!requiredFiltersAreSelected() ? ( + + {isLoading ? ( + + + + ) : !requiredFiltersAreSelected() ? ( {t('absences.requiredFilters')} ) : userAbsences.length === 0 ? ( {t('absences.emptyMessage')} From d1733843ddc81df35c3bb4850e6e89892d2f2603 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 2 Oct 2023 16:51:58 +0200 Subject: [PATCH 90/99] feat: update styling in availability table --- .../availability-table-cell-header.tsx | 1 - .../availability-table-cell.tsx | 6 +-- .../availability-table/availability-table.tsx | 43 ++++++------------- src/styles/misc.css | 2 + 4 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx index d86e88b0..e51ffa4f 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx @@ -25,7 +25,6 @@ export const AvailabilityTableCellHeader: FC = ({ day, isHoliday }) => { return ( - + {daysOfMonth.map((day, index) => ( { {userAbsences?.map((userAbsence, index) => ( - ) + const layoutWithData = ( + +
= ({ day, isHoliday, absence }) => backgroundColor={isWeekend(day) || isHoliday ? 'rgba(0, 0, 0, 0.10)' : ''} > - {absence && isSameDay(absence.startDate) ? ( + {absence ? ( ) : ( '' diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index aaecc5e6..0a2c3332 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -25,6 +25,7 @@ import { AbsenceFilters } from '../../../domain/absence-filters' import { useGetUseCase } from '../../../../../../../shared/arch/hooks/use-get-use-case' import { GetHolidaysByYearQry } from '../../../../holiday/application/get-holidays-by-year-qry' import { useTranslation } from 'react-i18next' +import { isWithinInterval } from 'date-fns' interface UserAbsences { userId: number @@ -57,7 +58,7 @@ export const AvailabilityTable: FC = () => { ) useEffect(() => { - if (absenceFilters.organizationId !== undefined) { + if (absenceFilters.organizationId !== undefined || absenceFilters.userId !== undefined) { getAbsencesQry .execute({ ...absenceFilters, @@ -113,6 +114,22 @@ export const AvailabilityTable: FC = () => { if (element !== null) element.scrollIntoView({ inline: 'center' }) }, [selectedDate]) + const updateAbsencesBasedOnInterval = (userAbsence: UserAbsences) => { + const absencesWithStartDateOutside = userAbsence.absences + .filter( + (x) => + !isWithinInterval(x.startDate, selectedDateInterval) && + isWithinInterval(x.endDate, selectedDateInterval) + ) + .map((x) => ({ ...x, startDate: selectedDateInterval.start, situation: 'start' })) + + const absencesWithStartDateNotOutside = userAbsence.absences + .filter((x) => isWithinInterval(x.startDate, selectedDateInterval)) + .map((x) => ({ ...x, situation: 'normal' })) + + return [...absencesWithStartDateOutside, ...absencesWithStartDateNotOutside] + } + const tableRows = (
+ {userAbsence.userName} From 5fa7265a55f1f1f0a09e64a0bbba1f2d41806544 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 26 Sep 2023 16:23:44 +0200 Subject: [PATCH 67/99] feat: put a flag to control when the start date and endate are outside of the interval --- .../availability-table/absence-item.tsx | 16 +++- .../availability-table/availability-table.tsx | 82 ++++++++++++++++--- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx index 0002c059..de16b89f 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx @@ -21,9 +21,13 @@ export const AbsenceItem: FC = ({ absence, interval, situation }) => { return `calc(${(duration - diff) * 100}% - 20px)` } - return situation === 'start' - ? `calc(${duration * 100}% + 72px)` - : `calc(${duration * 100}% + 48px)` + if (situation === 'both') { + return `calc(${duration * 100}% + 136px)` + } + + if (situation === 'start') return `calc(${duration * 100}% + 72px)` + + return `calc(${duration * 100}% + 48px)` } const getBorderRadius = () => { @@ -35,6 +39,10 @@ export const AbsenceItem: FC = ({ absence, interval, situation }) => { return '0 14px 14px 0' } + if (situation === 'both') { + return '0 0 0 0' + } + return '14px' } @@ -60,7 +68,7 @@ export const AbsenceItem: FC = ({ absence, interval, situation }) => { position: 'absolute', top: '50%', transform: 'translate(0, -50%)', - ...(situation === 'start' && { left: 0 }) + ...((situation === 'start' || situation === 'both') && { left: 0 }) }} > {t(`${getAbsenceTypeName()}`)} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 95d25a09..c545be1c 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -114,19 +114,75 @@ export const AvailabilityTable: FC = () => { }, [selectedDate]) const updateAbsencesBasedOnInterval = (userAbsence: UserAbsences) => { - const absencesWithStartDateOutside = userAbsence.absences - .filter( - (x) => - !chrono(x.startDate).isDateWithinInterval(selectedDateInterval) && - chrono(x.endDate).isDateWithinInterval(selectedDateInterval) - ) - .map((x) => ({ ...x, startDate: selectedDateInterval.start, situation: 'start' })) - - const absencesWithStartDateNotOutside = userAbsence.absences - .filter((x) => chrono(x.startDate).isDateWithinInterval(selectedDateInterval)) - .map((x) => ({ ...x, situation: 'normal' })) - - return [...absencesWithStartDateOutside, ...absencesWithStartDateNotOutside] + return userAbsence.absences + .map((absence) => { + if (chrono(absence.startDate).isDateWithinInterval(selectedDateInterval)) { + return { ...absence, situation: 'normal' } + } else if ( + !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) + ) { + return { + ...absence, + startDate: selectedDateInterval.start, + situation: 'start' + } + } else if ( + !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) && + chrono(selectedDateInterval.start).isDateWithinInterval({ + start: absence.startDate, + end: absence.endDate + }) + ) { + return { + ...absence, + startDate: selectedDateInterval.start, + endDate: selectedDateInterval.end, + situation: 'both' + } + } + }) + .filter((x) => x !== undefined) as (Absence & { situation: string })[] + + // const absencesWithStartDateInside = userAbsence.absences + // .filter((absence) => chrono(absence.startDate).isDateWithinInterval(selectedDateInterval)) + // .map((filteredAbsence) => ({ ...filteredAbsence, situation: 'normal' })) + // + // const absencesWithStartDateOutside = userAbsence.absences + // .filter( + // (absence) => + // !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + // chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) + // ) + // .map((filteredAbsence) => ({ + // ...filteredAbsence, + // startDate: selectedDateInterval.start, + // situation: 'start' + // })) + // + // const absencesWithBothDatesOutside = userAbsence.absences + // .filter( + // (absence) => + // !chrono(absence.startDate).isDateWithinInterval(selectedDateInterval) && + // !chrono(absence.endDate).isDateWithinInterval(selectedDateInterval) && + // chrono(selectedDateInterval.start).isDateWithinInterval({ + // start: absence.startDate, + // end: absence.endDate + // }) + // ) + // .map((filteredAbsence) => ({ + // ...filteredAbsence, + // startDate: selectedDateInterval.start, + // endDate: selectedDateInterval.end, + // situation: 'both' + // })) + // + // return [ + // ...absencesWithStartDateOutside, + // ...absencesWithStartDateInside, + // ...absencesWithBothDatesOutside + // ] } const tableRows = ( From 4844f1d5625c91d19300e3408a14d168577d0e95 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 08:38:08 +0200 Subject: [PATCH 68/99] feat: update absence filters to receive an array --- .../availability/domain/absence-filters.ts | 6 +++--- .../availability-table-filters.tsx | 14 +++++++++----- .../availability-table/availability-table.tsx | 2 +- src/test-utils/mothers/absence-mother.ts | 4 ++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/features/binnacle/features/availability/domain/absence-filters.ts b/src/features/binnacle/features/availability/domain/absence-filters.ts index f1db8cb3..fd4caad3 100644 --- a/src/features/binnacle/features/availability/domain/absence-filters.ts +++ b/src/features/binnacle/features/availability/domain/absence-filters.ts @@ -1,9 +1,9 @@ import { Id } from '../../../../../shared/types/id' export interface AbsenceFilters { - userId?: Id - organizationId?: Id - projectId?: Id + userIds?: Id[] + organizationIds?: Id[] + projectIds?: Id[] startDate: Date endDate: Date } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx index 2ac956e6..39a931c9 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx @@ -7,6 +7,8 @@ import { ProjectsCombo } from '../../../../../activity/ui/components/activity-fo import { useForm, useWatch } from 'react-hook-form' import { UserFilter } from './user-filter/user-filter' import { AvailabilityTableFiltersSchema } from './availability-table-filters.schema' +import { Project } from '../../../../../../../shared/project/domain/project' +import { Organization } from '../../../../../organization/domain/organization' interface Props { onChange: (params: Partial) => void @@ -29,21 +31,23 @@ export const AvailabilityTableFilters: FC = ({ onChange }) => { control={control} name={'organization'} organizationFilters={{ types: ['CLIENT'], imputable: true }} - onChange={(item) => { - handleChange({ organizationId: item?.id }) + onChange={(organization: Organization) => { + handleChange({ organizationIds: organization ? [organization?.id] : undefined }) }} /> { - handleChange({ projectId: item?.id }) + onChange={(project: Project) => { + handleChange({ projectIds: project ? [project?.id] : undefined }) }} isDisabled={projectDisabled} /> handleChange({ userId: userInfo?.id })} + onChange={(userInfo: UserInfo) => + handleChange({ userIds: userInfo ? [userInfo?.id] : undefined }) + } > ) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index c545be1c..a80640a0 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -57,7 +57,7 @@ export const AvailabilityTable: FC = () => { ) useEffect(() => { - if (absenceFilters.organizationId !== undefined || absenceFilters.userId !== undefined) { + if (absenceFilters.organizationIds !== undefined || absenceFilters.userIds !== undefined) { getAbsencesQry .execute({ ...absenceFilters, diff --git a/src/test-utils/mothers/absence-mother.ts b/src/test-utils/mothers/absence-mother.ts index fd1b2e8b..21ff9abe 100644 --- a/src/test-utils/mothers/absence-mother.ts +++ b/src/test-utils/mothers/absence-mother.ts @@ -9,6 +9,10 @@ export class AbsenceMother { startDate: parseISO('2023-09-02'), endDate: parseISO('2023-09-10') }), + this.paidLeaveAbsence({ + startDate: parseISO('2023-09-25'), + endDate: parseISO('2023-11-10') + }), this.vacationAbsence() ] } From 468a2d51baac9d360aa3be5ef665e2bd70417d95 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 08:41:55 +0200 Subject: [PATCH 69/99] feat: update literals --- src/shared/i18n/en.json | 2 +- src/shared/i18n/es.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index ac8b3ff8..01fc8e35 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -168,7 +168,7 @@ "absences": { "vacation": "Vacation", "paidLeave": "Paid leave", - "emptyMessage": "It is necessary to filter by organization to obtain availability." + "emptyMessage": "It is necessary to filter by organization or user to obtain availability." }, "actions": { "approve": "Approve", diff --git a/src/shared/i18n/es.json b/src/shared/i18n/es.json index 7df28a4d..ac70e04b 100644 --- a/src/shared/i18n/es.json +++ b/src/shared/i18n/es.json @@ -168,7 +168,7 @@ "absences": { "vacation": "Vacaciones", "paidLeave": "Permiso", - "emptyMessage": "Es necesario filtrar por organización para obtener la disponibilidad." + "emptyMessage": "Es necesario filtrar por organización o usuario para obtener la disponibilidad." }, "actions": { "approve": "Aprobar", From 4378da0eb3aecf4921e364d571715eed7bd71447 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Wed, 27 Sep 2023 10:11:56 +0200 Subject: [PATCH 70/99] feat: add logic to print absence item correctly --- .../availability-table/absence-item.tsx | 15 ++++++++------- .../availability-table-cell.tsx | 13 ++----------- .../availability-table/availability-table.tsx | 11 +++++++++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx index de16b89f..fa2fe70f 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/absence-item.tsx @@ -7,31 +7,32 @@ import { chrono } from '../../../../../../../shared/utils/chrono' interface Props { absence: Absence situation: string - interval: { start: Date; end: Date } } -export const AbsenceItem: FC = ({ absence, interval, situation }) => { +export const AbsenceItem: FC = ({ absence, situation }) => { const { t } = useTranslation() const getDurationInDays = () => { const duration = chrono(absence.endDate).diff(absence.startDate, 'day') - if (absence.endDate > interval.end) { - const diff = chrono(absence.endDate).diff(interval.end, 'day') - return `calc(${(duration - diff) * 100}% - 20px)` + if (situation === 'end') { + console.log(duration, absence) + return `calc(${(duration + 1) * 100}% + ${duration}px - 24px)` } if (situation === 'both') { return `calc(${duration * 100}% + 136px)` } - if (situation === 'start') return `calc(${duration * 100}% + 72px)` + if (situation === 'start') { + return `calc(${duration * 100}% + 72px)` + } return `calc(${duration * 100}% + 48px)` } const getBorderRadius = () => { - if (absence.endDate > interval.end) { + if (situation === 'end') { return '14px 0 0 14px ' } diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx index 24c732ad..2ef7a039 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell.tsx @@ -8,10 +8,9 @@ interface Props { day: Date isHoliday: boolean absence?: Absence & { situation: string } - interval: { start: Date; end: Date } } -export const AvailabilityTableCell: FC = ({ day, isHoliday, absence, interval }) => { +export const AvailabilityTableCell: FC = ({ day, isHoliday, absence }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') return ( @@ -23,15 +22,7 @@ export const AvailabilityTableCell: FC = ({ day, isHoliday, absence, inte backgroundColor={isWeekend(day) || isHoliday ? 'rgba(0, 0, 0, 0.10)' : ''} > - {absence ? ( - - ) : ( - '' - )} + {absence ? : ''}
- + {weekDays[getWeekDay(day) - 1]} {day.getDate()} diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx index c3c24b9e..2792d3e9 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell.tsx @@ -3,6 +3,7 @@ import { Box, Td, useColorModeValue } from '@chakra-ui/react' import { isWeekend } from '../../../../../../../../shared/utils/chrono' import { AbsenceItem } from '../absence-item/absence-item' import { AbsenceWithOverflowInfo } from '../../../../domain/absence-with-overflow-info' +import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' interface Props { day: Date @@ -13,15 +14,20 @@ interface Props { export const AvailabilityTableCell: FC = ({ day, isHoliday, absences }) => { const borderColor = useColorModeValue('gray.300', 'gray.700') + const isMobile = useIsMobile() + return ( - + {absences ? absences.map((absence, index) => ( { const { selectedDate } = useCalendarContext() @@ -36,6 +37,7 @@ export const AvailabilityTable: FC = () => { }) const { t } = useTranslation() + const isMobile = useIsMobile() const selectedDateInterval = useMemo(() => { return { @@ -152,14 +154,8 @@ export const AvailabilityTable: FC = () => {
- + + {userAbsence.userName}
+ +
- + + {userAbsence.userName} = ({ day, isHoliday, absences, userName }) => { - const borderColor = useColorModeValue('gray.300', 'gray.700') - const isMobile = useIsMobile() return ( { const checkIfHoliday = (day: Date) => holidays.some((holiday) => chrono(day).isSameDay(holiday.date)) - const borderColor = useColorModeValue('gray.300', 'gray.700') - const onFilterChange = (updatedFilter: Partial) => { setAbsenceFilters({ ...absenceFilters, ...updatedFilter }) } @@ -153,9 +139,7 @@ export const AvailabilityTable: FC = () => { const tableHeaders = (
- -
+ {
+ {tableHeaders} + {tableRows} +
+
+ ) + return ( <> @@ -213,12 +201,7 @@ export const AvailabilityTable: FC = () => { ) : userAbsences.length === 0 ? ( {t('absences.emptyMessage')} ) : ( - - - {tableHeaders} - {tableRows} -
-
+ layoutWithData )} ) diff --git a/src/styles/misc.css b/src/styles/misc.css index 4cf19606..ec8cc5f4 100644 --- a/src/styles/misc.css +++ b/src/styles/misc.css @@ -3,6 +3,7 @@ --body-bg-color: var(--chakra-colors-gray-700); --private-holiday-color: var(--chakra-colors-blue-400); --public-holiday-color: var(--chakra-colors-yellow-400); + --table-border-color: var(--chakra-colors-gray-700); --bg-color: #1a202c; } @@ -11,6 +12,7 @@ --body-bg-color: white; --private-holiday-color: var(--chakra-colors-blue-400); --public-holiday-color: var(--chakra-colors-yellow-400); + --table-border-color: var(--chakra-colors-gray-300); --bg-color: white; } From 9f3afc6a803887facbc34fc4f0fc731af68c06f3 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 2 Oct 2023 16:52:14 +0200 Subject: [PATCH 91/99] feat: add styling --- .../availability-table.module.css | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css index 505d9a4a..45ca2bd2 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.module.css @@ -1,7 +1,31 @@ -.data-table thead tr th:first-child, -.data-table tbody tr td:first-child { +.data-table { + border-collapse: separate; + border-spacing: 0; +} + +.data-table th { + border-top: 1px solid; + border-bottom: 1px solid; + border-right: 1px solid; + border-color: var(--table-border-color); +} + +.data-table td { + border-bottom: 1px solid; + border-right: 1px solid; + border-color: var(--table-border-color); +} + +.data-table th:first-child, +.data-table td:first-child { + border-left: 1px solid var(--table-border-color); position: sticky; - left: 0; background: var(--bg-color); + left: 0; z-index: 2; } + +.data-table th:first-child { + border-top: none; + border-left: none; +} From 91e74da2eabb149dfd2708b70ec2af291945f589 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Mon, 2 Oct 2023 17:03:37 +0200 Subject: [PATCH 92/99] feat: update calendar control utils to only handle keypress when body is not focused --- .../calendar-controls/calendar-control-utils.ts | 6 +++--- .../calendar-desktop/calendar-controls/next-month-arrow.tsx | 4 ++-- .../calendar-desktop/calendar-controls/prev-month-arrow.tsx | 4 ++-- .../ui/calendar-desktop/calendar-controls/today-button.tsx | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-control-utils.ts b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-control-utils.ts index 8e31335d..f3852bfb 100644 --- a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-control-utils.ts +++ b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/calendar-control-utils.ts @@ -1,9 +1,9 @@ -export const handleKeyPressWhenModalIsNotOpened = ( +export const handleKeyPressWhenBodyIsNotFocused = ( pressedKey: string, controlledKey: string, handler: () => void ) => { - const isModalOpened = document.querySelector('[id^=chakra-modal]') !== null - if (isModalOpened) return + const hasFocusInBody = document.activeElement?.tagName === 'BODY' + if (!hasFocusInBody) return if (pressedKey === controlledKey) handler() } diff --git a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/next-month-arrow.tsx b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/next-month-arrow.tsx index e13949af..d21eb5d4 100644 --- a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/next-month-arrow.tsx +++ b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/next-month-arrow.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next' import { chrono } from '../../../../../../../shared/utils/chrono' import { useCalendarContext } from '../../contexts/calendar-context' import { useCallback, useEffect } from 'react' -import { handleKeyPressWhenModalIsNotOpened } from './calendar-control-utils' +import { handleKeyPressWhenBodyIsNotFocused } from './calendar-control-utils' export const NextMonthArrow = () => { const { t } = useTranslation() @@ -18,7 +18,7 @@ export const NextMonthArrow = () => { }, [selectedDate, setSelectedDate]) const handlePressedKey = (e: KeyboardEvent) => { - handleKeyPressWhenModalIsNotOpened(e.key, 'n', handleNextMonthClick) + handleKeyPressWhenBodyIsNotFocused(e.key, 'n', handleNextMonthClick) } useEffect(() => { diff --git a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/prev-month-arrow.tsx b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/prev-month-arrow.tsx index 47d37e7e..363760aa 100644 --- a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/prev-month-arrow.tsx +++ b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/prev-month-arrow.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { chrono } from '../../../../../../../shared/utils/chrono' import { useCalendarContext } from '../../contexts/calendar-context' -import { handleKeyPressWhenModalIsNotOpened } from './calendar-control-utils' +import { handleKeyPressWhenBodyIsNotFocused } from './calendar-control-utils' export const PrevMonthArrow = () => { const { t } = useTranslation() @@ -17,7 +17,7 @@ export const PrevMonthArrow = () => { }, [selectedDate, setSelectedDate]) const handlePressedKey = (e: KeyboardEvent) => { - handleKeyPressWhenModalIsNotOpened(e.key, 'p', handlePrevMonthClick) + handleKeyPressWhenBodyIsNotFocused(e.key, 'p', handlePrevMonthClick) } useEffect(() => { diff --git a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/today-button.tsx b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/today-button.tsx index 4bd915e4..90209d32 100644 --- a/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/today-button.tsx +++ b/src/features/binnacle/features/activity/ui/calendar-desktop/calendar-controls/today-button.tsx @@ -3,7 +3,7 @@ import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { chrono } from '../../../../../../../shared/utils/chrono' import { useCalendarContext } from '../../contexts/calendar-context' -import { handleKeyPressWhenModalIsNotOpened } from './calendar-control-utils' +import { handleKeyPressWhenBodyIsNotFocused } from './calendar-control-utils' import { useIsMobile } from '../../../../../../../shared/hooks/use-is-mobile' export const TodayButton: FC = () => { @@ -17,7 +17,7 @@ export const TodayButton: FC = () => { }, [selectedDate]) const handlePressedKey = (e: KeyboardEvent) => { - handleKeyPressWhenModalIsNotOpened(e.key, 't', handleSetCurrentMonth) + handleKeyPressWhenBodyIsNotFocused(e.key, 't', handleSetCurrentMonth) } useEffect(() => { From b4bbc0f098aa915b4966a1559b4eff7adb61746e Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 3 Oct 2023 14:05:55 +0200 Subject: [PATCH 93/99] test: add integration test --- .../tests/see-users-availability.int.tsx | 29 +++++++++++++++++++ src/test-utils/di/integration-di.ts | 3 ++ 2 files changed, 32 insertions(+) create mode 100644 src/features/binnacle/features/availability/tests/see-users-availability.int.tsx diff --git a/src/features/binnacle/features/availability/tests/see-users-availability.int.tsx b/src/features/binnacle/features/availability/tests/see-users-availability.int.tsx new file mode 100644 index 00000000..5fa924a2 --- /dev/null +++ b/src/features/binnacle/features/availability/tests/see-users-availability.int.tsx @@ -0,0 +1,29 @@ +import AvailabilityPage from '../ui/availability-page' + +describe('See users availability', () => { + it('should not show table until filters are selected', () => { + setup() + + cy.findByLabelText('Organization', { selector: 'input' }).should('have.value', '') + + cy.findByLabelText('User', { selector: 'input' }).should('have.value', '') + + cy.findByText('Paid leave').should('not.exist') + }) + + it('should show table when filters are selected', () => { + setup() + + cy.findByLabelText('Organization', { selector: 'input' }).click() + + cy.findByText('Test organization').click() + + cy.findByText('Paid leave').should('be.visible') + }) +}) + +const setup = () => { + cy.clock().invoke('setSystemTime', new Date(2023, 9, 1, 0, 0, 0, 0).getTime()) + + cy.mount() +} diff --git a/src/test-utils/di/integration-di.ts b/src/test-utils/di/integration-di.ts index 1a160af4..5ddbb365 100644 --- a/src/test-utils/di/integration-di.ts +++ b/src/test-utils/di/integration-di.ts @@ -1,6 +1,7 @@ import 'reflect-metadata' import { container } from 'tsyringe' import { + ABSENCE_REPOSITORY, ACTIVITY_REPOSITORY, AUTH_REPOSITORY, HOLIDAY_REPOSITORY, @@ -27,6 +28,7 @@ import { FakeActivityRepository } from '../../features/binnacle/features/activit import { toast, ToastType } from '../../shared/notification/toast' import { FakeUserSettingsRepository } from '../../features/shared/user/features/settings/infrastructure/fake-user-settings-repository' import { FakeProjectRepository } from '../../features/shared/project/infrastructure/fake-project-repository' +import { FakeAbsenceRepository } from '../../features/binnacle/features/availability/infrastructure/fake-absence-repository' container.register(STORAGE, { useValue: localStorage }) container.register(TOAST, { useValue: toast }) @@ -41,3 +43,4 @@ container.registerSingleton(PROJECT_ROLE_REPOSITORY, FakeProjectRoleRepository) container.registerSingleton(PROJECT_REPOSITORY, FakeProjectRepository) container.registerSingleton(ORGANIZATION_REPOSITORY, FakeOrganizationRepository) container.registerSingleton(ACTIVITY_REPOSITORY, FakeActivityRepository) +container.registerSingleton(ABSENCE_REPOSITORY, FakeAbsenceRepository) From 9e280dda65b4b48b7a73b297db67a3b80a62dfce Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 3 Oct 2023 14:52:20 +0200 Subject: [PATCH 94/99] test: add filters mobile layout --- .../availability-table-filters.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx index 3db6dee1..eb6b86fb 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx @@ -9,6 +9,7 @@ import { UserFilter } from './user-filter/user-filter' import { AvailabilityTableFiltersSchema } from './availability-table-filters.schema' import { Project } from '../../../../../../../shared/project/domain/project' import { Organization } from '../../../../../organization/domain/organization' +import { useIsMobile } from '../../../../../../../../shared/hooks/use-is-mobile' interface Props { onChange: (params: Partial) => void @@ -20,13 +21,19 @@ export const AvailabilityTableFilters: FC = ({ onChange }) => { control, name: ['organization'] }) + const isMobile = useIsMobile() const projectDisabled = useMemo(() => organization === undefined, [organization]) const handleChange = (params: Partial) => onChange(params) return ( - + Date: Tue, 3 Oct 2023 15:18:30 +0200 Subject: [PATCH 95/99] test: add repository and qry filters --- .../application/get-absences-qry.test.ts | 32 +++++++++++++++++++ .../http-absence-repository.test.ts | 27 ++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/features/binnacle/features/availability/application/get-absences-qry.test.ts create mode 100644 src/features/binnacle/features/availability/infrastructure/http-absence-repository.test.ts diff --git a/src/features/binnacle/features/availability/application/get-absences-qry.test.ts b/src/features/binnacle/features/availability/application/get-absences-qry.test.ts new file mode 100644 index 00000000..de723850 --- /dev/null +++ b/src/features/binnacle/features/availability/application/get-absences-qry.test.ts @@ -0,0 +1,32 @@ +import { mock } from 'jest-mock-extended' +import { AbsenceRepository } from '../domain/absence-repository' +import { GetAbsencesQry } from './get-absences-qry' +import { AbsenceMother } from '../../../../../test-utils/mothers/absence-mother' + +describe('GetAbsencesQry', () => { + it('should get absences', async function () { + const { getAbsencesQry, absenceRepository } = setup() + const absences = AbsenceMother.userAbsences() + absenceRepository.getAbsences.mockResolvedValue(absences) + + const response = await getAbsencesQry.internalExecute({ + startDate: '10-10-2023', + endDate: '10-10-2023' + }) + + expect(absenceRepository.getAbsences).toHaveBeenCalledWith({ + startDate: '10-10-2023', + endDate: '10-10-2023' + }) + expect(response).toEqual(absences) + }) +}) + +const setup = () => { + const absenceRepository = mock() + + return { + absenceRepository, + getAbsencesQry: new GetAbsencesQry(absenceRepository) + } +} diff --git a/src/features/binnacle/features/availability/infrastructure/http-absence-repository.test.ts b/src/features/binnacle/features/availability/infrastructure/http-absence-repository.test.ts new file mode 100644 index 00000000..f72dde0a --- /dev/null +++ b/src/features/binnacle/features/availability/infrastructure/http-absence-repository.test.ts @@ -0,0 +1,27 @@ +import { HttpClient } from '../../../../../shared/http/http-client' +import { HttpAbsenceRepository } from './http-absence-repository' +import { mock } from 'jest-mock-extended' + +describe('HttpAbsenceRepository', () => { + it('should get absences', () => { + const { httpClient, httpAbsenceRepository } = setup() + + httpAbsenceRepository.getAbsences({ startDate: '10-10-2023', endDate: '10-10-2023' }) + + expect(httpClient.get).toHaveBeenCalledWith('/api/absence', { + params: { + startDate: '10-10-2023', + endDate: '10-10-2023' + } + }) + }) +}) + +const setup = () => { + const httpClient = mock() + + return { + httpClient, + httpAbsenceRepository: new HttpAbsenceRepository(httpClient) + } +} From e7cb3d16cbac60b71ba936d557c73e45f54c136b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 3 Oct 2023 15:38:01 +0200 Subject: [PATCH 96/99] feat: resolve padding issue --- .../availability-table-cell/availability-table-cell-header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx index e51ffa4f..1d3cfd20 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-cell/availability-table-cell-header.tsx @@ -38,7 +38,7 @@ export const AvailabilityTableCellHeader: FC = ({ day, isHoliday }) => { padding={'0px'} > {weekDays[getWeekDay(day) - 1]} - + {day.getDate()}
From d7086878fa568b37eae811e120bfa4051b3e5aee Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 3 Oct 2023 16:25:12 +0200 Subject: [PATCH 97/99] feat: update how scroll works --- .../availability-table/availability-table.tsx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx index 8fd88592..f1c98bd9 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table.tsx @@ -69,22 +69,18 @@ export const AvailabilityTable: FC = () => { } useEffect(() => { - if (previousSelectedDate > selectedDate) { + if (chrono().isBetween(selectedDateInterval.start, selectedDateInterval.end)) { + const element = document.getElementById('is-today') + if (element !== null) element.scrollIntoView({ inline: 'center' }) + } else if (previousSelectedDate > selectedDate) { const lastElement = document.querySelector('thead tr th:last-child') if (lastElement !== null) lastElement.scrollIntoView({ inline: 'center' }) - } - - if (previousSelectedDate < selectedDate) { + } else if (previousSelectedDate < selectedDate) { const firstElement = document.querySelector('thead tr th:first-child + th') if (firstElement !== null) firstElement.scrollIntoView({ inline: 'center' }) } - - if (chrono(selectedDate).isSameDay(previousSelectedDate)) { - const element = document.getElementById('is-today') - if (element !== null) element.scrollIntoView({ inline: 'center' }) - setPreviousSelectedDate(selectedDate) - } - }, [selectedDate]) + setPreviousSelectedDate(selectedDate) + }, [userAbsences]) const updateAbsencesBasedOnInterval = (userAbsence: UserAbsence) => { return userAbsence.absences From 44f7e9e1437c2a73d12d0732328f199637afd874 Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 3 Oct 2023 16:44:15 +0200 Subject: [PATCH 98/99] feat: update props to make them optional --- .../features/organization/domain/organization-repository.ts | 2 +- .../organization/infrastructure/http-organization-repository.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/binnacle/features/organization/domain/organization-repository.ts b/src/features/binnacle/features/organization/domain/organization-repository.ts index be9b78ee..09dc8150 100644 --- a/src/features/binnacle/features/organization/domain/organization-repository.ts +++ b/src/features/binnacle/features/organization/domain/organization-repository.ts @@ -2,5 +2,5 @@ import { Organization } from './organization' import { OrganizationFilters } from './organization-filters' export interface OrganizationRepository { - getAll(organizationFilters: OrganizationFilters): Promise + getAll(organizationFilters?: OrganizationFilters): Promise } diff --git a/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts b/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts index 679679b4..209026f9 100644 --- a/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts +++ b/src/features/binnacle/features/organization/infrastructure/http-organization-repository.ts @@ -10,7 +10,7 @@ export class HttpOrganizationRepository implements OrganizationRepository { constructor(private httpClient: HttpClient) {} - async getAll(organizationFilters: OrganizationFilters): Promise { + async getAll(organizationFilters?: OrganizationFilters): Promise { return this.httpClient.get(HttpOrganizationRepository.organizationPath, { params: organizationFilters }) From 3fec67fa862e2586a83505fef9246368dbb88b2b Mon Sep 17 00:00:00 2001 From: Manumartin95 Date: Tue, 3 Oct 2023 17:36:22 +0200 Subject: [PATCH 99/99] feat: delete project filter when there is no organization --- .../availability-table-filters.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx index eb6b86fb..5f20485d 100644 --- a/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx +++ b/src/features/binnacle/features/availability/ui/components/availability-table/availability-table-filters/availability-table-filters.tsx @@ -4,7 +4,7 @@ import { UserInfo } from '../../../../../../../shared/user/domain/user-info' import { AbsenceFilters } from '../../../../domain/absence-filters' import { OrganizationsCombo } from '../../../../../activity/ui/components/activity-form/components/combos/organizations-combo' import { ProjectsCombo } from '../../../../../activity/ui/components/activity-form/components/combos/projects-combo' -import { useForm, useWatch } from 'react-hook-form' +import { useController, useForm, useWatch } from 'react-hook-form' import { UserFilter } from './user-filter/user-filter' import { AvailabilityTableFiltersSchema } from './availability-table-filters.schema' import { Project } from '../../../../../../../shared/project/domain/project' @@ -21,11 +21,19 @@ export const AvailabilityTableFilters: FC = ({ onChange }) => { control, name: ['organization'] }) + + const { field: projectField } = useController({ + name: 'project', + control + }) + const isMobile = useIsMobile() const projectDisabled = useMemo(() => organization === undefined, [organization]) - const handleChange = (params: Partial) => onChange(params) + const handleChange = (params: Partial) => { + onChange(params) + } return ( = ({ onChange }) => { control={control} name={'organization'} organizationFilters={{ types: ['CLIENT'] }} - onChange={(organization: Organization) => { + onChange={(organization?: Organization) => { handleChange({ organizationIds: organization ? [organization?.id] : undefined }) + projectField.onChange() }} /> { handleChange({ projectIds: project ? [project?.id] : undefined }) }}