Chart Series değişikliği

This commit is contained in:
Sedat Öztürk 2025-09-30 00:11:15 +03:00
parent e1e731a031
commit ae9ce38478
2 changed files with 188 additions and 8 deletions

View file

@ -32,8 +32,6 @@ import {
columnSummaryTypeListOptions, columnSummaryTypeListOptions,
} from '../options' } from '../options'
import { ChartPanesDto, ChartSeriesDto, ChartValueAxisDto } from '@/proxy/admin/charts/models' import { ChartPanesDto, ChartSeriesDto, ChartValueAxisDto } from '@/proxy/admin/charts/models'
import { getListFormFields } from '@/services/admin/list-form-field.service'
import { groupBy } from 'lodash'
import CreatableSelect from 'react-select/creatable' import CreatableSelect from 'react-select/creatable'
const schema = object().shape({ const schema = object().shape({

View file

@ -4,17 +4,20 @@ import { Container } from '@/components/shared'
import { DX_CLASSNAMES } from '@/constants/app.constant' import { DX_CLASSNAMES } from '@/constants/app.constant'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import DxChart from 'devextreme-react/chart' import DxChart from 'devextreme-react/chart'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { useParams, useSearchParams } from 'react-router-dom' import { useParams, useSearchParams } from 'react-router-dom'
import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource' import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource'
import { GridDto } from '@/proxy/form/models' import { GridDto } from '@/proxy/form/models'
import { usePermission } from '@/utils/hooks/usePermission' import { usePermission } from '@/utils/hooks/usePermission'
import { Button } from '@/components/ui' import { Button, FormContainer, FormItem, Input, toast, Notification, Dialog } from '@/components/ui'
import { ROUTES_ENUM } from '@/routes/route.constant' import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePWA } from '@/utils/hooks/usePWA' import { usePWA } from '@/utils/hooks/usePWA'
import { FaCog, FaSearch, FaSyncAlt } from 'react-icons/fa' import { FaCog, FaCrosshairs, FaMinus, FaPlus, FaSearch, FaSyncAlt } from 'react-icons/fa'
import { buildSeriesDto } from './Utils' import { buildSeriesDto } from './Utils'
import { ChartSeriesDto } from '@/proxy/admin/charts/models'
import { Formik, Form, Field, FieldArray, useFormikContext } from 'formik' // sadece buradan Form
import { object, string, array } from 'yup'
interface ChartProps extends CommonProps, Meta { interface ChartProps extends CommonProps, Meta {
listFormCode: string listFormCode: string
@ -31,6 +34,8 @@ const Chart = (props: ChartProps) => {
const { translate } = useLocalization() const { translate } = useLocalization()
const { checkPermission } = usePermission() const { checkPermission } = usePermission()
const isPwaMode = usePWA() const isPwaMode = usePWA()
const [series, setSeries] = useState<ChartSeriesDto[]>([])
const initialized = useRef(false)
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const [chartOptions, setChartOptions] = useState<any>() const [chartOptions, setChartOptions] = useState<any>()
@ -45,11 +50,27 @@ const Chart = (props: ChartProps) => {
searchParams ? new URLSearchParams(searchParams) : new URLSearchParams(), searchParams ? new URLSearchParams(searchParams) : new URLSearchParams(),
) )
const schema = object().shape({
series: array().of(
object().shape({
name: string().required('Name Required'),
argumentField: string().required('Argument Field Required'),
valueField: string().required('Value Field Required'),
summaryType: string().required('Summary Type Required'),
}),
),
})
useEffect(() => { useEffect(() => {
if (!gridDto) return if (!gridDto) return
const gridOptions = {
...gridDto.gridOptions,
seriesDto: series ?? gridDto.gridOptions.seriesDto,
}
const dataSource = createSelectDataSource( const dataSource = createSelectDataSource(
gridDto.gridOptions, gridOptions,
listFormCode, listFormCode,
urlSearchParams, urlSearchParams,
[], [],
@ -85,7 +106,7 @@ const Chart = (props: ChartProps) => {
valueAxis: gridDto.gridOptions.valueAxisDto, valueAxis: gridDto.gridOptions.valueAxisDto,
tooltip: gridDto.gridOptions.tooltipDto, tooltip: gridDto.gridOptions.tooltipDto,
series: buildSeriesDto(gridDto.gridOptions.seriesDto), series: buildSeriesDto(series ?? gridDto.gridOptions.seriesDto),
panes: gridDto.gridOptions.panesDto?.length > 0 ? gridDto.gridOptions.panesDto : undefined, panes: gridDto.gridOptions.panesDto?.length > 0 ? gridDto.gridOptions.panesDto : undefined,
commonSeriesSettings: gridDto.gridOptions.commonSeriesSettingsDto, commonSeriesSettings: gridDto.gridOptions.commonSeriesSettingsDto,
@ -100,7 +121,7 @@ const Chart = (props: ChartProps) => {
} }
setChartOptions(options) setChartOptions(options)
}, [gridDto, searchParams, urlSearchParams]) }, [gridDto, series, searchParams, urlSearchParams])
const onFilter = useCallback( const onFilter = useCallback(
(value?: string) => { (value?: string) => {
@ -148,6 +169,51 @@ const Chart = (props: ChartProps) => {
[gridDto, urlSearchParams, searchText], [gridDto, urlSearchParams, searchText],
) )
const newSeriesValue = () => {
return {
argumentField: '',
axis: '',
barOverlapGroup: '',
barPadding: 0,
barWidth: 0,
color: '',
cornerRadius: 0,
dashStyle: 'solid',
ignoreEmptyPoints: false,
name: '',
pane: '',
rangeValue1Field: '',
rangeValue2Field: '',
selectionMode: 'none',
showInLegend: true,
type: 'line',
valueField: '',
visible: true,
width: 2,
label: {
visible: true,
backgroundColor: '#f05b41',
customizeText: '',
format: 'decimal',
font: {
color: '#FFFFFF',
family: '"Segoe UI", "Helvetica Neue", "Trebuchet MS", Verdana, sans-serif',
size: 12,
weight: 400,
},
},
}
}
useEffect(() => {
if (gridDto && !initialized.current) {
setSeries(gridDto.gridOptions.seriesDto)
initialized.current = true
}
}, [gridDto])
const [openDialog, setOpenDialog] = useState(false)
return ( return (
<Container className={DX_CLASSNAMES}> <Container className={DX_CLASSNAMES}>
{!isSubForm && gridDto && ( {!isSubForm && gridDto && (
@ -192,12 +258,24 @@ const Chart = (props: ChartProps) => {
variant={'default'} variant={'default'}
className="text-sm" className="text-sm"
onClick={async () => { onClick={async () => {
setSeries(gridDto?.gridOptions?.seriesDto ?? [])
await props.refreshGridDto() await props.refreshGridDto()
}} }}
title="Refresh Data" title="Refresh Data"
> >
<FaSyncAlt className="w-3 h-3" /> <FaSyncAlt className="w-3 h-3" />
</Button> </Button>
<Button
size="xs"
variant="default"
className="text-sm"
onClick={() => setOpenDialog(true)}
title="Edit Series"
>
<FaCrosshairs className="w-3 h-3" />
</Button>
{checkPermission(gridDto?.gridOptions.permissionDto.u) && ( {checkPermission(gridDto?.gridOptions.permissionDto.u) && (
<Button <Button
size="xs" size="xs"
@ -220,6 +298,110 @@ const Chart = (props: ChartProps) => {
</div> </div>
</div> </div>
<DxChart key={'DxChart' + _listFormCode} {...chartOptions}></DxChart> <DxChart key={'DxChart' + _listFormCode} {...chartOptions}></DxChart>
<Dialog isOpen={openDialog} onClose={() => setOpenDialog(false)} width={600}>
<Formik
enableReinitialize
initialValues={{
series: series && series.length > 0 ? series : [newSeriesValue()],
}}
validationSchema={schema}
onSubmit={(values, { setSubmitting }) => {
setSeries(values.series)
setSubmitting(true)
try {
toast.push(<Notification type="success">{'Chart güncellendi'}</Notification>, {
placement: 'top-end',
})
setOpenDialog(false) // kaydettikten sonra dialogu kapat
} catch (error: any) {
toast.push(
<Notification type="danger">
Hata
<code>{error}</code>
</Notification>,
{ placement: 'top-end' },
)
} finally {
setSubmitting(false)
}
}}
>
{({ setFieldValue, values, isSubmitting }) => (
<Form className="flex flex-col h-full">
<FormContainer size="sm" className="flex flex-col h-full">
{/* Kaydırılabilir içerik */}
<div className="flex-1 overflow-y-auto p-2">
<FieldArray name="series">
{({ remove }) => (
<div>
{values.series.map((_, index) => (
<div key={index} className="mb-2 border-b pb-2">
<FormItem label="Name">
<Field
name={`series[${index}].name`}
type="text"
component={Input}
/>
</FormItem>
<FormItem label="Argument Field">
<Field
name={`series[${index}].argumentField`}
type="text"
component={Input}
/>
</FormItem>
<FormItem label="Value Field">
<Field
name={`series[${index}].valueField`}
type="text"
component={Input}
/>
</FormItem>
<FormItem label="Summary Type">
<Field
name={`series[${index}].summaryType`}
type="text"
component={Input}
/>
</FormItem>
<Button
shape="circle"
type="button"
className="mt-2"
size="xs"
icon={<FaMinus />}
onClick={() => remove(index)}
/>
</div>
))}
</div>
)}
</FieldArray>
</div>
{/* Footer */}
<div className="flex gap-2 mt-auto pt-2 border-t text-right">
<Button
variant="default"
type="button"
onClick={() =>
setFieldValue('series', [...values.series, newSeriesValue()])
}
>
<FaPlus />
</Button>
<Button block variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
</Button>
</div>
</FormContainer>
</Form>
)}
</Formik>
</Dialog>
</div> </div>
)} )}
</Container> </Container>