Scheduler

This commit is contained in:
Sedat ÖZTÜRK 2025-12-02 21:15:09 +03:00
parent 712918121e
commit f5315a4aca
14 changed files with 784 additions and 4 deletions

View file

@ -177,6 +177,19 @@ public class GridOptionsDto : AuditedEntityDto<Guid>
set { GanttOptionJson = JsonSerializer.Serialize(value); } set { GanttOptionJson = JsonSerializer.Serialize(value); }
} }
[JsonIgnore]
public string SchedulerOptionJson { get; set; }
public SchedulerOptionDto SchedulerOptionDto
{
get
{
if (!string.IsNullOrEmpty(SchedulerOptionJson))
return JsonSerializer.Deserialize<SchedulerOptionDto>(SchedulerOptionJson);
return new SchedulerOptionDto();
}
set { SchedulerOptionJson = JsonSerializer.Serialize(value); }
}
[JsonIgnore] [JsonIgnore]
public string PagerOptionJson { get; set; } public string PagerOptionJson { get; set; }
public GridPagerOptionDto PagerOptionDto public GridPagerOptionDto PagerOptionDto

View file

@ -0,0 +1,27 @@
namespace Erp.Platform.ListForms;
/// <summary>
/// TreeList için özel ayarları içerir
/// </summary>
public class SchedulerOptionDto
{
/// <summary>
/// Text olarak gösterilecek field adı (örn: "title")
/// </summary>
public string TextField { get; set; }
/// <summary>
/// Başlangıç tarihinin tutulduğu field adı (örn: "startDate")
/// </summary>
public string StartDateField { get; set; }
/// <summary>
/// Bitiş tarihinin tutulduğu field adı (örn: "endDate")
/// </summary>
public string EndDateField { get; set; }
/// <summary>
/// Tüm gün etkinlikler için kullanılan field adı (örn: "allDay")
/// </summary>
public string AllDayField { get; set; }
}

View file

@ -48,6 +48,7 @@ public class ListFormEditTabs
public const string PivotForm = "pivot"; public const string PivotForm = "pivot";
public const string TreeForm = "tree"; public const string TreeForm = "tree";
public const string GanttForm = "gantt"; public const string GanttForm = "gantt";
public const string SchedulerForm = "scheduler";
public const string PagerForm = "pager"; public const string PagerForm = "pager";
public const string StateForm = "state"; public const string StateForm = "state";
public const string SubFormJsonRow = "subForm"; public const string SubFormJsonRow = "subForm";

View file

@ -155,6 +155,10 @@ public class ListFormsAppService : CrudAppService<
{ {
item.GanttOptionJson = JsonSerializer.Serialize(input.GanttOptionDto); item.GanttOptionJson = JsonSerializer.Serialize(input.GanttOptionDto);
} }
else if (input.EditType == ListFormEditTabs.SchedulerForm)
{
item.SchedulerOptionJson = JsonSerializer.Serialize(input.SchedulerOptionDto);
}
else if (input.EditType == ListFormEditTabs.PagerForm) else if (input.EditType == ListFormEditTabs.PagerForm)
{ {
item.PageSize = input.PageSize; item.PageSize = input.PageSize;

View file

@ -3607,6 +3607,12 @@
"en": "Gantt", "en": "Gantt",
"tr": "Gantt" "tr": "Gantt"
}, },
{
"resourceName": "Platform",
"key": "ListForms.ListFormEdit.TabScheduler",
"en": "Scheduler",
"tr": "Zamanlayıcı"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "ListForms.ListFormEdit.TabDetails", "key": "ListForms.ListFormEdit.TabDetails",

View file

@ -35,6 +35,7 @@ public class ListForm : FullAuditedEntity<Guid>
public string PivotOptionJson { get; set; } public string PivotOptionJson { get; set; }
public string TreeOptionJson { get; set; } // Tree yapisi ile ilgili ayarlar public string TreeOptionJson { get; set; } // Tree yapisi ile ilgili ayarlar
public string GanttOptionJson { get; set; } // Gantt yapisi ile ilgili ayarlar public string GanttOptionJson { get; set; } // Gantt yapisi ile ilgili ayarlar
public string SchedulerOptionJson { get; set; } // Scheduler yapisi ile ilgili ayarlar
public string FilterRowJson { get; set; } // Filtre ayarlari, Json olarak tutulur, donus sinifi FilterRowDto public string FilterRowJson { get; set; } // Filtre ayarlari, Json olarak tutulur, donus sinifi FilterRowDto
public string RowJson { get; set; } // Row ayarları, Json olarak tutulur, donus sinifi FilterRowDto public string RowJson { get; set; } // Row ayarları, Json olarak tutulur, donus sinifi FilterRowDto
public string HeaderFilterJson { get; set; } // Header filtreleme ayarlari, Json olarak tutulur public string HeaderFilterJson { get; set; } // Header filtreleme ayarlari, Json olarak tutulur

View file

@ -43,6 +43,7 @@ export const ListFormEditTabs = {
PivotForm: 'pivot', PivotForm: 'pivot',
TreeForm: 'tree', TreeForm: 'tree',
GanttForm: 'gantt', GanttForm: 'gantt',
SchedulerForm: 'scheduler',
PagerForm: 'pager', PagerForm: 'pager',
StateForm: 'state', StateForm: 'state',
SubForm: 'subForm', SubForm: 'subForm',
@ -92,6 +93,7 @@ export const tabVisibilityConfig: Record<string, string[]> = {
'pivots', 'pivots',
'tree', 'tree',
'gantt', 'gantt',
'scheduler',
'pager', 'pager',
'state', 'state',
'extrafilter', 'extrafilter',

View file

@ -404,6 +404,32 @@ export interface GanttOptionDto {
scaleType: GanttScaleType scaleType: GanttScaleType
} }
export interface SchedulerOptionDto {
textExpr?: string
startDateExpr?: string
endDateExpr?: string
allDayExpr?: string
recurrenceRuleExpr?: string
recurrenceExceptionExpr?: string
startDayHour?: number
endDayHour?: number
defaultView?: string
showAllDayPanel?: boolean
crossScrollingEnabled?: boolean
cellDuration?: number
firstDayOfWeek?: number
allowResizing?: boolean
allowDragging?: boolean
resources?: SchedulerResourceDto[]
}
export interface SchedulerResourceDto {
fieldExpr?: string
dataSource?: any[]
label?: string
useColorAsDefault?: boolean
}
export interface GridEditingDto { export interface GridEditingDto {
mode?: GridsEditMode mode?: GridsEditMode
refreshMode?: GridsEditRefreshMode refreshMode?: GridsEditRefreshMode
@ -504,6 +530,8 @@ export interface GridOptionsDto extends AuditedEntityDto<string> {
treeOptionDto: TreeOptionDto treeOptionDto: TreeOptionDto
ganttOptionJson?: string ganttOptionJson?: string
ganttOptionDto: GanttOptionDto ganttOptionDto: GanttOptionDto
schedulerOptionJson?: string
schedulerOptionDto: SchedulerOptionDto
pagerOptionJson?: string pagerOptionJson?: string
pagerOptionDto: GridPagerOptionDto pagerOptionDto: GridPagerOptionDto
editingOptionJson?: string editingOptionJson?: string
@ -851,6 +879,7 @@ export interface LayoutDto {
tree: boolean tree: boolean
chart: boolean chart: boolean
gantt: boolean gantt: boolean
scheduler: boolean
defaultLayout: ListViewLayoutType defaultLayout: ListViewLayoutType
cardLayoutColumn: number cardLayoutColumn: number
} }

View file

@ -51,6 +51,7 @@ import { tabVisibilityConfig } from '@/proxy/admin/list-form/options'
import FormTabSorting from './FormTabSorting' import FormTabSorting from './FormTabSorting'
import FormTabRow from './FormTabRow' import FormTabRow from './FormTabRow'
import FormTabGantt from './FormTabGantt' import FormTabGantt from './FormTabGantt'
import FormTabScheduler from './FormTabScheduler'
export interface FormEditProps { export interface FormEditProps {
onSubmit: ( onSubmit: (
@ -260,6 +261,9 @@ const FormEdit = () => {
{visibleTabs.includes('gantt') && ( {visibleTabs.includes('gantt') && (
<TabNav value="gantt">{translate('::ListForms.ListFormEdit.TabGantt')}</TabNav> <TabNav value="gantt">{translate('::ListForms.ListFormEdit.TabGantt')}</TabNav>
)} )}
{visibleTabs.includes('scheduler') && (
<TabNav value="scheduler">{translate('::ListForms.ListFormEdit.TabScheduler')}</TabNav>
)}
{visibleTabs.includes('subForms') && ( {visibleTabs.includes('subForms') && (
<TabNav value="subForms">{translate('::ListForms.ListFormEdit.SubForms')}</TabNav> <TabNav value="subForms">{translate('::ListForms.ListFormEdit.SubForms')}</TabNav>
)} )}
@ -367,6 +371,9 @@ const FormEdit = () => {
<TabContent value="gantt" className="px-2"> <TabContent value="gantt" className="px-2">
<FormTabGantt onSubmit={onSubmit} /> <FormTabGantt onSubmit={onSubmit} />
</TabContent> </TabContent>
<TabContent value="scheduler" className="px-2">
<FormTabScheduler onSubmit={onSubmit} />
</TabContent>
<TabContent value="pager" className="px-2"> <TabContent value="pager" className="px-2">
<FormTabPager onSubmit={onSubmit} /> <FormTabPager onSubmit={onSubmit} />
</TabContent> </TabContent>

View file

@ -0,0 +1,399 @@
import {
Button,
Notification,
Checkbox,
FormContainer,
FormItem,
Input,
Select,
toast,
Card,
} from '@/components/ui'
import { ListFormEditTabs } from '@/proxy/admin/list-form/options'
import { useStoreState } from '@/store'
import { useLocalization } from '@/utils/hooks/useLocalization'
import { Field, FieldProps, Form, Formik } from 'formik'
import * as Yup from 'yup'
import { FormEditProps } from './FormEdit'
import { SelectBoxOption } from '@/types/shared'
import { useEffect, useState } from 'react'
import { getListFormFields } from '@/services/admin/list-form-field.service'
import { groupBy } from 'lodash'
import { useParams } from 'react-router-dom'
const validationSchema = Yup.object().shape({})
const schedulerViewOptions = [
{ value: 'day', label: 'Day' },
{ value: 'week', label: 'Week' },
{ value: 'workWeek', label: 'Work Week' },
{ value: 'month', label: 'Month' },
{ value: 'timelineDay', label: 'Timeline Day' },
{ value: 'timelineWeek', label: 'Timeline Week' },
{ value: 'timelineMonth', label: 'Timeline Month' },
{ value: 'agenda', label: 'Agenda' },
]
const firstDayOfWeekOptions = [
{ value: 0, label: 'Sunday' },
{ value: 1, label: 'Monday' },
{ value: 2, label: 'Tuesday' },
{ value: 3, label: 'Wednesday' },
{ value: 4, label: 'Thursday' },
{ value: 5, label: 'Friday' },
{ value: 6, label: 'Saturday' },
]
function FormTabScheduler(props: FormEditProps) {
const { listFormCode } = useParams()
const { translate } = useLocalization()
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
const getFields = async () => {
if (!listFormCode) {
return
}
try {
const resp = await getListFormFields({
listFormCode,
sorting: 'ListOrderNo',
maxResultCount: 1000,
})
if (resp.data?.items) {
const fieldNames = groupBy(resp?.data?.items, 'fieldName')
setFieldList(
Object.keys(fieldNames).map((a) => ({
value: a,
label: a,
})),
)
}
} catch (error: any) {
toast.push(
<Notification type="danger" duration={2000}>
Alanlar getirilemedi
{error.toString()}
</Notification>,
{
placement: 'top-end',
},
)
}
}
useEffect(() => {
getFields()
}, [listFormCode])
const listFormValues = useStoreState((s) => s.admin.lists.values)
if (!listFormValues) {
return null
}
return (
<Formik
initialValues={listFormValues}
validationSchema={validationSchema}
onSubmit={async (values, formikHelpers) => {
await props.onSubmit(ListFormEditTabs.SchedulerForm, values, formikHelpers)
}}
>
{({ touched, errors, isSubmitting, values }) => (
<Form>
<FormContainer size="sm">
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<Card>
<h5 className="mb-4">{translate('::SchedulerOptions.BasicSettings')}</h5>
<FormItem
label={translate('::SchedulerOptions.TextField')}
invalid={
errors.schedulerOptionDto?.textExpr && touched.schedulerOptionDto?.textExpr
}
errorMessage={errors.schedulerOptionDto?.textExpr}
>
<Field type="text" name="schedulerOptionDto.textExpr">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option) => option.value === values.schedulerOptionDto?.textExpr,
)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::SchedulerOptions.StartDateField')}
invalid={
errors.schedulerOptionDto?.startDateExpr &&
touched.schedulerOptionDto?.startDateExpr
}
errorMessage={errors.schedulerOptionDto?.startDateExpr}
>
<Field type="text" name="schedulerOptionDto.startDateExpr">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option) => option.value === values.schedulerOptionDto?.startDateExpr,
)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::SchedulerOptions.EndDateField')}
invalid={
errors.schedulerOptionDto?.endDateExpr &&
touched.schedulerOptionDto?.endDateExpr
}
errorMessage={errors.schedulerOptionDto?.endDateExpr}
>
<Field type="text" name="schedulerOptionDto.endDateExpr">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option) => option.value === values.schedulerOptionDto?.endDateExpr,
)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::SchedulerOptions.AllDayField')}
invalid={
errors.schedulerOptionDto?.allDayExpr && touched.schedulerOptionDto?.allDayExpr
}
errorMessage={errors.schedulerOptionDto?.allDayExpr}
>
<Field type="text" name="schedulerOptionDto.allDayExpr">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option) => option.value === values.schedulerOptionDto?.allDayExpr,
)}
/>
)}
</Field>
</FormItem>
</Card>
<Card className="mt-4">
<h5 className="mb-4">{translate('::SchedulerOptions.RecurrenceSettings')}</h5>
<FormItem
label={translate('::SchedulerOptions.RecurrenceRuleField')}
invalid={
errors.schedulerOptionDto?.recurrenceRuleExpr &&
touched.schedulerOptionDto?.recurrenceRuleExpr
}
errorMessage={errors.schedulerOptionDto?.recurrenceRuleExpr}
>
<Field type="text" name="schedulerOptionDto.recurrenceRuleExpr">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option) =>
option.value === values.schedulerOptionDto?.recurrenceRuleExpr,
)}
/>
)}
</Field>
</FormItem>
<FormItem
label={translate('::SchedulerOptions.RecurrenceExceptionField')}
invalid={
errors.schedulerOptionDto?.recurrenceExceptionExpr &&
touched.schedulerOptionDto?.recurrenceExceptionExpr
}
errorMessage={errors.schedulerOptionDto?.recurrenceExceptionExpr}
>
<Field type="text" name="schedulerOptionDto.recurrenceExceptionExpr">
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={fieldList}
value={fieldList.find(
(option) =>
option.value === values.schedulerOptionDto?.recurrenceExceptionExpr,
)}
/>
)}
</Field>
</FormItem>
</Card>
<Card className="mt-4">
<h5 className="mb-4">{translate('::SchedulerOptions.ViewSettings')}</h5>
<FormItem label={translate('::SchedulerOptions.DefaultView')}>
<Field name="schedulerOptionDto.defaultView">
{({ field, form }: FieldProps) => (
<Select
field={field}
form={form}
options={schedulerViewOptions}
value={schedulerViewOptions.find(
(option) => option.value === values.schedulerOptionDto?.defaultView,
)}
/>
)}
</Field>
</FormItem>
<FormItem label={translate('::SchedulerOptions.FirstDayOfWeek')}>
<Field name="schedulerOptionDto.firstDayOfWeek">
{({ field, form }: FieldProps) => (
<Select
field={field}
form={form}
options={firstDayOfWeekOptions}
value={firstDayOfWeekOptions.find(
(option) => option.value === values.schedulerOptionDto?.firstDayOfWeek,
)}
/>
)}
</Field>
</FormItem>
<FormItem label={translate('::SchedulerOptions.StartDayHour')}>
<Field name="schedulerOptionDto.startDayHour">
{({ field, form }: FieldProps) => (
<Input
type="number"
field={field}
form={form}
placeholder="8"
min={0}
max={23}
/>
)}
</Field>
</FormItem>
<FormItem label={translate('::SchedulerOptions.EndDayHour')}>
<Field name="schedulerOptionDto.endDayHour">
{({ field, form }: FieldProps) => (
<Input
type="number"
field={field}
form={form}
placeholder="18"
min={0}
max={24}
/>
)}
</Field>
</FormItem>
<FormItem label={translate('::SchedulerOptions.CellDuration')}>
<Field name="schedulerOptionDto.cellDuration">
{({ field, form }: FieldProps) => (
<Input
type="number"
field={field}
form={form}
placeholder="30"
min={5}
step={5}
/>
)}
</Field>
</FormItem>
</Card>
<Card className="mt-4">
<h5 className="mb-4">{translate('::SchedulerOptions.InteractionSettings')}</h5>
<FormItem>
<Field name="schedulerOptionDto.showAllDayPanel">
{({ field, form }: FieldProps) => (
<Checkbox
{...field}
checked={values.schedulerOptionDto?.showAllDayPanel ?? true}
>
{translate('::SchedulerOptions.ShowAllDayPanel')}
</Checkbox>
)}
</Field>
</FormItem>
<FormItem>
<Field name="schedulerOptionDto.crossScrollingEnabled">
{({ field, form }: FieldProps) => (
<Checkbox
{...field}
checked={values.schedulerOptionDto?.crossScrollingEnabled ?? false}
>
{translate('::SchedulerOptions.CrossScrollingEnabled')}
</Checkbox>
)}
</Field>
</FormItem>
<FormItem>
<Field name="schedulerOptionDto.allowResizing">
{({ field, form }: FieldProps) => (
<Checkbox
{...field}
checked={values.schedulerOptionDto?.allowResizing ?? false}
>
{translate('::SchedulerOptions.AllowResizing')}
</Checkbox>
)}
</Field>
</FormItem>
<FormItem>
<Field name="schedulerOptionDto.allowDragging">
{({ field, form }: FieldProps) => (
<Checkbox
{...field}
checked={values.schedulerOptionDto?.allowDragging ?? false}
>
{translate('::SchedulerOptions.AllowDragging')}
</Checkbox>
)}
</Field>
</FormItem>
</Card>
</div>
<FormItem className="mt-4">
<Button variant="solid" type="submit" loading={isSubmitting}>
{translate('::Submit')}
</Button>
</FormItem>
</FormContainer>
</Form>
)}
</Formik>
)
}
export default FormTabScheduler

View file

@ -1,6 +1,6 @@
export type ChartOperation = '' | 'select' | 'insert' | 'update' | 'delete' export type ChartOperation = '' | 'select' | 'insert' | 'update' | 'delete'
export type ChartDialogType = '' | 'pane' | 'serie' | 'annotation' | 'axis' export type ChartDialogType = '' | 'pane' | 'serie' | 'annotation' | 'axis'
export type ListViewLayoutType = 'grid' | 'card' | 'pivot' | 'tree' | 'chart' | 'gantt' export type ListViewLayoutType = 'grid' | 'card' | 'pivot' | 'tree' | 'chart' | 'gantt' | 'scheduler'
export const layoutTypes = { export const layoutTypes = {
grid: 'Grid', grid: 'Grid',
@ -9,4 +9,5 @@ export const layoutTypes = {
tree: 'Tree', tree: 'Tree',
chart: 'Chart', chart: 'Chart',
gantt: 'Gantt', gantt: 'Gantt',
scheduler: 'Scheduler',
} }

View file

@ -2,7 +2,7 @@ import { useParams, useSearchParams } from 'react-router-dom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import Container from '@/components/shared/Container' import Container from '@/components/shared/Container'
import Grid from './Grid' import Grid from './Grid'
import { FaChartArea, FaList, FaSitemap, FaTable, FaTh, FaUser } from 'react-icons/fa' import { FaChartArea, FaList, FaSitemap, FaTable, FaTh, FaUser, FaCalendarAlt } from 'react-icons/fa'
import { useStoreActions, useStoreState } from '@/store/store' import { useStoreActions, useStoreState } from '@/store/store'
import classNames from 'classnames' import classNames from 'classnames'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
@ -17,6 +17,7 @@ import Chart from './Chart'
import Card from './Card' import Card from './Card'
import { FaChartGantt } from 'react-icons/fa6' import { FaChartGantt } from 'react-icons/fa6'
import GanttView from './GanttView' import GanttView from './GanttView'
import SchedulerView from './SchedulerView'
const List = () => { const List = () => {
const params = useParams() const params = useParams()
@ -95,9 +96,25 @@ const List = () => {
)} )}
<div className="flex gap-1"> <div className="flex gap-1">
{gridDto?.gridOptions?.layoutDto.scheduler &&
gridDto?.gridOptions?.schedulerOptionDto?.textExpr &&
gridDto?.gridOptions?.schedulerOptionDto?.startDateExpr && (
<Button
size="xs"
variant={viewMode === 'scheduler' ? 'solid' : 'default'}
onClick={() => {
setViewMode('scheduler')
setStates({ listFormCode, layout: 'scheduler' })
}}
title="Scheduler Görünümü"
>
<FaCalendarAlt className="w-4 h-4" />
</Button>
)}
{gridDto?.gridOptions?.layoutDto.gantt && {gridDto?.gridOptions?.layoutDto.gantt &&
gridDto?.gridOptions?.treeOptionDto?.parentIdExpr && gridDto?.gridOptions?.ganttOptionDto?.parentIdExpr &&
gridDto?.gridOptions?.treeOptionDto?.titleExpr && ( gridDto?.gridOptions?.ganttOptionDto?.titleExpr && (
<Button <Button
size="xs" size="xs"
variant={viewMode === 'gantt' ? 'solid' : 'default'} variant={viewMode === 'gantt' ? 'solid' : 'default'}
@ -192,6 +209,13 @@ const List = () => {
gridDto={gridDto} gridDto={gridDto}
refreshGridDto={refreshGridDto} refreshGridDto={refreshGridDto}
/> />
) : viewMode === 'scheduler' ? (
<SchedulerView
listFormCode={listFormCode}
searchParams={searchParams}
isSubForm={false}
gridDto={gridDto}
/>
) : viewMode === 'gantt' ? ( ) : viewMode === 'gantt' ? (
<GanttView <GanttView
listFormCode={listFormCode} listFormCode={listFormCode}

View file

@ -0,0 +1,264 @@
import Container from '@/components/shared/Container'
import { DX_CLASSNAMES } from '@/constants/app.constant'
import { GridDto } from '@/proxy/form/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import Scheduler, { Editing, Resource, SchedulerRef, View } from 'devextreme-react/scheduler'
import { useCallback, useEffect, useRef, useState } from 'react'
import { Helmet } from 'react-helmet'
import { getList } from '@/services/form.service'
import { useListFormCustomDataSource } from './useListFormCustomDataSource'
import { addCss, addJs } from './Utils'
import { layoutTypes } from '../admin/listForm/edit/types'
import WidgetGroup from '@/components/ui/Widget/WidgetGroup'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePWA } from '@/utils/hooks/usePWA'
import CustomStore from 'devextreme/data/custom_store'
import { Loading } from '@/components/shared'
interface SchedulerViewProps {
listFormCode: string
searchParams?: URLSearchParams
isSubForm?: boolean
level?: number
refreshData?: () => Promise<void>
gridDto?: GridDto
}
const SchedulerView = (props: SchedulerViewProps) => {
const { listFormCode, searchParams, isSubForm, level, gridDto: extGridDto } = props
const { translate } = useLocalization()
const isPwaMode = usePWA()
const schedulerRef = useRef<SchedulerRef>()
const refListFormCode = useRef('')
const widgetGroupRef = useRef<HTMLDivElement>(null)
const [schedulerDataSource, setSchedulerDataSource] = useState<CustomStore<any, any>>()
const [gridDto, setGridDto] = useState<GridDto>()
const [widgetGroupHeight, setWidgetGroupHeight] = useState(0)
const [currentView, setCurrentView] = useState<string>('week')
const layout = layoutTypes.scheduler || 'scheduler'
useEffect(() => {
const initializeScheduler = async () => {
const response = await getList({ listFormCode })
setGridDto(response.data)
}
if (extGridDto === undefined) {
initializeScheduler()
} else {
setGridDto(extGridDto)
}
setCurrentView(extGridDto?.gridOptions.schedulerOptionDto?.defaultView || 'week')
}, [listFormCode, extGridDto])
useEffect(() => {
if (schedulerRef?.current) {
const instance = schedulerRef?.current?.instance()
if (instance) {
instance.option('dataSource', undefined)
}
}
if (refListFormCode.current !== listFormCode) {
// Reset state if needed
}
}, [listFormCode])
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef: schedulerRef })
useEffect(() => {
if (!gridDto) {
return
}
// Set js and css
const grdOpt = gridDto.gridOptions
if (grdOpt.customJsSources.length) {
for (const js of grdOpt.customJsSources) {
addJs(js)
}
}
if (grdOpt.customStyleSources.length) {
for (const css of grdOpt.customStyleSources) {
addCss(css)
}
}
}, [gridDto])
useEffect(() => {
if (!gridDto) return
const dataSource = createSelectDataSource(
gridDto.gridOptions,
listFormCode,
searchParams,
layout,
undefined,
)
setSchedulerDataSource(dataSource)
}, [gridDto, searchParams, createSelectDataSource])
useEffect(() => {
refListFormCode.current = listFormCode
}, [listFormCode])
// WidgetGroup yüksekliğini hesapla
useEffect(() => {
const calculateWidgetHeight = () => {
if (widgetGroupRef.current) {
const height = widgetGroupRef.current.offsetHeight
setWidgetGroupHeight(height)
}
}
calculateWidgetHeight()
const resizeObserver = new ResizeObserver(calculateWidgetHeight)
if (widgetGroupRef.current) {
resizeObserver.observe(widgetGroupRef.current)
}
return () => {
resizeObserver.disconnect()
}
}, [gridDto?.widgets])
const settingButtonClick = useCallback(() => {
window.open(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode),
isPwaMode ? '_self' : '_blank',
)
}, [listFormCode, isPwaMode])
const onCurrentViewChange = useCallback((value: string) => {
setCurrentView(value)
}, [])
const onAppointmentFormOpening = useCallback((e: any) => {
// Özelleştirme yapılabilir
}, [])
return (
<>
<div ref={widgetGroupRef}>
<WidgetGroup widgetGroups={gridDto?.widgets ?? []} />
</div>
<Container className={DX_CLASSNAMES}>
{!isSubForm && (
<Helmet
titleTemplate="%s | Erp Platform"
title={translate('::' + gridDto?.gridOptions.title)}
defaultTitle="Erp Platform"
>
<link rel="stylesheet" href="/css/scheduler/dx.common.css" />
<link rel="stylesheet" href="/css/scheduler/dx.light.css" />
</Helmet>
)}
{!gridDto && (
<div className="p-4">
<Loading loading>Loading scheduler configuration...</Loading>
</div>
)}
{gridDto && !schedulerDataSource && (
<div className="p-4">
<Loading loading>Loading data source...</Loading>
</div>
)}
{gridDto && schedulerDataSource && (
<>
<div className="p-1">
<Scheduler
ref={schedulerRef as any}
key={`Scheduler-${listFormCode}-${schedulerDataSource ? 'loaded' : 'loading'}`}
id={'Scheduler-' + listFormCode}
dataSource={schedulerDataSource}
textExpr={gridDto.gridOptions.schedulerOptionDto?.textExpr || 'text'}
startDateExpr={gridDto.gridOptions.schedulerOptionDto?.startDateExpr || 'startDate'}
endDateExpr={gridDto.gridOptions.schedulerOptionDto?.endDateExpr || 'endDate'}
allDayExpr={gridDto.gridOptions.schedulerOptionDto?.allDayExpr}
recurrenceRuleExpr={gridDto.gridOptions.schedulerOptionDto?.recurrenceRuleExpr}
recurrenceExceptionExpr={
gridDto.gridOptions.schedulerOptionDto?.recurrenceExceptionExpr
}
startDayHour={gridDto.gridOptions.schedulerOptionDto?.startDayHour || 8}
endDayHour={gridDto.gridOptions.schedulerOptionDto?.endDayHour || 18}
currentView={currentView}
onCurrentViewChange={onCurrentViewChange}
onAppointmentFormOpening={onAppointmentFormOpening}
height={
gridDto.gridOptions.height > 0
? gridDto.gridOptions.height
: gridDto.gridOptions.fullHeight
? `calc(100vh - ${170 + widgetGroupHeight}px)`
: undefined
}
showAllDayPanel={gridDto.gridOptions.schedulerOptionDto?.showAllDayPanel ?? true}
crossScrollingEnabled={
gridDto.gridOptions.schedulerOptionDto?.crossScrollingEnabled ?? false
}
cellDuration={gridDto.gridOptions.schedulerOptionDto?.cellDuration || 30}
firstDayOfWeek={
(gridDto.gridOptions.schedulerOptionDto?.firstDayOfWeek as
| 0
| 1
| 2
| 3
| 4
| 5
| 6) || 1
}
adaptivityEnabled={true}
>
<Editing
allowAdding={gridDto.gridOptions.editingOptionDto?.allowAdding ?? false}
allowUpdating={gridDto.gridOptions.editingOptionDto?.allowUpdating ?? false}
allowDeleting={gridDto.gridOptions.editingOptionDto?.allowDeleting ?? false}
allowResizing={gridDto.gridOptions.schedulerOptionDto?.allowResizing ?? false}
allowDragging={gridDto.gridOptions.schedulerOptionDto?.allowDragging ?? false}
/>
<View type="day" name={translate('::Day')} />
<View type="week" name={translate('::Week')} />
<View type="workWeek" name={translate('::WorkWeek')} />
<View type="month" name={translate('::Month')} />
<View
type="timelineDay"
name={translate('::TimelineDay')}
maxAppointmentsPerCell="unlimited"
/>
<View
type="timelineWeek"
name={translate('::TimelineWeek')}
maxAppointmentsPerCell="unlimited"
/>
<View
type="timelineMonth"
name={translate('::TimelineMonth')}
maxAppointmentsPerCell="unlimited"
/>
<View type="agenda" name={translate('::Agenda')} />
{gridDto.gridOptions.schedulerOptionDto?.resources?.map((resource, index) => (
<Resource
key={index}
fieldExpr={resource.fieldExpr}
dataSource={resource.dataSource}
label={resource.label}
useColorAsDefault={resource.useColorAsDefault}
/>
))}
</Scheduler>
</div>
</>
)}
</Container>
</>
)
}
export default SchedulerView

View file

@ -10,6 +10,7 @@ import { dynamicFetch } from '@/services/form.service'
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant' import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
import { TreeListRef } from 'devextreme-react/cjs/tree-list' import { TreeListRef } from 'devextreme-react/cjs/tree-list'
import { GanttRef } from 'devextreme-react/cjs/gantt' import { GanttRef } from 'devextreme-react/cjs/gantt'
import { SchedulerRef } from 'devextreme-react/cjs/scheduler'
const filteredGridPanelColor = 'rgba(10, 200, 10, 0.5)' // kullanici tanimli filtre ile filtrelenmis gridin paneline ait renk const filteredGridPanelColor = 'rgba(10, 200, 10, 0.5)' // kullanici tanimli filtre ile filtrelenmis gridin paneline ait renk
@ -21,6 +22,7 @@ const useListFormCustomDataSource = ({
| MutableRefObject<PivotGridRef | undefined> | MutableRefObject<PivotGridRef | undefined>
| MutableRefObject<TreeListRef<any, any> | undefined> | MutableRefObject<TreeListRef<any, any> | undefined>
| MutableRefObject<GanttRef | undefined> | MutableRefObject<GanttRef | undefined>
| MutableRefObject<SchedulerRef | undefined>
}) => { }) => {
const createSelectDataSource = useCallback( const createSelectDataSource = useCallback(
( (