Chart komponenti düzenlemesi
This commit is contained in:
parent
ae9ce38478
commit
e3a8165499
21 changed files with 823 additions and 247 deletions
|
|
@ -91,11 +91,6 @@ public class ChartSeriesDto
|
|||
/// FullStackedBarSeries,BubbleSeries,
|
||||
/// </summary>
|
||||
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>
|
||||
public bool Visible { get; set; } = true;
|
||||
|
|
@ -126,4 +121,12 @@ public class ChartSeriesDto
|
|||
// valueErrorBar
|
||||
|
||||
public ChartLabelDto Label { 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";
|
||||
/// <example>'Kullanıcı Adı'</example>
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Kurs.Platform.Entities;
|
||||
|
|
@ -8,6 +9,7 @@ using Kurs.Platform.Queries;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Volo.Abp;
|
||||
using Volo.Abp.Domain.Repositories;
|
||||
using Volo.Abp.Users;
|
||||
using static Kurs.Platform.PlatformConsts;
|
||||
|
||||
namespace Kurs.Platform.ListForms.Administration;
|
||||
|
|
@ -19,37 +21,121 @@ namespace Kurs.Platform.ListForms.Administration;
|
|||
public class ListFormJsonRowAppService : PlatformAppService
|
||||
{
|
||||
private readonly IRepository<ListForm, Guid> repository;
|
||||
private readonly ICurrentUser currentUser;
|
||||
private readonly string userId;
|
||||
|
||||
public ListFormJsonRowAppService(
|
||||
IRepository<ListForm, Guid> _repository) : base()
|
||||
IRepository<ListForm, Guid> _repository,
|
||||
ICurrentUser _currentUser
|
||||
) : base()
|
||||
{
|
||||
repository = _repository;
|
||||
currentUser = _currentUser;
|
||||
userId = currentUser.Name;
|
||||
|
||||
}
|
||||
|
||||
public async Task<dynamic> GetAllAsync(Guid id, string field)
|
||||
{
|
||||
var listForm = await GetAsync(id);
|
||||
|
||||
return field switch
|
||||
switch (field)
|
||||
{
|
||||
ListFormEditTabs.Database.Select.SelectFieldsDefaultValuesJsonRow => listForm.SelectFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.SelectFieldsDefaultValueJson),
|
||||
ListFormEditTabs.Database.Insert.InsertFieldsDefaultValuesJsonRow => listForm.InsertFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.InsertFieldsDefaultValueJson),
|
||||
ListFormEditTabs.Database.Update.UpdateFieldsDefaultValuesJsonRow => listForm.UpdateFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.UpdateFieldsDefaultValueJson),
|
||||
ListFormEditTabs.Database.Delete.DeleteFieldsDefaultValuesJsonRow => listForm.DeleteFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.DeleteFieldsDefaultValueJson),
|
||||
ListFormEditTabs.Database.Insert.FormFieldsDefaultValuesJsonRow => listForm.FormFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.FormFieldsDefaultValueJson),
|
||||
ListFormEditTabs.Edit.EditingFormFieldsJsonRow => listForm.EditingFormJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<EditingFormDto>>(listForm.EditingFormJson),
|
||||
ListFormEditTabs.CommandColumnsJsonRow => listForm.CommandColumnJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<CommandColumnDto>>(listForm.CommandColumnJson),
|
||||
ListFormEditTabs.SubFormJsonRow => listForm.SubFormsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<SubFormDto>>(listForm.SubFormsJson),
|
||||
ListFormEditTabs.WidgetForm => listForm.WidgetsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<WidgetEditDto>>(listForm.WidgetsJson),
|
||||
ListFormEditTabs.ExtraFilterForm => listForm.ExtraFilterJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ExtraFilterEditDto>>(listForm.ExtraFilterJson),
|
||||
case ListFormEditTabs.Database.Select.SelectFieldsDefaultValuesJsonRow:
|
||||
return listForm.SelectFieldsDefaultValueJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.SelectFieldsDefaultValueJson) ?? [];
|
||||
|
||||
ListFormEditTabs.ChartSeries.GeneralJsonRow => listForm.SeriesJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartSeriesDto>>(listForm.SeriesJson),
|
||||
ListFormEditTabs.ChartAxis.ValueAxisJsonRow => listForm.ValueAxisJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartValueAxisDto>>(listForm.ValueAxisJson),
|
||||
ListFormEditTabs.ChartPanes.PanesJsonRow => listForm.PanesJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartPanesDto>>(listForm.PanesJson),
|
||||
ListFormEditTabs.ChartAnnotations.GeneralJsonRow => listForm.AnnotationsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartAnnotationDto>>(listForm.AnnotationsJson),
|
||||
case ListFormEditTabs.Database.Insert.InsertFieldsDefaultValuesJsonRow:
|
||||
return listForm.InsertFieldsDefaultValueJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.InsertFieldsDefaultValueJson) ?? [];
|
||||
|
||||
_ => throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]),
|
||||
};
|
||||
case ListFormEditTabs.Database.Update.UpdateFieldsDefaultValuesJsonRow:
|
||||
return listForm.UpdateFieldsDefaultValueJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.UpdateFieldsDefaultValueJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.Database.Delete.DeleteFieldsDefaultValuesJsonRow:
|
||||
return listForm.DeleteFieldsDefaultValueJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.DeleteFieldsDefaultValueJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.Database.Insert.FormFieldsDefaultValuesJsonRow:
|
||||
return listForm.FormFieldsDefaultValueJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.FormFieldsDefaultValueJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.Edit.EditingFormFieldsJsonRow:
|
||||
return listForm.EditingFormJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<EditingFormDto>>(listForm.EditingFormJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.CommandColumnsJsonRow:
|
||||
return listForm.CommandColumnJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<CommandColumnDto>>(listForm.CommandColumnJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.SubFormJsonRow:
|
||||
return listForm.SubFormsJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<SubFormDto>>(listForm.SubFormsJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.WidgetForm:
|
||||
return listForm.WidgetsJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<WidgetEditDto>>(listForm.WidgetsJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.ExtraFilterForm:
|
||||
return listForm.ExtraFilterJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<ExtraFilterEditDto>>(listForm.ExtraFilterJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.ChartSeries.GeneralJsonRow:
|
||||
return listForm.SeriesJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<ChartSeriesDto>>(listForm.SeriesJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.ChartAxis.ValueAxisJsonRow:
|
||||
return listForm.ValueAxisJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<ChartValueAxisDto>>(listForm.ValueAxisJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.ChartPanes.PanesJsonRow:
|
||||
return listForm.PanesJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<ChartPanesDto>>(listForm.PanesJson) ?? [];
|
||||
|
||||
case ListFormEditTabs.ChartAnnotations.GeneralJsonRow:
|
||||
return listForm.AnnotationsJson.IsNullOrWhiteSpace()
|
||||
? []
|
||||
: JsonSerializer.Deserialize<List<ChartAnnotationDto>>(listForm.AnnotationsJson) ?? [];
|
||||
|
||||
default:
|
||||
throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]);
|
||||
}
|
||||
|
||||
|
||||
// return field switch
|
||||
// {
|
||||
// ListFormEditTabs.Database.Select.SelectFieldsDefaultValuesJsonRow => listForm.SelectFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.SelectFieldsDefaultValueJson),
|
||||
// ListFormEditTabs.Database.Insert.InsertFieldsDefaultValuesJsonRow => listForm.InsertFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.InsertFieldsDefaultValueJson),
|
||||
// ListFormEditTabs.Database.Update.UpdateFieldsDefaultValuesJsonRow => listForm.UpdateFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.UpdateFieldsDefaultValueJson),
|
||||
// ListFormEditTabs.Database.Delete.DeleteFieldsDefaultValuesJsonRow => listForm.DeleteFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.DeleteFieldsDefaultValueJson),
|
||||
// ListFormEditTabs.Database.Insert.FormFieldsDefaultValuesJsonRow => listForm.FormFieldsDefaultValueJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<FieldsDefaultValueDto>>(listForm.FormFieldsDefaultValueJson),
|
||||
// ListFormEditTabs.Edit.EditingFormFieldsJsonRow => listForm.EditingFormJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<EditingFormDto>>(listForm.EditingFormJson),
|
||||
// ListFormEditTabs.CommandColumnsJsonRow => listForm.CommandColumnJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<CommandColumnDto>>(listForm.CommandColumnJson),
|
||||
// ListFormEditTabs.SubFormJsonRow => listForm.SubFormsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<SubFormDto>>(listForm.SubFormsJson),
|
||||
// ListFormEditTabs.WidgetForm => listForm.WidgetsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<WidgetEditDto>>(listForm.WidgetsJson),
|
||||
// ListFormEditTabs.ExtraFilterForm => listForm.ExtraFilterJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ExtraFilterEditDto>>(listForm.ExtraFilterJson),
|
||||
|
||||
// ListFormEditTabs.ChartSeries.GeneralJsonRow => listForm.SeriesJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartSeriesDto>>(listForm.SeriesJson),
|
||||
// ListFormEditTabs.ChartAxis.ValueAxisJsonRow => listForm.ValueAxisJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartValueAxisDto>>(listForm.ValueAxisJson),
|
||||
// ListFormEditTabs.ChartPanes.PanesJsonRow => listForm.PanesJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartPanesDto>>(listForm.PanesJson),
|
||||
// ListFormEditTabs.ChartAnnotations.GeneralJsonRow => listForm.AnnotationsJson.IsNullOrWhiteSpace() ? [] : JsonSerializer.Deserialize<List<ChartAnnotationDto>>(listForm.AnnotationsJson),
|
||||
|
||||
// _ => throw new UserFriendlyException(L[AppErrorCodes.ParameterNotValid]),
|
||||
// };
|
||||
}
|
||||
|
||||
public async Task CreateAsync(Guid id, CrudFieldsDefaultValueJsonItemDto model)
|
||||
|
|
|
|||
|
|
@ -90,6 +90,10 @@ public class ChartSeries : ValueObject
|
|||
[JsonPropertyName("Label")]
|
||||
public ChartLabel Label { get; private set; } = new();
|
||||
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("UserId")]
|
||||
public string UserId { get; private set; }
|
||||
|
||||
// JSON + EF Core için parametresiz public ctor
|
||||
public ChartSeries() { }
|
||||
|
||||
|
|
@ -116,5 +120,6 @@ public class ChartSeries : ValueObject
|
|||
yield return Visible;
|
||||
yield return Width;
|
||||
yield return Label;
|
||||
yield return UserId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -766,8 +766,8 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
|||
|
||||
var seriesList = JsonSerializer.Deserialize<List<ChartSeries>>(listform.SeriesJson);
|
||||
|
||||
if (seriesList == null || !seriesList.Any())
|
||||
throw new ArgumentException("Series list is empty!");
|
||||
if (seriesList == null || seriesList.Count == 0)
|
||||
return null;
|
||||
|
||||
// ArgumentField listesi
|
||||
var argumentFields = seriesList.Select(s => s.ArgumentField).Distinct().ToList();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { AuditedEntityDto } from '@/proxy/abp'
|
||||
import { PermissionCrudDto } from '@/proxy/form/models'
|
||||
import { PermissionCrudDto, SummaryTypeEnum } from '@/proxy/form/models'
|
||||
|
||||
export interface BreakDto {
|
||||
endValue: number
|
||||
|
|
@ -299,6 +299,7 @@ export interface ChartScrollBarDto {
|
|||
}
|
||||
|
||||
export interface ChartSeriesDto {
|
||||
index: number
|
||||
argumentField?: string
|
||||
axis?: string
|
||||
barOverlapGroup?: string
|
||||
|
|
@ -316,10 +317,11 @@ export interface ChartSeriesDto {
|
|||
showInLegend: boolean
|
||||
type?: string
|
||||
valueField?: string
|
||||
summaryType?: string
|
||||
summaryType?: SummaryTypeEnum
|
||||
visible: boolean
|
||||
width: number
|
||||
label: ChartLabelDto
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export interface ChartSizeDto {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import {
|
|||
ChartZoomAndPanDto,
|
||||
} from '../admin/charts/models'
|
||||
import { ListViewLayoutType } from '@/views/admin/listForm/edit/types'
|
||||
import { SeriesType } from 'devextreme/common/charts'
|
||||
|
||||
//1
|
||||
export interface SelectListItem {
|
||||
|
|
@ -367,30 +368,7 @@ export interface GridPivotOptionDto {
|
|||
showFilterFields: boolean
|
||||
showChart: boolean
|
||||
chartHeight: number
|
||||
chartCommonSeriesType:
|
||||
| 'area'
|
||||
| 'bar'
|
||||
| 'bubble'
|
||||
| 'candlestick'
|
||||
| 'fullstackedarea'
|
||||
| 'fullstackedbar'
|
||||
| 'fullstackedline'
|
||||
| 'fullstackedspline'
|
||||
| 'fullstackedsplinearea'
|
||||
| 'line'
|
||||
| 'rangearea'
|
||||
| 'rangebar'
|
||||
| 'scatter'
|
||||
| 'spline'
|
||||
| 'splinearea'
|
||||
| 'stackedarea'
|
||||
| 'stackedbar'
|
||||
| 'stackedline'
|
||||
| 'stackedspline'
|
||||
| 'stackedsplinearea'
|
||||
| 'steparea'
|
||||
| 'stepline'
|
||||
| 'stock'
|
||||
chartCommonSeriesType: SeriesType
|
||||
}
|
||||
|
||||
export interface GridEditingDto {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
Tabs,
|
||||
Notification,
|
||||
toast,
|
||||
Badge,
|
||||
} from '@/components/ui'
|
||||
import TBody from '@/components/ui/Table/TBody'
|
||||
import THead from '@/components/ui/Table/THead'
|
||||
|
|
@ -153,6 +154,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
}}
|
||||
/>
|
||||
</Th>
|
||||
<Th></Th>
|
||||
<Th>Type</Th>
|
||||
<Th>Name</Th>
|
||||
<Th>Argument Field</Th>
|
||||
|
|
@ -204,6 +206,7 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
/>
|
||||
</div>
|
||||
</Td>
|
||||
<Td>{row.userId && <Badge content={row.userId} />}</Td>
|
||||
<Td>{row.type}</Td>
|
||||
<Td>{row.name}</Td>
|
||||
<Td>{row.argumentField}</Td>
|
||||
|
|
@ -320,7 +323,10 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
|
||||
<FormItem
|
||||
label="Argument Field"
|
||||
invalid={errors.commonSeriesSettingsDto?.argumentField && touched.commonSeriesSettingsDto?.argumentField}
|
||||
invalid={
|
||||
errors.commonSeriesSettingsDto?.argumentField &&
|
||||
touched.commonSeriesSettingsDto?.argumentField
|
||||
}
|
||||
errorMessage={errors.commonSeriesSettingsDto?.argumentField}
|
||||
>
|
||||
<Field type="text" name="commonSeriesSettingsDto.argumentField">
|
||||
|
|
@ -331,7 +337,10 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
form={form}
|
||||
isClearable={true}
|
||||
options={fieldList}
|
||||
value={fieldList.find((option) => option.value === values.commonSeriesSettingsDto.argumentField)}
|
||||
value={fieldList.find(
|
||||
(option) =>
|
||||
option.value === values.commonSeriesSettingsDto.argumentField,
|
||||
)}
|
||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
||||
menuPlacement="auto"
|
||||
maxMenuHeight={150}
|
||||
|
|
@ -342,7 +351,10 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
|
||||
<FormItem
|
||||
label="Value Field"
|
||||
invalid={errors.commonSeriesSettingsDto?.valueField && touched.commonSeriesSettingsDto?.valueField}
|
||||
invalid={
|
||||
errors.commonSeriesSettingsDto?.valueField &&
|
||||
touched.commonSeriesSettingsDto?.valueField
|
||||
}
|
||||
errorMessage={errors.commonSeriesSettingsDto?.valueField}
|
||||
>
|
||||
<Field type="text" name="valueField">
|
||||
|
|
@ -353,7 +365,9 @@ function ChartTabSeries(props: FormEditProps & { listFormCode: string }) {
|
|||
form={form}
|
||||
isClearable={true}
|
||||
options={fieldList}
|
||||
value={fieldList.find((option) => option.value === values.commonSeriesSettingsDto.valueField)}
|
||||
value={fieldList.find(
|
||||
(option) => option.value === values.commonSeriesSettingsDto.valueField,
|
||||
)}
|
||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
||||
menuPlacement="auto"
|
||||
maxMenuHeight={150}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ function JsonRowOpDialogAnnotation({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<Tabs defaultValue="annotations_general">
|
||||
<TabList>
|
||||
<TabNav value="annotations_general">General</TabNav>
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ function JsonRowOpDialogAxis({
|
|||
return (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Visible"
|
||||
invalid={errors.visible && touched.visible}
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ function JsonRowOpDialogCommand({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Button Position"
|
||||
invalid={errors.buttonPosition && touched.buttonPosition}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ function JsonRowOpDialogDatabase({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Field Name"
|
||||
invalid={errors.fieldName && touched.fieldName}
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ function JsonRowOpDialogEditForm({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-[90vh] overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<div className="grid grid-cols-5 gap-4 w-full">
|
||||
<FormItem
|
||||
label="Order"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
Select,
|
||||
toast,
|
||||
} from '@/components/ui'
|
||||
import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models'
|
||||
import { ListFormJsonRowDto } from '@/proxy/admin/list-form/models'
|
||||
import { SelectBoxOption } from '@/shared/types'
|
||||
import { useStoreActions, useStoreState } from '@/store'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
|
|
@ -177,7 +177,7 @@ function JsonRowOpDialogExtraFilter({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-full overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Field Name"
|
||||
invalid={errors.fieldName && touched.fieldName}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ function JsonRowOpDialogPane({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Name"
|
||||
invalid={errors.name && touched.name}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
chartSeriesTypeOptions,
|
||||
columnSummaryTypeListOptions,
|
||||
} from '../options'
|
||||
import { ChartPanesDto, ChartSeriesDto, ChartValueAxisDto } from '@/proxy/admin/charts/models'
|
||||
import { ChartPanesDto, ChartValueAxisDto } from '@/proxy/admin/charts/models'
|
||||
import CreatableSelect from 'react-select/creatable'
|
||||
|
||||
const schema = object().shape({
|
||||
|
|
@ -134,6 +134,7 @@ function JsonRowOpDialogSeries({
|
|||
<Formik
|
||||
initialValues={
|
||||
data.chartSeriesValues ?? {
|
||||
index: -1,
|
||||
argumentField: '',
|
||||
axis: '',
|
||||
barOverlapGroup: '',
|
||||
|
|
@ -204,7 +205,7 @@ function JsonRowOpDialogSeries({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Visible"
|
||||
invalid={errors.visible && touched.visible}
|
||||
|
|
@ -266,7 +267,6 @@ function JsonRowOpDialogSeries({
|
|||
<Field type="text" name="argumentField">
|
||||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||
<Select
|
||||
componentAs={CreatableSelect}
|
||||
field={field}
|
||||
form={form}
|
||||
isClearable={true}
|
||||
|
|
@ -290,7 +290,6 @@ function JsonRowOpDialogSeries({
|
|||
<Field type="text" name="valueField">
|
||||
{({ field, form }: FieldProps<SelectBoxOption>) => (
|
||||
<Select
|
||||
componentAs={CreatableSelect}
|
||||
field={field}
|
||||
form={form}
|
||||
isClearable={true}
|
||||
|
|
@ -306,10 +305,7 @@ function JsonRowOpDialogSeries({
|
|||
|
||||
<FormItem
|
||||
label={translate('::ListForms.ListFormFieldEdit.PivotSettingSummaryType')}
|
||||
invalid={
|
||||
errors.summaryType &&
|
||||
touched.summaryType
|
||||
}
|
||||
invalid={errors.summaryType && touched.summaryType}
|
||||
errorMessage={errors.summaryType}
|
||||
>
|
||||
<Field
|
||||
|
|
@ -327,8 +323,7 @@ function JsonRowOpDialogSeries({
|
|||
isClearable={true}
|
||||
options={columnSummaryTypeListOptions}
|
||||
value={columnSummaryTypeListOptions.filter(
|
||||
(option: any) =>
|
||||
option.value === values.summaryType,
|
||||
(option: any) => option.value === values.summaryType,
|
||||
)}
|
||||
onChange={(option) => form.setFieldValue(field.name, option?.value)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ function JsonRowOpDialogSubForm({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-96 overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Tab Title"
|
||||
invalid={errors.tabTitle && touched.tabTitle}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ function JsonRowOpDialogWidget({
|
|||
{({ touched, errors, values, isSubmitting }) => (
|
||||
<Form>
|
||||
<FormContainer size="sm">
|
||||
<div className="max-h-full overflow-y-auto p-2">
|
||||
<div className="h-full overflow-y-auto p-2">
|
||||
<FormItem
|
||||
label="Column Gap (Sütun Boşluğu)"
|
||||
invalid={errors.colGap && touched.colGap}
|
||||
|
|
|
|||
|
|
@ -10,16 +10,27 @@ 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, FormContainer, FormItem, Input, toast, Notification, Dialog } from '@/components/ui'
|
||||
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 { Formik, Form, Field, FieldArray, useFormikContext } from 'formik' // sadece buradan Form
|
||||
import { object, string, array } from 'yup'
|
||||
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
|
||||
|
|
@ -30,11 +41,12 @@ interface ChartProps extends CommonProps, Meta {
|
|||
}
|
||||
|
||||
const Chart = (props: ChartProps) => {
|
||||
const { listFormCode, filter, isSubForm, level, gridDto } = props
|
||||
// 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 [series, setSeries] = useState<ChartSeriesDto[]>([])
|
||||
const initialized = useRef(false)
|
||||
|
||||
const [searchParams] = useSearchParams()
|
||||
|
|
@ -44,38 +56,45 @@ const Chart = (props: ChartProps) => {
|
|||
const params = useParams()
|
||||
const _listFormCode = props?.listFormCode ?? params?.listFormCode ?? ''
|
||||
|
||||
const [openDialog, setOpenDialog] = useState(false)
|
||||
const [fieldList, setFieldList] = useState<SelectBoxOption[]>([])
|
||||
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const [prevValue, setPrevValue] = useState('')
|
||||
const [urlSearchParams, setUrlSearchParams] = useState<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'),
|
||||
}),
|
||||
),
|
||||
})
|
||||
const [allSeries, setAllSeries] = useState<ChartSeriesDto[]>([])
|
||||
|
||||
const [userSeries, setUserSeries] = useState<ChartSeriesDto[]>([])
|
||||
const [oldSeries, setOldSeries] = useState<ChartSeriesDto[]>([])
|
||||
|
||||
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: series ?? gridDto.gridOptions.seriesDto,
|
||||
seriesDto,
|
||||
}
|
||||
|
||||
const dataSource = createSelectDataSource(
|
||||
gridOptions,
|
||||
listFormCode,
|
||||
urlSearchParams,
|
||||
[],
|
||||
true,
|
||||
)
|
||||
const dataSource = createSelectDataSource(gridOptions, listFormCode, urlSearchParams, [], true)
|
||||
|
||||
const options = {
|
||||
dataSource: dataSource,
|
||||
|
|
@ -106,7 +125,7 @@ const Chart = (props: ChartProps) => {
|
|||
valueAxis: gridDto.gridOptions.valueAxisDto,
|
||||
tooltip: gridDto.gridOptions.tooltipDto,
|
||||
|
||||
series: buildSeriesDto(series ?? gridDto.gridOptions.seriesDto),
|
||||
series: buildSeriesDto(seriesDto),
|
||||
|
||||
panes: gridDto.gridOptions.panesDto?.length > 0 ? gridDto.gridOptions.panesDto : undefined,
|
||||
commonSeriesSettings: gridDto.gridOptions.commonSeriesSettingsDto,
|
||||
|
|
@ -121,7 +140,7 @@ const Chart = (props: ChartProps) => {
|
|||
}
|
||||
|
||||
setChartOptions(options)
|
||||
}, [gridDto, series, searchParams, urlSearchParams])
|
||||
}, [gridDto, allSeries, initialized.current, searchParams, urlSearchParams])
|
||||
|
||||
const onFilter = useCallback(
|
||||
(value?: string) => {
|
||||
|
|
@ -169,50 +188,64 @@ const Chart = (props: ChartProps) => {
|
|||
[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,
|
||||
},
|
||||
},
|
||||
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(
|
||||
<Notification type="danger" duration={2000}>
|
||||
Alanlar getirilemedi {error.toString()}
|
||||
</Notification>,
|
||||
{ placement: 'top-end' },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (gridDto && !initialized.current) {
|
||||
setSeries(gridDto.gridOptions.seriesDto)
|
||||
initialized.current = true
|
||||
}
|
||||
}, [gridDto])
|
||||
if (props.listFormCode) getFields()
|
||||
}, [props.listFormCode])
|
||||
|
||||
const [openDialog, setOpenDialog] = useState(false)
|
||||
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 (
|
||||
<Container className={DX_CLASSNAMES}>
|
||||
|
|
@ -252,26 +285,24 @@ const Chart = (props: ChartProps) => {
|
|||
}}
|
||||
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"
|
||||
/>
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
variant={'default'}
|
||||
className="text-sm"
|
||||
onClick={async () => {
|
||||
setSeries(gridDto?.gridOptions?.seriesDto ?? [])
|
||||
await props.refreshGridDto()
|
||||
initialized.current = false
|
||||
await refreshGridDto()
|
||||
}}
|
||||
title="Refresh Data"
|
||||
>
|
||||
<FaSyncAlt className="w-3 h-3" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
variant="default"
|
||||
className="text-sm"
|
||||
onClick={() => setOpenDialog(true)}
|
||||
title="Edit Series"
|
||||
title="Series Özelleştir"
|
||||
>
|
||||
<FaCrosshairs className="w-3 h-3" />
|
||||
</Button>
|
||||
|
|
@ -299,109 +330,13 @@ const Chart = (props: ChartProps) => {
|
|||
</div>
|
||||
<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>
|
||||
<ChartSeriesDialog
|
||||
open={openDialog}
|
||||
onClose={() => setOpenDialog(false)}
|
||||
initialSeries={allSeries.filter((s) => s.userId === userName)}
|
||||
fieldList={fieldList}
|
||||
onSave={onSave}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Container>
|
||||
|
|
|
|||
279
ui/src/views/list/ChartSeriesDialog.tsx
Normal file
279
ui/src/views/list/ChartSeriesDialog.tsx
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
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'
|
||||
import { SelectBoxOption } from '@/shared/types'
|
||||
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
|
||||
278
ui/src/views/list/ChartSeriesDialog1.tsx
Normal file
278
ui/src/views/list/ChartSeriesDialog1.tsx
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
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'
|
||||
import { SelectBoxOption } from '@/shared/types'
|
||||
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'
|
||||
|
||||
interface ChartSeriesDialog1 {
|
||||
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 ChartSeriesDialog1 = ({
|
||||
open,
|
||||
onClose,
|
||||
initialSeries,
|
||||
fieldList,
|
||||
onSave,
|
||||
}: ChartSeriesDialog1) => {
|
||||
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: '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 ChartSeriesDialog1
|
||||
|
|
@ -168,6 +168,7 @@ const List = () => {
|
|||
/>
|
||||
) : viewMode === 'chart' ? (
|
||||
<Chart
|
||||
id={gridDto?.gridOptions.id!}
|
||||
listFormCode={listFormCode}
|
||||
filter={searchParams.toString()}
|
||||
isSubForm={true}
|
||||
|
|
|
|||
Loading…
Reference in a new issue