2025-09-30 13:11:23 +00:00
|
|
|
|
import { Button, FormContainer, Input, Notification, Select, Dialog, toast } from '@/components/ui'
|
|
|
|
|
|
import { Field, FieldArray, Form, Formik, FieldProps } from 'formik'
|
|
|
|
|
|
import { FaMinus, FaPlus } from 'react-icons/fa'
|
2025-11-09 10:30:15 +00:00
|
|
|
|
import { SelectBoxOption } from '@/types/shared'
|
2025-09-30 13:11:23 +00:00
|
|
|
|
import {
|
|
|
|
|
|
chartSeriesTypeOptions,
|
|
|
|
|
|
columnSummaryTypeListOptions,
|
|
|
|
|
|
} from '../admin/listForm/edit/options'
|
|
|
|
|
|
import { ChartSeriesDto } from '@/proxy/admin/charts/models'
|
|
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
|
import { useStoreState } from '@/store/store'
|
|
|
|
|
|
import { object, array, string } from 'yup'
|
|
|
|
|
|
import { SummaryTypeEnum } from '@/proxy/form/models'
|
|
|
|
|
|
|
|
|
|
|
|
interface ChartSeriesDialogProps {
|
|
|
|
|
|
open: boolean
|
|
|
|
|
|
onClose: () => void
|
|
|
|
|
|
initialSeries: ChartSeriesDto[]
|
|
|
|
|
|
fieldList: SelectBoxOption[]
|
|
|
|
|
|
onSave: (series: ChartSeriesDto[]) => void
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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'),
|
|
|
|
|
|
}),
|
|
|
|
|
|
),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const ChartSeriesDialog = ({
|
|
|
|
|
|
open,
|
|
|
|
|
|
onClose,
|
|
|
|
|
|
initialSeries,
|
|
|
|
|
|
fieldList,
|
|
|
|
|
|
onSave,
|
|
|
|
|
|
}: ChartSeriesDialogProps) => {
|
|
|
|
|
|
const { translate } = useLocalization()
|
|
|
|
|
|
|
|
|
|
|
|
// State UserId güncellemesi için
|
|
|
|
|
|
const { userName } = useStoreState((s) => s.auth.user)
|
|
|
|
|
|
|
|
|
|
|
|
const newSeriesValue = () => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
index: -1,
|
|
|
|
|
|
type: 'line',
|
|
|
|
|
|
name: '',
|
|
|
|
|
|
argumentField: '',
|
|
|
|
|
|
valueField: '',
|
|
|
|
|
|
summaryType: SummaryTypeEnum.Sum,
|
|
|
|
|
|
axis: '',
|
|
|
|
|
|
barOverlapGroup: '',
|
|
|
|
|
|
barPadding: 0,
|
|
|
|
|
|
barWidth: 0,
|
|
|
|
|
|
color: '',
|
|
|
|
|
|
cornerRadius: 0,
|
|
|
|
|
|
dashStyle: 'solid',
|
|
|
|
|
|
ignoreEmptyPoints: false,
|
|
|
|
|
|
pane: '',
|
|
|
|
|
|
rangeValue1Field: '',
|
|
|
|
|
|
rangeValue2Field: '',
|
|
|
|
|
|
selectionMode: 'none',
|
|
|
|
|
|
showInLegend: true,
|
|
|
|
|
|
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,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
userId: userName ?? '',
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Dialog isOpen={open} onClose={onClose} width={1200}>
|
|
|
|
|
|
<div className="flex flex-col bg-white p-4 h-[600px]">
|
|
|
|
|
|
<Formik
|
|
|
|
|
|
enableReinitialize
|
|
|
|
|
|
initialValues={{
|
|
|
|
|
|
series: initialSeries && initialSeries.length > 0 ? initialSeries : [newSeriesValue()],
|
|
|
|
|
|
}}
|
|
|
|
|
|
validationSchema={schema}
|
|
|
|
|
|
onSubmit={(values, { setSubmitting }) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
onSave(values.series)
|
|
|
|
|
|
toast.push(<Notification type="success">{'Chart güncellendi'}</Notification>, {
|
|
|
|
|
|
placement: 'top-end',
|
|
|
|
|
|
})
|
|
|
|
|
|
onClose()
|
|
|
|
|
|
} 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">
|
|
|
|
|
|
{/* Header */}
|
|
|
|
|
|
<div className="mb-2 pb-2 border-b flex items-center justify-between">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="default"
|
|
|
|
|
|
shape="circle"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
size="xs"
|
|
|
|
|
|
onClick={() => setFieldValue('series', [...values.series, newSeriesValue()])}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
|
<FaPlus /> Seri Ekle
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{/* Kaydırılabilir içerik */}
|
|
|
|
|
|
<div className="flex-1 overflow-y-auto p-1">
|
|
|
|
|
|
<FieldArray name="series">
|
|
|
|
|
|
{({ remove }) => (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div className="grid grid-cols-12 gap-2 font-semibold text-xs py-2">
|
|
|
|
|
|
<div className="text-center col-span-1">#</div>
|
|
|
|
|
|
<div className="text-center col-span-2">Type</div>
|
|
|
|
|
|
<div className="text-center col-span-2">Name</div>
|
|
|
|
|
|
<div className="text-center col-span-2">Argument Field</div>
|
|
|
|
|
|
<div className="text-center col-span-2">Value Field</div>
|
|
|
|
|
|
<div className="text-center col-span-2">Summary Type</div>
|
|
|
|
|
|
<div className="text-center col-span-1">#</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{values.series.map((_, index) => (
|
|
|
|
|
|
<div key={index} className="border-b py-1">
|
|
|
|
|
|
<div className="grid grid-cols-12 gap-1 items-center">
|
|
|
|
|
|
<div className="text-center text-xs col-span-1">
|
|
|
|
|
|
{values.series[index].index}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-xs col-span-2">
|
|
|
|
|
|
<Field name={`series[${index}].type`}>
|
|
|
|
|
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
field={field}
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
options={chartSeriesTypeOptions}
|
|
|
|
|
|
isClearable
|
|
|
|
|
|
value={chartSeriesTypeOptions.find(
|
|
|
|
|
|
(option) => option.value === field.value,
|
|
|
|
|
|
)}
|
|
|
|
|
|
onChange={(option) =>
|
|
|
|
|
|
form.setFieldValue(field.name, option?.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Field>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-xs col-span-2">
|
|
|
|
|
|
<Field
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
name={`series[${index}].name`}
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
component={Input}
|
|
|
|
|
|
className="text-xs px-1 py-0 grid-cols-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-xs col-span-2">
|
|
|
|
|
|
<Field type="text" name={`series[${index}].argumentField`}>
|
|
|
|
|
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
field={field}
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
isClearable={true}
|
|
|
|
|
|
options={fieldList}
|
|
|
|
|
|
value={fieldList?.find(
|
|
|
|
|
|
(option) => option.value === field.value,
|
|
|
|
|
|
)}
|
|
|
|
|
|
onChange={(option) =>
|
|
|
|
|
|
form.setFieldValue(field.name, option?.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
menuPlacement="auto"
|
|
|
|
|
|
maxMenuHeight={150}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Field>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-xs col-span-2">
|
|
|
|
|
|
<Field type="text" name={`series[${index}].valueField`}>
|
|
|
|
|
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
field={field}
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
isClearable={true}
|
|
|
|
|
|
options={fieldList}
|
|
|
|
|
|
value={fieldList?.find(
|
|
|
|
|
|
(option) => option.value === field.value,
|
|
|
|
|
|
)}
|
|
|
|
|
|
onChange={(option) =>
|
|
|
|
|
|
form.setFieldValue(field.name, option?.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
menuPlacement="auto"
|
|
|
|
|
|
maxMenuHeight={150}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Field>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="text-xs col-span-2">
|
|
|
|
|
|
<Field type="text" name={`series[${index}].summaryType`}>
|
|
|
|
|
|
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
|
|
|
|
|
<Select
|
|
|
|
|
|
field={field}
|
|
|
|
|
|
form={form}
|
|
|
|
|
|
isClearable={true}
|
|
|
|
|
|
options={columnSummaryTypeListOptions}
|
|
|
|
|
|
value={columnSummaryTypeListOptions.find(
|
|
|
|
|
|
(option) => option.value === field.value,
|
|
|
|
|
|
)}
|
|
|
|
|
|
onChange={(option) =>
|
|
|
|
|
|
form.setFieldValue(field.name, option?.value)
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Field>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex items-center justify-center gap-1 col-span-1">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
shape="circle"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
size="xs"
|
|
|
|
|
|
icon={<FaMinus />}
|
|
|
|
|
|
className="bg-slate-100 hover:bg-red-100"
|
|
|
|
|
|
onClick={() => remove(index)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</FieldArray>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{/* Footer */}
|
|
|
|
|
|
<div className="flex gap-2 mt-auto pt-2 border-t text-right justify-end">
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="solid"
|
|
|
|
|
|
loading={isSubmitting}
|
|
|
|
|
|
type="submit"
|
|
|
|
|
|
className="ml-auto px-4 py-1 text-sm rounded"
|
|
|
|
|
|
>
|
|
|
|
|
|
{isSubmitting ? translate('::SavingWithThreeDot') : translate('::Save')}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</FormContainer>
|
|
|
|
|
|
</Form>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Formik>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default ChartSeriesDialog
|