Scheduler
This commit is contained in:
parent
712918121e
commit
f5315a4aca
14 changed files with 784 additions and 4 deletions
|
|
@ -177,6 +177,19 @@ public class GridOptionsDto : AuditedEntityDto<Guid>
|
|||
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]
|
||||
public string PagerOptionJson { get; set; }
|
||||
public GridPagerOptionDto PagerOptionDto
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +48,7 @@ public class ListFormEditTabs
|
|||
public const string PivotForm = "pivot";
|
||||
public const string TreeForm = "tree";
|
||||
public const string GanttForm = "gantt";
|
||||
public const string SchedulerForm = "scheduler";
|
||||
public const string PagerForm = "pager";
|
||||
public const string StateForm = "state";
|
||||
public const string SubFormJsonRow = "subForm";
|
||||
|
|
|
|||
|
|
@ -154,6 +154,10 @@ public class ListFormsAppService : CrudAppService<
|
|||
else if (input.EditType == ListFormEditTabs.GanttForm)
|
||||
{
|
||||
item.GanttOptionJson = JsonSerializer.Serialize(input.GanttOptionDto);
|
||||
}
|
||||
else if (input.EditType == ListFormEditTabs.SchedulerForm)
|
||||
{
|
||||
item.SchedulerOptionJson = JsonSerializer.Serialize(input.SchedulerOptionDto);
|
||||
}
|
||||
else if (input.EditType == ListFormEditTabs.PagerForm)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3607,6 +3607,12 @@
|
|||
"en": "Gantt",
|
||||
"tr": "Gantt"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListFormEdit.TabScheduler",
|
||||
"en": "Scheduler",
|
||||
"tr": "Zamanlayıcı"
|
||||
},
|
||||
{
|
||||
"resourceName": "Platform",
|
||||
"key": "ListForms.ListFormEdit.TabDetails",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public class ListForm : FullAuditedEntity<Guid>
|
|||
public string PivotOptionJson { get; set; }
|
||||
public string TreeOptionJson { get; set; } // Tree 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 RowJson { get; set; } // Row ayarları, Json olarak tutulur, donus sinifi FilterRowDto
|
||||
public string HeaderFilterJson { get; set; } // Header filtreleme ayarlari, Json olarak tutulur
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const ListFormEditTabs = {
|
|||
PivotForm: 'pivot',
|
||||
TreeForm: 'tree',
|
||||
GanttForm: 'gantt',
|
||||
SchedulerForm: 'scheduler',
|
||||
PagerForm: 'pager',
|
||||
StateForm: 'state',
|
||||
SubForm: 'subForm',
|
||||
|
|
@ -92,6 +93,7 @@ export const tabVisibilityConfig: Record<string, string[]> = {
|
|||
'pivots',
|
||||
'tree',
|
||||
'gantt',
|
||||
'scheduler',
|
||||
'pager',
|
||||
'state',
|
||||
'extrafilter',
|
||||
|
|
|
|||
|
|
@ -404,6 +404,32 @@ export interface GanttOptionDto {
|
|||
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 {
|
||||
mode?: GridsEditMode
|
||||
refreshMode?: GridsEditRefreshMode
|
||||
|
|
@ -504,6 +530,8 @@ export interface GridOptionsDto extends AuditedEntityDto<string> {
|
|||
treeOptionDto: TreeOptionDto
|
||||
ganttOptionJson?: string
|
||||
ganttOptionDto: GanttOptionDto
|
||||
schedulerOptionJson?: string
|
||||
schedulerOptionDto: SchedulerOptionDto
|
||||
pagerOptionJson?: string
|
||||
pagerOptionDto: GridPagerOptionDto
|
||||
editingOptionJson?: string
|
||||
|
|
@ -851,6 +879,7 @@ export interface LayoutDto {
|
|||
tree: boolean
|
||||
chart: boolean
|
||||
gantt: boolean
|
||||
scheduler: boolean
|
||||
defaultLayout: ListViewLayoutType
|
||||
cardLayoutColumn: number
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ import { tabVisibilityConfig } from '@/proxy/admin/list-form/options'
|
|||
import FormTabSorting from './FormTabSorting'
|
||||
import FormTabRow from './FormTabRow'
|
||||
import FormTabGantt from './FormTabGantt'
|
||||
import FormTabScheduler from './FormTabScheduler'
|
||||
|
||||
export interface FormEditProps {
|
||||
onSubmit: (
|
||||
|
|
@ -260,6 +261,9 @@ const FormEdit = () => {
|
|||
{visibleTabs.includes('gantt') && (
|
||||
<TabNav value="gantt">{translate('::ListForms.ListFormEdit.TabGantt')}</TabNav>
|
||||
)}
|
||||
{visibleTabs.includes('scheduler') && (
|
||||
<TabNav value="scheduler">{translate('::ListForms.ListFormEdit.TabScheduler')}</TabNav>
|
||||
)}
|
||||
{visibleTabs.includes('subForms') && (
|
||||
<TabNav value="subForms">{translate('::ListForms.ListFormEdit.SubForms')}</TabNav>
|
||||
)}
|
||||
|
|
@ -367,6 +371,9 @@ const FormEdit = () => {
|
|||
<TabContent value="gantt" className="px-2">
|
||||
<FormTabGantt onSubmit={onSubmit} />
|
||||
</TabContent>
|
||||
<TabContent value="scheduler" className="px-2">
|
||||
<FormTabScheduler onSubmit={onSubmit} />
|
||||
</TabContent>
|
||||
<TabContent value="pager" className="px-2">
|
||||
<FormTabPager onSubmit={onSubmit} />
|
||||
</TabContent>
|
||||
|
|
|
|||
399
ui/src/views/admin/listForm/edit/FormTabScheduler.tsx
Normal file
399
ui/src/views/admin/listForm/edit/FormTabScheduler.tsx
Normal 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
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
export type ChartOperation = '' | 'select' | 'insert' | 'update' | 'delete'
|
||||
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 = {
|
||||
grid: 'Grid',
|
||||
|
|
@ -9,4 +9,5 @@ export const layoutTypes = {
|
|||
tree: 'Tree',
|
||||
chart: 'Chart',
|
||||
gantt: 'Gantt',
|
||||
scheduler: 'Scheduler',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useParams, useSearchParams } from 'react-router-dom'
|
|||
import { useEffect, useState } from 'react'
|
||||
import Container from '@/components/shared/Container'
|
||||
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 classNames from 'classnames'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
|
|
@ -17,6 +17,7 @@ import Chart from './Chart'
|
|||
import Card from './Card'
|
||||
import { FaChartGantt } from 'react-icons/fa6'
|
||||
import GanttView from './GanttView'
|
||||
import SchedulerView from './SchedulerView'
|
||||
|
||||
const List = () => {
|
||||
const params = useParams()
|
||||
|
|
@ -95,9 +96,25 @@ const List = () => {
|
|||
)}
|
||||
|
||||
<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?.treeOptionDto?.parentIdExpr &&
|
||||
gridDto?.gridOptions?.treeOptionDto?.titleExpr && (
|
||||
gridDto?.gridOptions?.ganttOptionDto?.parentIdExpr &&
|
||||
gridDto?.gridOptions?.ganttOptionDto?.titleExpr && (
|
||||
<Button
|
||||
size="xs"
|
||||
variant={viewMode === 'gantt' ? 'solid' : 'default'}
|
||||
|
|
@ -192,6 +209,13 @@ const List = () => {
|
|||
gridDto={gridDto}
|
||||
refreshGridDto={refreshGridDto}
|
||||
/>
|
||||
) : viewMode === 'scheduler' ? (
|
||||
<SchedulerView
|
||||
listFormCode={listFormCode}
|
||||
searchParams={searchParams}
|
||||
isSubForm={false}
|
||||
gridDto={gridDto}
|
||||
/>
|
||||
) : viewMode === 'gantt' ? (
|
||||
<GanttView
|
||||
listFormCode={listFormCode}
|
||||
|
|
|
|||
264
ui/src/views/list/SchedulerView.tsx
Normal file
264
ui/src/views/list/SchedulerView.tsx
Normal 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
|
||||
|
|
@ -10,6 +10,7 @@ import { dynamicFetch } from '@/services/form.service'
|
|||
import { MULTIVALUE_DELIMITER } from '@/constants/app.constant'
|
||||
import { TreeListRef } from 'devextreme-react/cjs/tree-list'
|
||||
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
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ const useListFormCustomDataSource = ({
|
|||
| MutableRefObject<PivotGridRef | undefined>
|
||||
| MutableRefObject<TreeListRef<any, any> | undefined>
|
||||
| MutableRefObject<GanttRef | undefined>
|
||||
| MutableRefObject<SchedulerRef | undefined>
|
||||
}) => {
|
||||
const createSelectDataSource = useCallback(
|
||||
(
|
||||
|
|
|
|||
Loading…
Reference in a new issue