Chart Series Summary Type eklendi

This commit is contained in:
Sedat Öztürk 2025-09-28 21:09:07 +03:00
parent d136fb2447
commit 531b924f5e
7 changed files with 103 additions and 61 deletions

View file

@ -91,6 +91,11 @@ public class ChartSeriesDto
/// FullStackedBarSeries,BubbleSeries, /// FullStackedBarSeries,BubbleSeries,
/// </summary> /// </summary>
public string ValueField { get; set; } public string ValueField { get; set; }
/// <summary> Değer özetleme işlevini belirtir.
/// Accepted Values: 'avg' | 'count' | 'custom' | 'max' | 'min' | 'sum'
/// Default Value: 'sum'
/// </summary>
public string SummaryType { get; set; } = "sum";
/// <summary> Serinin görünür olup olmayacağını belirtir. /// <summary> Serinin görünür olup olmayacağını belirtir.
/// </summary> /// </summary>
public bool Visible { get; set; } = true; public bool Visible { get; set; } = true;

View file

@ -40,6 +40,8 @@ public class ChartSeries : ValueObject
public string Type { get; private set; } public string Type { get; private set; }
[JsonPropertyName("ValueField")] [JsonPropertyName("ValueField")]
public string ValueField { get; private set; } public string ValueField { get; private set; }
[JsonPropertyName("SummaryType")]
public string SummaryType { get; private set; }
[JsonPropertyName("Visible")] [JsonPropertyName("Visible")]
public bool Visible { get; private set; } = true; public bool Visible { get; private set; } = true;
[JsonPropertyName("Width")] [JsonPropertyName("Width")]
@ -69,6 +71,7 @@ public class ChartSeries : ValueObject
yield return ShowInLegend; yield return ShowInLegend;
yield return Type; yield return Type;
yield return ValueField; yield return ValueField;
yield return SummaryType;
yield return Visible; yield return Visible;
yield return Width; yield return Width;
yield return Label; yield return Label;

View file

@ -316,6 +316,7 @@ export interface ChartSeriesDto {
showInLegend: boolean showInLegend: boolean
type?: string type?: string
valueField?: string valueField?: string
summaryType?: string
visible: boolean visible: boolean
width: number width: number
label: ChartLabelDto label: ChartLabelDto

View file

@ -157,6 +157,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
<Th>Name</Th> <Th>Name</Th>
<Th>Argument Field</Th> <Th>Argument Field</Th>
<Th>Value Field</Th> <Th>Value Field</Th>
<Th>Summary Type</Th>
<Th>Pane</Th> <Th>Pane</Th>
<Th>Axis</Th> <Th>Axis</Th>
</Tr> </Tr>
@ -207,6 +208,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
<Td>{row.name}</Td> <Td>{row.name}</Td>
<Td>{row.argumentField}</Td> <Td>{row.argumentField}</Td>
<Td>{row.valueField}</Td> <Td>{row.valueField}</Td>
<Td>{row.summaryType}</Td>
<Td>{row.pane}</Td> <Td>{row.pane}</Td>
<Td>{row.axis}</Td> <Td>{row.axis}</Td>
</Tr> </Tr>
@ -215,11 +217,11 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
</Table> </Table>
</Card> </Card>
<JsonRowOpDialogSeries <JsonRowOpDialogSeries
listFormCode={listFormValues.listFormCode!}
isOpen={isJsonRowOpDialogOpen} isOpen={isJsonRowOpDialogOpen}
setIsOpen={setIsJsonRowOpDialogOpen} setIsOpen={setIsJsonRowOpDialogOpen}
data={jsonRowOpModalData} data={jsonRowOpModalData}
setData={setJsonRowOpModalData} setData={setJsonRowOpModalData}
fieldList={fieldList}
/> />
</TabContent> </TabContent>
<TabContent value="series_common"> <TabContent value="series_common">

View file

@ -29,6 +29,7 @@ import {
chartSeriesDashStyleOptions, chartSeriesDashStyleOptions,
chartSeriesSelectionModeOptions, chartSeriesSelectionModeOptions,
chartSeriesTypeOptions, chartSeriesTypeOptions,
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 { getListFormFields } from '@/services/admin/list-form-field.service'
@ -41,8 +42,9 @@ const schema = object().shape({
ignoreEmptyPoints: boolean().notRequired(), ignoreEmptyPoints: boolean().notRequired(),
type: string().notRequired(), type: string().notRequired(),
name: string().required('Name Required'), name: string().required('Name Required'),
argumentField: string().notRequired(), argumentField: string().required('Argument Field Required'),
valueField: string().notRequired(), valueField: string().required('Value Field Required'),
summaryType: string().required('Summary Type Required'),
axis: string().notRequired(), axis: string().notRequired(),
pane: string().notRequired(), pane: string().notRequired(),
dashStyle: string().notRequired(), dashStyle: string().notRequired(),
@ -68,21 +70,20 @@ const schema = object().shape({
}) })
function JsonRowOpDialogSeries({ function JsonRowOpDialogSeries({
listFormCode,
isOpen, isOpen,
setIsOpen, setIsOpen,
data, data,
setData, setData,
fieldList,
}: { }: {
listFormCode: string
isOpen: boolean isOpen: boolean
setIsOpen: Dispatch<SetStateAction<boolean>> setIsOpen: Dispatch<SetStateAction<boolean>>
data: JsonRowDialogData | undefined data: JsonRowDialogData | undefined
setData: Dispatch<SetStateAction<JsonRowDialogData | undefined>> setData: Dispatch<SetStateAction<JsonRowDialogData | undefined>>
fieldList?: SelectBoxOption[]
}) { }) {
const { translate } = useLocalization() const { translate } = useLocalization()
const { setJsonValue } = useStoreActions((a) => a.admin) const { setJsonValue } = useStoreActions((a) => a.admin)
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
const handleClose = async (e?: any) => { const handleClose = async (e?: any) => {
if (e) { if (e) {
@ -117,44 +118,6 @@ function JsonRowOpDialogSeries({
})) }))
} }
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(() => {
if (isOpen && data) {
getFields()
}
}, [isOpen, data])
if (!data) { if (!data) {
return null return null
} }
@ -310,7 +273,7 @@ function JsonRowOpDialogSeries({
form={form} form={form}
isClearable={true} isClearable={true}
options={fieldList} options={fieldList}
value={fieldList.find( value={fieldList?.find(
(option) => option.value === values.argumentField, (option) => option.value === values.argumentField,
)} )}
onChange={(option) => form.setFieldValue(field.name, option?.value)} onChange={(option) => form.setFieldValue(field.name, option?.value)}
@ -334,7 +297,7 @@ function JsonRowOpDialogSeries({
form={form} form={form}
isClearable={true} isClearable={true}
options={fieldList} options={fieldList}
value={fieldList.find((option) => option.value === values.valueField)} value={fieldList?.find((option) => option.value === values.valueField)}
onChange={(option) => form.setFieldValue(field.name, option?.value)} onChange={(option) => form.setFieldValue(field.name, option?.value)}
menuPlacement="auto" menuPlacement="auto"
maxMenuHeight={150} maxMenuHeight={150}
@ -343,6 +306,38 @@ function JsonRowOpDialogSeries({
</Field> </Field>
</FormItem> </FormItem>
<FormItem
label={translate('::ListForms.ListFormFieldEdit.PivotSettingSummaryType')}
invalid={
errors.summaryType &&
touched.summaryType
}
errorMessage={errors.summaryType}
>
<Field
type="text"
autoComplete="off"
name="summaryType"
placeholder={translate(
'::ListForms.ListFormFieldEdit.PivotSettingSummaryType',
)}
>
{({ field, form }: FieldProps<SelectBoxOption>) => (
<Select
field={field}
form={form}
isClearable={true}
options={columnSummaryTypeListOptions}
value={columnSummaryTypeListOptions.filter(
(option: any) =>
option.value === values.summaryType,
)}
onChange={(option) => form.setFieldValue(field.name, option?.value)}
/>
)}
</Field>
</FormItem>
<FormItem <FormItem
label="Axis" label="Axis"
invalid={errors.axis && touched.axis} invalid={errors.axis && touched.axis}

View file

@ -9,6 +9,11 @@ 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 { Button } from '@/components/ui'
import { ROUTES_ENUM } from '@/routes/route.constant'
import { usePWA } from '@/utils/hooks/usePWA'
import { FaInfoCircle } from 'react-icons/fa'
interface ChartProps extends CommonProps, Meta { interface ChartProps extends CommonProps, Meta {
listFormCode: string listFormCode: string
@ -22,6 +27,8 @@ interface ChartProps extends CommonProps, Meta {
const Chart = (props: ChartProps) => { const Chart = (props: ChartProps) => {
const { listFormCode, filter, isSubForm, level, gridDto } = props const { listFormCode, filter, isSubForm, level, gridDto } = props
const { translate } = useLocalization() const { translate } = useLocalization()
const { checkPermission } = usePermission()
const isPwaMode = usePWA()
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
const [chartOptions, setChartOptions] = useState<any>() const [chartOptions, setChartOptions] = useState<any>()
@ -33,10 +40,10 @@ const Chart = (props: ChartProps) => {
useEffect(() => { useEffect(() => {
if (!gridDto) return if (!gridDto) return
console.log( // console.log(
gridDto.gridOptions?.seriesDto?.map((s) => `${s.argumentField} asc false`).join(', '), // gridDto.gridOptions?.seriesDto?.map((s) => `${s.argumentField} asc false`).join(', '),
) // )
console.log(gridDto.gridOptions?.seriesDto?.map((s) => `${s.valueField} count`).join(', ')) // console.log(gridDto.gridOptions?.seriesDto?.map((s) => `${s.valueField} count`).join(', '))
const dataSource = createSelectDataSource( const dataSource = createSelectDataSource(
gridDto.gridOptions, gridDto.gridOptions,
@ -59,7 +66,9 @@ const Chart = (props: ChartProps) => {
//theme: s(chartDto.commonDto?.theme, 'generic.light'), //theme: s(chartDto.commonDto?.theme, 'generic.light'),
title: gridDto.gridOptions.titleDto, title: gridDto.gridOptions.titleDto,
size: gridDto.gridOptions.sizeDto?.useSize ? gridDto.gridOptions.sizeDto : null, 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, legend: gridDto.gridOptions.legendDto,
margin: gridDto.gridOptions.marginDto, margin: gridDto.gridOptions.marginDto,
adaptiveLayout: gridDto.gridOptions.adaptivelayoutDto, adaptiveLayout: gridDto.gridOptions.adaptivelayoutDto,
@ -106,7 +115,32 @@ const Chart = (props: ChartProps) => {
></Helmet> ></Helmet>
)} )}
{_listFormCode && chartOptions && ( {_listFormCode && chartOptions && (
<div className="p-1 bg-white dark:bg-neutral-800 dark:border-neutral-700 ">
<div className="flex justify-end items-center">
<div className="relative pb-1 flex gap-1 border-b-1">
{checkPermission(gridDto?.gridOptions.permissionDto.u) && (
<Button
size="xs"
variant={'default'}
className="text-sm"
onClick={() => {
window.open(
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(
':listFormCode',
listFormCode,
),
isPwaMode ? '_self' : '_blank',
)
}}
title="Form Manager"
>
<FaInfoCircle className="w-3 h-3" />
</Button>
)}
</div>
</div>
<DxChart key={'DxChart' + _listFormCode} {...chartOptions}></DxChart> <DxChart key={'DxChart' + _listFormCode} {...chartOptions}></DxChart>
</div>
)} )}
</Container> </Container>
) )

View file

@ -8,7 +8,7 @@ import classNames from 'classnames'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import { GridDto } from '@/proxy/form/models' import { GridDto } from '@/proxy/form/models'
import Card from './Card' import Card from './Card'
import { Button } from '@/components/ui' import { Badge, Button } from '@/components/ui'
import Pivot from './Pivot' import Pivot from './Pivot'
import { getList } from '@/services/form.service' import { getList } from '@/services/form.service'
import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon' import { useCurrentMenuIcon } from '@/utils/hooks/useCurrentMenuIcon'
@ -73,6 +73,8 @@ const List = () => {
<h4 className="text-slate-700 text-sm font-medium leading-none"> <h4 className="text-slate-700 text-sm font-medium leading-none">
{translate('::' + gridDto?.gridOptions?.title) || ''} {translate('::' + gridDto?.gridOptions?.title) || ''}
</h4> </h4>
<Badge content={viewMode} />
</div> </div>
{gridDto?.gridOptions?.description === gridDto?.gridOptions?.title ? ( {gridDto?.gridOptions?.description === gridDto?.gridOptions?.title ? (