From ce338c8637326bdad351943a6132571554390217 Mon Sep 17 00:00:00 2001 From: Oleksandr Zhukov Date: Mon, 28 Oct 2024 14:39:29 +0200 Subject: [PATCH 1/2] added draggable dates marker on single project page --- .../draggableProjectDates.tsx | 96 +++++++++++++++++++ .../scrollingCalendar/calendarHeader.tsx | 21 +++- app/components/scrollingCalendar/constants.ts | 5 + app/components/scrollingCalendar/helpers.ts | 21 ++++ .../scrollingCalendar/scrollingCalendar.tsx | 5 +- app/projects/[projectId]/page.tsx | 26 +++-- tailwind.config.ts | 1 + 7 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 app/components/projectAssignment/draggableProjectDates.tsx diff --git a/app/components/projectAssignment/draggableProjectDates.tsx b/app/components/projectAssignment/draggableProjectDates.tsx new file mode 100644 index 0000000..fab5ee9 --- /dev/null +++ b/app/components/projectAssignment/draggableProjectDates.tsx @@ -0,0 +1,96 @@ +'use client' + +import React, { DragEvent, FC } from 'react'; +import { useMutation } from "@apollo/client"; +import { UPSERT_PROJECT } from "@/app/gqlQueries"; +import { useProjectsDataContext } from '@/app/contexts/projectsDataContext'; +import { getISODateFromWeek, getWeekNumberAndYear } from '../scrollingCalendar/helpers'; +import { PROJECT_DATES_TYPE } from '../scrollingCalendar/constants'; + +interface DraggableDatesProps { + weekNumberOfTheYear: number; + monthYear: number; + monthLabel: string; +} + +const DraggableDates: FC = ({ + weekNumberOfTheYear, + monthYear, + monthLabel, +}) => { + const { projectList, setProjectList, singleProjectPage } = useProjectsDataContext() + + const endDateWeekNumber = getWeekNumberAndYear(singleProjectPage?.endsOn) + const startDateWeekNumber = getWeekNumberAndYear(singleProjectPage?.startsOn) + const isEndDateWeek = endDateWeekNumber?.weekNumber === weekNumberOfTheYear && monthYear === endDateWeekNumber?.year + const isStartDateWeek = startDateWeekNumber?.weekNumber === weekNumberOfTheYear && monthYear === startDateWeekNumber?.year + + const [upsertProject] = useMutation(UPSERT_PROJECT, { + errorPolicy: "all", + onCompleted({ upsertProject }) { + const { id, endsOn, startsOn } = upsertProject + const updatedProjectList = projectList.map(project => + project.id === id + ? { ...project, endsOn, startsOn } + : project + ); + setProjectList(updatedProjectList) + }, + }); + + const handleDragStart = (e: DragEvent) => { + const type = isStartDateWeek ? PROJECT_DATES_TYPE.STARTS_ON : PROJECT_DATES_TYPE.ENDS_ON + e.dataTransfer.setData('text/plain', JSON.stringify(type)); + e.stopPropagation(); + e.currentTarget.style.zIndex = '100'; + e.currentTarget.style.opacity = '0.5'; + }; + + const handleDrop = async (e: DragEvent) => { + const typeDate = JSON.parse(e.dataTransfer.getData('text/plain')); + const newTargetDate = getISODateFromWeek(monthYear, weekNumberOfTheYear, Number(monthLabel)) + const newDateForComparison = new Date(newTargetDate) + e.preventDefault(); + e.currentTarget.style.zIndex = ''; + e.currentTarget.style.opacity = '1'; + if (typeDate === PROJECT_DATES_TYPE.ENDS_ON && singleProjectPage?.startsOn) { + const startDate = new Date(singleProjectPage.startsOn); + if (startDate >= newDateForComparison) return; + + } + if (typeDate === PROJECT_DATES_TYPE.STARTS_ON && singleProjectPage?.endsOn) { + const endDate = new Date(singleProjectPage.endsOn) + if (newDateForComparison >= endDate) return; + } + const variables = { + id: singleProjectPage?.id, + name: singleProjectPage?.name, + [typeDate]: newTargetDate + } + await upsertProject({ + variables + }); + + }; + + const handleDragEnd = (e: DragEvent) => { + e.currentTarget.style.zIndex = ''; + e.currentTarget.style.opacity = '1'; + }; + + return ( +
e.preventDefault()} + onDrop={handleDrop} + className={`absolute top-0 left-0 right-0 inset-0 flex flex-col leading-none h-full w-full ${(isStartDateWeek || isEndDateWeek) ? 'cursor-pointer navbar' : '' + } `} + > + {isStartDateWeek && 'START' || isEndDateWeek && 'END'} +
+ ); +}; + +export default DraggableDates; diff --git a/app/components/scrollingCalendar/calendarHeader.tsx b/app/components/scrollingCalendar/calendarHeader.tsx index 62f1b83..fd96656 100644 --- a/app/components/scrollingCalendar/calendarHeader.tsx +++ b/app/components/scrollingCalendar/calendarHeader.tsx @@ -15,6 +15,7 @@ import { AssignmentType, MonthsDataType, ProjectType } from '@/app/typeInterface import { calculateTotalHoursPerWeek, isBeforeWeek, showMonthAndYear, getNextWeeksPerView, getPrevWeeksPerView, currentWeek, currentYear } from './helpers'; import ViewsMenu from '../viewsMenu/viewsMenu'; import EditFormController from './editFormController'; +import DraggableDates from '../projectAssignment/draggableProjectDates'; interface ColumnHeaderTitle { title: string; @@ -31,6 +32,7 @@ type CalendarHeaderProps = { editable?: boolean, projectInfo?: string, columnHeaderTitles: ColumnHeaderTitle[], + draggableDates?: boolean } const CalendarHeader: React.FC = ({ @@ -41,7 +43,9 @@ const CalendarHeader: React.FC = ({ userName, editable = false, projectInfo, - columnHeaderTitles }) => { + columnHeaderTitles, + draggableDates = false, +}) => { const [isEditing, setIsEditing] = useState(false); const { setDateRange, scrollToTodayFunction } = useGeneralDataContext(); const { totalActualHours, totalEstimatedHours, proposedEstimatedHours, maxTotalHours } = calculateTotalHoursPerWeek(assignments as AssignmentType[], months) @@ -99,8 +103,15 @@ const CalendarHeader: React.FC = ({ {months?.map((month) => { return month.weeks.map((week) => { - return ( + const isCurrentWeek = currentWeek === week.weekNumberOfTheYear && currentYear === month.year + return ( + {draggableDates && } = ({ {months?.map((month) => { return month.weeks.map((week, index) => { + const isCurrentWeek = currentWeek === week.weekNumberOfTheYear && currentYear === month.year return ( - +
{`W${week.weekNumberOfTheMonth}`} diff --git a/app/components/scrollingCalendar/constants.ts b/app/components/scrollingCalendar/constants.ts index f430898..f2d7b2d 100644 --- a/app/components/scrollingCalendar/constants.ts +++ b/app/components/scrollingCalendar/constants.ts @@ -22,3 +22,8 @@ export const MONTHS_COUNT: MonthsCount = { export const ACTUAL_HOURS = "actualHours"; export const ESTIMATED_HOURS = "estimatedHours"; + +export enum PROJECT_DATES_TYPE { + STARTS_ON = "startsOn", + ENDS_ON = "endsOn", +} diff --git a/app/components/scrollingCalendar/helpers.ts b/app/components/scrollingCalendar/helpers.ts index e5a9d05..7695d30 100644 --- a/app/components/scrollingCalendar/helpers.ts +++ b/app/components/scrollingCalendar/helpers.ts @@ -1158,3 +1158,24 @@ export const calculatePlannedHoursPerProject = ( return totalAssignments + totalForAssignment; }, 0); }; + +export const getWeekNumberAndYear = (dateString: string | null) => { + if (!dateString) { + return; + } + const date = DateTime.fromISO(dateString, { zone: "utc" }); + + if (!date.isValid) { + throw new Error("Invalid date format. Use ISO string format (YYYY-MM-DD)."); + } + + const monday = date.startOf("week"); + let weekNumber = monday.weekNumber; + if (monday.month === 12 && weekNumber === 1) { + weekNumber = 53; + } + return { + weekNumber: weekNumber, + year: monday.year, + }; +}; diff --git a/app/components/scrollingCalendar/scrollingCalendar.tsx b/app/components/scrollingCalendar/scrollingCalendar.tsx index 3b51e4b..433e85d 100644 --- a/app/components/scrollingCalendar/scrollingCalendar.tsx +++ b/app/components/scrollingCalendar/scrollingCalendar.tsx @@ -21,6 +21,7 @@ interface ScrollingCalendarProps { userName?: string; projectInfo?: string; editable?: boolean; + draggableDates?: boolean } export const ScrollingCalendar = ({ @@ -31,7 +32,8 @@ export const ScrollingCalendar = ({ avatarUrl, userName, projectInfo, - editable + editable, + draggableDates }: ScrollingCalendarProps) => { const [months, setMonths] = useState([]); const { dateRange, setDateRange } = useGeneralDataContext(); @@ -72,6 +74,7 @@ export const ScrollingCalendar = ({ title={title} projectInfo={projectInfo} editable={editable} + draggableDates={draggableDates} /> {React.Children.map(children, (child) => diff --git a/app/projects/[projectId]/page.tsx b/app/projects/[projectId]/page.tsx index 83545f2..07bef02 100644 --- a/app/projects/[projectId]/page.tsx +++ b/app/projects/[projectId]/page.tsx @@ -73,18 +73,17 @@ const ProjectPage: React.FC = () => { const startDate = singleProjectPage?.startsOn ? DateTime.fromISO(singleProjectPage.startsOn) : null; const endDate = singleProjectPage?.endsOn ? DateTime.fromISO(singleProjectPage.endsOn) : null; - if (!startDate || !endDate) { + if (!startDate && !endDate) { return 'Start Date - End Date'; } - const formattedStartDate = startDate.toFormat('d.MMM'); - const formattedEndDate = endDate.toFormat('d.MMM'); - const startYear = startDate.year; - const endYear = endDate.year; - if (startYear === endYear) { - return `${formattedStartDate}-${formattedEndDate}.${endYear}`; - } else { - return `${formattedStartDate}.${startYear}-${formattedEndDate} ${endYear}`; + const formattedStartDate = startDate?.toFormat('d.MMM') + const formattedEndDate = endDate?.toFormat('d.MMM') + const startYear = startDate?.year || '' + const endYear = endDate?.year || '' + if (startDate && startYear && startYear !== endYear) { + return `${formattedStartDate}.${startYear}-${formattedEndDate || 'End Date'} ${endYear}`; } + return `${formattedStartDate || 'Start Date'}-${formattedEndDate}.${endYear}`; } const columnHeaderTitles = [{ title: 'People', showIcon: true, onClick: () => addNewAssignmentRow() }] @@ -94,7 +93,14 @@ const ProjectPage: React.FC = () => { <> {singleProjectPage && projectList.length ? ( <> - + {sortedSingleProjectAssignments?.map((assignment: AssignmentType, rowIndex) => { return ( Date: Mon, 28 Oct 2024 14:48:10 +0200 Subject: [PATCH 2/2] added dragging dates --- .../projectAssignment/draggableProjectDates.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/components/projectAssignment/draggableProjectDates.tsx b/app/components/projectAssignment/draggableProjectDates.tsx index fab5ee9..233875c 100644 --- a/app/components/projectAssignment/draggableProjectDates.tsx +++ b/app/components/projectAssignment/draggableProjectDates.tsx @@ -19,9 +19,15 @@ const DraggableDates: FC = ({ monthLabel, }) => { const { projectList, setProjectList, singleProjectPage } = useProjectsDataContext() + let endDateWeekNumber; + let startDateWeekNumber; - const endDateWeekNumber = getWeekNumberAndYear(singleProjectPage?.endsOn) - const startDateWeekNumber = getWeekNumberAndYear(singleProjectPage?.startsOn) + if (singleProjectPage?.endsOn) { + endDateWeekNumber = getWeekNumberAndYear(singleProjectPage?.endsOn) + } + if (singleProjectPage?.startsOn) { + startDateWeekNumber = getWeekNumberAndYear(singleProjectPage?.startsOn) + } const isEndDateWeek = endDateWeekNumber?.weekNumber === weekNumberOfTheYear && monthYear === endDateWeekNumber?.year const isStartDateWeek = startDateWeekNumber?.weekNumber === weekNumberOfTheYear && monthYear === startDateWeekNumber?.year