import { CommonProps } from '@/@types/common' import { Meta } from '@/@types/routes' import { Container } from '@/components/shared' import { DX_CLASSNAMES } from '@/constants/app.constant' import { useLocalization } from '@/utils/hooks/useLocalization' import DxChart from 'devextreme-react/chart' import { useCallback, useEffect, useRef, useState } from 'react' import { Helmet } from 'react-helmet' import { useParams, useSearchParams } from 'react-router-dom' import { useListFormCustomDataSource } from '@/shared/useListFormCustomDataSource' import { GridDto } from '@/proxy/form/models' import { usePermission } from '@/utils/hooks/usePermission' import { Button, toast, Notification } from '@/components/ui' import { ROUTES_ENUM } from '@/routes/route.constant' import { usePWA } from '@/utils/hooks/usePWA' import { FaCog, FaCrosshairs, FaMinus, FaPlus, FaSearch, FaSyncAlt } from 'react-icons/fa' import { buildSeriesDto } from './Utils' import { ChartSeriesDto } from '@/proxy/admin/charts/models' import { SelectBoxOption } from '@/shared/types' import { useStoreState } from '@/store/store' import ChartSeriesDialog from './ChartSeriesDialog' import { getListFormFields } from '@/services/admin/list-form-field.service' import { groupBy } from 'lodash' import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models' import { ListFormEditTabs } from '@/proxy/admin/list-form/options' import { deleteListFormJsonRow, postListFormJsonRow, putListFormJsonRow, } from '@/services/admin/list-form.service' interface ChartProps extends CommonProps, Meta { id: string listFormCode: string filter?: string isSubForm?: boolean level?: number refreshData?: () => Promise gridDto?: GridDto refreshGridDto?: () => Promise } const Chart = (props: ChartProps) => { // State UserId güncellemesi için const { userName } = useStoreState((s) => s.auth.user) const { id, listFormCode, filter, isSubForm, level, gridDto, refreshGridDto } = props const { translate } = useLocalization() const { checkPermission } = usePermission() const isPwaMode = usePWA() const initialized = useRef(false) const [searchParams] = useSearchParams() const [chartOptions, setChartOptions] = useState() const { createSelectDataSource } = useListFormCustomDataSource({}) const params = useParams() const _listFormCode = props?.listFormCode ?? params?.listFormCode ?? '' const [openDialog, setOpenDialog] = useState(false) const [fieldList, setFieldList] = useState([]) const [searchText, setSearchText] = useState('') const [prevValue, setPrevValue] = useState('') const [urlSearchParams, setUrlSearchParams] = useState( searchParams ? new URLSearchParams(searchParams) : new URLSearchParams(), ) const [allSeries, setAllSeries] = useState([]) const [userSeries, setUserSeries] = useState([]) const [oldSeries, setOldSeries] = useState([]) useEffect(() => { if (gridDto && !initialized.current) { const initialSeries = gridDto.gridOptions.seriesDto.map((s, index) => ({ ...s, index })) setAllSeries(initialSeries) setUserSeries(initialSeries.filter((s) => s.userId === userName)) setOldSeries(initialSeries.filter((s) => s.userId === userName)) initialized.current = true } }, [gridDto]) useEffect(() => { if (!gridDto) return if (!allSeries) return if (!initialized.current) return const seriesDto = userSeries.length > 0 ? userSeries : allSeries.length > 0 ? allSeries : [] const gridOptions = { ...gridDto.gridOptions, seriesDto, } const dataSource = createSelectDataSource(gridOptions, listFormCode, urlSearchParams, [], true) const options = { dataSource: dataSource, adjustOnZoom: gridDto.gridOptions.commonDto?.adjustOnZoom ?? true, containerBackgroundColor: gridDto.gridOptions.commonDto?.containerBackgroundColor ?? '#FFFFFF', disabled: gridDto.gridOptions.commonDto?.disabled ?? false, palette: gridDto.gridOptions.commonDto?.palette ?? 'Material', paletteExtensionMode: gridDto.gridOptions.commonDto?.paletteExtensionMode ?? 'blend', //theme: s(chartDto.commonDto?.theme, 'generic.light'), title: gridDto.gridOptions.titleDto, size: gridDto.gridOptions.sizeDto?.useSize ? { width: gridDto.gridOptions.sizeDto.width, height: gridDto.gridOptions.sizeDto.height } : { width: '100%', height: window.innerHeight - 210 }, legend: gridDto.gridOptions.legendDto, margin: gridDto.gridOptions.marginDto, adaptiveLayout: gridDto.gridOptions.adaptivelayoutDto, defaultPane: gridDto.gridOptions.commonDto?.defaultPane, scrollBar: gridDto.gridOptions.scrollBarDto, zoomAndPan: gridDto.gridOptions.zoomAndPanDto, animation: gridDto.gridOptions.animationDto, export: gridDto.gridOptions.exportDto, crosshair: gridDto.gridOptions.crosshairDto, argumentAxis: gridDto.gridOptions.argumentAxisDto, valueAxis: gridDto.gridOptions.valueAxisDto, tooltip: gridDto.gridOptions.tooltipDto, series: buildSeriesDto(seriesDto), panes: gridDto.gridOptions.panesDto?.length > 0 ? gridDto.gridOptions.panesDto : undefined, commonSeriesSettings: gridDto.gridOptions.commonSeriesSettingsDto, commonPaneSettings: gridDto.gridOptions.commonPaneSettingsDto, commonAxisSettings: gridDto.gridOptions.commonAxisSettingsDto, annotations: gridDto.gridOptions.annotationsDto, commonAnnotationSettings: gridDto.gridOptions.commonAnnotationSettingsDto, loadingIndicator: { enabled: true, }, } setChartOptions(options) }, [gridDto, allSeries, initialized.current, searchParams, urlSearchParams]) const onFilter = useCallback( (value?: string) => { const text = value !== undefined ? value.trim() : searchText.trim() if (!gridDto?.columnFormats) return const newParams = new URLSearchParams(urlSearchParams.toString()) if (!text) { newParams.delete('filter') setUrlSearchParams(newParams) return } const merged = gridDto.columnFormats .filter( (col) => col.dataType === 'string' && col.visible && col.width && col.allowSearch && col.width > 0, ) .map((col) => [col.fieldName, 'contains', text]) let filter: any = null if (merged.length === 1) { filter = merged[0] } else if (merged.length > 1) { filter = merged.reduce((acc, f, idx) => { if (idx === 0) return f return [acc, 'or', f] }, null as any) } if (filter) { newParams.set('filter', JSON.stringify(filter)) } else { newParams.delete('filter') } setUrlSearchParams(newParams) }, [gridDto, urlSearchParams, searchText], ) const getFields = async () => { if (!props.listFormCode) return try { const resp = await getListFormFields({ listFormCode: props.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( Alanlar getirilemedi {error.toString()} , { placement: 'top-end' }, ) } } useEffect(() => { if (props.listFormCode) getFields() }, [props.listFormCode]) const onSave = async (newSeries: ChartSeriesDto[]) => { // 1. Silinecek serileri bul (oldSeries var ama newSeries yok) const toDelete = oldSeries.filter((old) => !newSeries.some((s) => s.index === old.index)) // Index kaymasını önlemek için büyükten küçüğe sırala toDelete.sort((a, b) => b.index - a.index) for (const old of toDelete) { await deleteListFormJsonRow(id, ListFormEditTabs.ChartSeries.GeneralJsonRow, old.index) } // 2. Yeni veya güncellenen serileri kaydet for (const series of newSeries) { const input: ListFormJsonRowDto = { index: series.index, fieldName: ListFormEditTabs.ChartSeries.GeneralJsonRow, itemChartSeries: series, } if (series.index === -1) { await postListFormJsonRow(id, input) } else { await putListFormJsonRow(id, input) } } // 3. Yeniden yükle if (props.refreshGridDto) { initialized.current = false await props.refreshGridDto() } } return ( {!isSubForm && gridDto && ( )} {_listFormCode && chartOptions && (
setSearchText(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { onFilter(e.currentTarget.value) setPrevValue(e.currentTarget.value.trim()) // Enter ile tetiklenirse güncelle } }} onBlur={(e) => { const newValue = e.currentTarget.value.trim() // 1. Değer değişmemişse => hiçbir şey yapma if (newValue === prevValue) return // 2. Yeni değer boş, ama eskiden değer vardı => filtre temizle // 3. Yeni değer dolu ve eskisinden farklı => filtre uygula onFilter(newValue) setPrevValue(newValue) }} className="p-1 pl-6 pr-2 border border-1 outline-none text-xs text-gray-700 dark:text-gray-200 placeholder-gray-400 rounded" /> {checkPermission(gridDto?.gridOptions.permissionDto.u) && ( )}
setOpenDialog(false)} initialSeries={allSeries.filter((s) => s.userId === userName)} fieldList={fieldList} onSave={onSave} />
)}
) } export default Chart