Gantt schedule timeline calendar open calendar open the calendar and drop it to the element

28 views Asked by At

I use nextjs and gantt-schedule-timeline-calendar,

please help me when opening the calendar, do not show the beginning of the calendar but move it to the current day. and I have elements inside the calendar that I receive from a request. I have a modal when I double-click on an element and when I make a change in it I need to change the ui, I did a refetch for this and it worked, but after that I need to open the calendar again and direct to the element that was changed

first component

'use client'
import React, { useCallback, useEffect, useState } from 'react'
import 'gantt-schedule-timeline-calendar/dist/style.css'
import { useQuery } from '@tanstack/react-query'
import { GetGanttJobsSuccessResponse, GetJobSuccessResponse, getGanttJobs } from '@/fetch/jobs'

import { initializeGSTC } from './InitializeGant'
import { useModalWindow } from '../../_store/modalStore'
import { EditJobModal } from '../../(tables)/project/[uid]/components/job/modals/editJobModal'
import { ProjectJobType } from '@/types/projectTable'
import { useGantFilter } from '../_store/gantFilterStore'
import { GantPopoverFilter } from './filter/gant-popover-filter'
import { Spinner } from '../../components/loader'
import { GantNavFilter } from './filter/gant-navfilter'
import { GroupingValue, SortingValue } from '../types/Gantt'
import GanttMain from './GanttMain'

const Gantt: React.FC = () => {
  const [closeModal, openModal] = useModalWindow((state) => [state.closeModal, state.openModal])
  const [currentJob, setCurrentJob] = useState<null | GetJobSuccessResponse>(null)
  const [filter, setFilter, setSearch] = useGantFilter((state) => [state.filter, state.setFilter, state.setSearch])
  const [groupingValue, setGroupingValue] = useState<GroupingValue>(GroupingValue.PROJECTS)
  const [sortingValue, setSortingValue] = useState<SortingValue>(SortingValue.WITHOUT_SORTING)

  useEffect(() => {
    window.localStorage.setItem('gantFilter', filter)
  }, [filter])

  const {
    data: jobs,
    isRefetching,
    refetch,
  } = useQuery({
    queryFn: () => getGanttJobs(`${filter ? `${filter}` : window?.localStorage.getItem('gantFilter')}`),
    queryKey: ['jobs_gantt'],
    refetchOnWindowFocus: false,
  })

  const handleCloseModal = () => {
    closeModal()
  }

  useEffect(() => {
    if (currentJob) {
      openModal({
        body: <EditJobModal job={currentJob as ProjectJobType} refetchGant={refetch} />,
        onInteractOutside: handleCloseModal,
      })
    }
  }, [currentJob])

  return (
    <div className='h-full'>
      <div className=' w-full border-b-[0.5px] border-solid border-b-[#E1E2E5] px-5 py-3 font-bold sm:ml-0 s:ml-0'>
        Диаграмма Ганта
      </div>

      <div className='flex w-full items-center justify-end border-b-[0.5px] border-solid border-b-[#E1E2E5]'>
        <div className='flex w-fit gap-1 py-3 pr-5'>
          <GantPopoverFilter filter={filter} setFilter={setFilter} />
          <GantNavFilter
            setSearch={setSearch}
            uid=''
            groupingValue={groupingValue}
            setGroupingValue={setGroupingValue}
            sortingValue={sortingValue}
            setSortingValue={setSortingValue}
          />
        </div>
      </div>
      {jobs && isRefetching === false ? (
        <GanttMain
          key={groupingValue + sortingValue}
          filter={filter}
          jobs={jobs}
          setCurrentJob={setCurrentJob}
          groupingValue={groupingValue}
          sortingValue={sortingValue}
          isRefetching={isRefetching}
        />
      ) : (
        <div className='flex h-full w-full justify-center'>
          <Spinner />
        </div>
      )}
    </div>
  )
}

export default Gantt

Second component

'use client'
import React, { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'
import 'gantt-schedule-timeline-calendar/dist/style.css'
import { GetGanttJobsSuccessResponse, GetJobSuccessResponse, getGanttJobs, updateJob } from '@/fetch/jobs'

import { initializeGSTC } from './InitializeGant'
import { GroupingValue, SortingValue } from '../types/Gantt'
import { useMutation } from '@tanstack/react-query'

interface GanttMainProps {
  filter: string
  jobs: GetGanttJobsSuccessResponse
  setCurrentJob: Dispatch<SetStateAction<null | GetJobSuccessResponse>>
  groupingValue: GroupingValue
  sortingValue: SortingValue
  isRefetching: boolean
}

const GanttMain: React.FC<GanttMainProps> = ({
  filter,
  jobs,
  setCurrentJob,
  groupingValue,
  isRefetching,
  sortingValue,
}) => {
  const elementRef = React.useRef<HTMLDivElement>(null)

  const { mutate: updateJobsMutation, isPending } = useMutation({
    mutationFn: updateJob,
  })
  
  useEffect(() => {
    window.localStorage.setItem('gantFilter', filter)
  }, [filter])

  const callback = useCallback(
    (element: HTMLDivElement | null) => {
      initializeGSTC(
        element,
        jobs as GetGanttJobsSuccessResponse,
        setCurrentJob,
        groupingValue,
        sortingValue,
        updateJobsMutation,
      )
    },
    [isRefetching, jobs],
  )

  useEffect(() => {
    return () => {
      const element = elementRef.current
      if (element) {
        element.innerHTML = ''
      }
    }
  }, [])

  return <div id='gstc' ref={callback}></div>
}

export default GanttMain

third

import { GetGanttJobsSuccessResponse, GetJobSuccessResponse, UpdateJobSuccessResp, getJob } from '@/fetch/jobs'
import { LICENSE_KEY, findMinMaxDates, millisecondsToDate } from '../utils/gantt'
import { ColumnsData, Config, Template, TemplateVariables } from 'gantt-schedule-timeline-calendar'
import { Dispatch, SetStateAction } from 'react'
import { ArrowLeftGantt, ArrowRightGantt } from '../icons/Arrows'
import { generateItemsForDaysView } from './generateGant/generateItems'
import { generateRows } from './generateGant/generateRows'
import { dateSlot, itemSlot, rowSlot } from './slots/slots'
import { GroupingValue, SortingValue } from '../types/Gantt'
import { Options } from 'gantt-schedule-timeline-calendar/dist/plugins/item-resizing.esm.min.js'
import { UseMutateFunction } from '@tanstack/react-query'
import { toast } from 'react-toastify'
export async function initializeGSTC(
  element: HTMLDivElement | null,
  jobs: GetGanttJobsSuccessResponse,
  setCurrentJob: Dispatch<SetStateAction<null | GetJobSuccessResponse>>,
  groupingValue: GroupingValue,
  sortingValue: SortingValue,
  updateJobsMutation: any,
) {
  if (!element || !jobs) return

  const GSTC = (await import('gantt-schedule-timeline-calendar')).default
  const TimelinePointer = (await import('gantt-schedule-timeline-calendar/dist/plugins/timeline-pointer.esm.min.js'))
    .Plugin
  const Selection = (await import('gantt-schedule-timeline-calendar/dist/plugins/selection.esm.min.js')).Plugin
  const CalendarScroll = (await import('gantt-schedule-timeline-calendar/dist/plugins/calendar-scroll.esm.min.js'))
    .Plugin

  const ExportImage = (await import('gantt-schedule-timeline-calendar/dist/plugins/export-image.esm.min.js')).Plugin
  const ExportPDF = (await import('gantt-schedule-timeline-calendar/dist/plugins/export-pdf.esm.min.js')).Plugin
  const ItemResizing = (await import('gantt-schedule-timeline-calendar/dist/plugins/item-resizing.esm.min.js')).Plugin

  const GSTCID = GSTC.api.GSTCID
console.log('jobsjobs',jobs);

  const { minStartDate, maxFinishDate } = findMinMaxDates(jobs)
  const startDate = GSTC.api.date(minStartDate)
  const endDate = GSTC.api.date(maxFinishDate).endOf('day')

  const itemsForDaysView = await generateItemsForDaysView({ jobs, groupingValue, sortingValue, minStartDate })
  const gantRows = await generateRows({ jobs, groupingValue, sortingValue })

  async function onItemClick(ev: any) {
    const itemElement = ev.target.closest('.gstc__chart-timeline-items-row-item')

    const itemId = itemElement.dataset.gstcid
    const item = gstc.api.getItem(itemId)
    const jobId = item.id.slice(7)
console.log('itemElementitemElement',itemElement);

    const dataaaa: GetJobSuccessResponse = await getJob(jobId)

    if (dataaaa) {
      setCurrentJob(dataaaa)
    }
  }
  const snapTime = true
  function snapStart({ startTime, vido }: any) {
    if (!snapTime) return startTime
    const date = vido.api.time.findOrCreateMainDateAtTime(startTime.valueOf())
    return date.leftGlobalDate
  }
  function snapEnd({ endTime, vido }: any) {
    if (!snapTime) return endTime
    const date = vido.api.time.findOrCreateMainDateAtTime(endTime.valueOf())

    return date.rightGlobalDate
  }
  const itemResizeOptions: Options = {
    threshold: 10,
    snapToTime: {
      start: snapStart,
      end: snapEnd,
    },
    events: {
      onResize({ items }) {
        // for (const item of items.after) {
        //  return items.before;
        // }
        return items.after
      },
      onEnd({ items }) {
        const item = items.after[0]

        updateJobsMutation(
          {
            job_uid: item.id.slice(7),
            body: {
              update_job_data: {
                start_at: millisecondsToDate(item.time.start),
                finish_at: millisecondsToDate(item.time.end),
              },
            },
          },
          {
            onSuccess: () => {
              toast('Время изменен', {
                type: 'success',
                position: 'top-right',
              })
            },
            onError: () => {
              toast('Не удалось изменить время', {
                type: 'error',
                position: 'top-right',
              })
            },
          },
        )
        return items.after
      },
    },
  }
  const chartTimelineItemsRowItemTemplate: Template = ({
    className,
    labelClassName,
    styleMap,
    cache,
    shouldDetach,
    cutterLeft,
    cutterRight,
    getContent,
    actions,
    slots,
    html,
    vido,
    props,
  }: TemplateVariables) => {
    const detach = shouldDetach || !props || !props.item
    return cache(
      detach
        ? null
        : slots.html(
            'outer',
            html`
              <div
                class=${className}
                data-gstcid=${props.item.id}
                data-actions=${actions()}
                style=${styleMap.directive()}
                @dblclick=${onItemClick}
              >
                ${slots.html(
                  'inner',
                  html`
                    ${cutterLeft()}
                    <div class=${labelClassName}>${slots.html('content', getContent())}</div>
                    ${cutterRight()}
                  `,
                )}
              </div>
            `,
          ),
    )
  }
  const columnsData: ColumnsData = {
    [GSTCID('label')]: {
      id: GSTCID('label'),
      data: 'label',
      sortable: 'label',
      expander: true,
      isHTML: false,
      width: 315,
      header: {
        content: 'Список работ',
      },
    },
  }
  const config: Config = {
    licenseKey: LICENSE_KEY,
    innerHeight: 700,
    plugins: [
      TimelinePointer(),
      Selection({
        events: {
          onEnd(selected) {
            console.log('Selected', selected)
            return selected
          },
        },
      }),
      CalendarScroll(),
      ItemResizing(itemResizeOptions),
      ExportImage(),
      ExportPDF(),
    ],

    list: {
      row: {
        height: 48,
      },
      rows: gantRows,
      columns: {
        data: columnsData,
      },
      expander: {
        icons: {
          child: '',
        },
      },
    },
    chart: {
      time: {
        from: startDate.valueOf(),
        to: endDate.valueOf(),
        // checkCurrentDateInterval: GSTC.api.date('2024-01-01').valueOf()
      },
      item: {
        height: 36,
        cutIcons: {
          left: ArrowLeftGantt(),
          right: ArrowRightGantt(),
        },
        gap: {
          top: 7,
        },
      },
      items: itemsForDaysView,
    },
    scroll: {
      vertical: { precise: true, byPixels: true },
      horizontal: { precise: true, byPixels: true },
    },
    slots: {
      'chart-timeline-items-row-item': { content: [itemSlot] },
      'list-column-row': { content: [rowSlot] },
      'chart-calendar-date': { outer: [dateSlot] },
    },
    templates: {
      'chart-timeline-items-row-item': chartTimelineItemsRowItemTemplate,
    },
  }

  const state = GSTC.api.stateFromConfig(config)

  const gstc = GSTC({
    element,
    state,
  })
}

[first render[elementsmodal](https://i.stack.imgur.com/DpgEJ.png)](https://i.stack.imgur.com/q1lvv.png)

I tried to find in the config how to set the default position. Have not found. I also looked in the documentation for how to solve these problems, but there is nothing

0

There are 0 answers