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