Optimizasyon güncellemeleri
This commit is contained in:
parent
d982a3ce42
commit
7051a84b4b
19 changed files with 525 additions and 465 deletions
|
|
@ -5,6 +5,7 @@ using Sozsoft.Settings.Localization;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Localization;
|
using Volo.Abp.Localization;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
|
using Volo.Abp.Threading;
|
||||||
using SettingDefinition = Sozsoft.Settings.Entities.SettingDefinition;
|
using SettingDefinition = Sozsoft.Settings.Entities.SettingDefinition;
|
||||||
|
|
||||||
namespace Sozsoft.Settings;
|
namespace Sozsoft.Settings;
|
||||||
|
|
@ -22,8 +23,7 @@ public class SettingsDefinitionProvider : SettingDefinitionProvider
|
||||||
public override void Define(ISettingDefinitionContext context)
|
public override void Define(ISettingDefinitionContext context)
|
||||||
{
|
{
|
||||||
repository.DisableTracking();
|
repository.DisableTracking();
|
||||||
var settingDefinitions = repository.GetListAsync().Result;
|
var settingDefinitions = AsyncHelper.RunSync(() => repository.GetListAsync());
|
||||||
//var settingDefinitions = AsyncHelper.RunSync(() => repository.ToListAsync());
|
|
||||||
|
|
||||||
foreach (var item in settingDefinitions.OrderBy(a => a.Order))
|
foreach (var item in settingDefinitions.OrderBy(a => a.Order))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Volo.Abp.Application.Dtos;
|
using Volo.Abp.Application.Dtos;
|
||||||
using Volo.Abp.Authorization;
|
using Volo.Abp.Authorization;
|
||||||
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Entities;
|
||||||
|
|
@ -272,16 +273,28 @@ public class ForumAppService : PlatformAppService, IForumAppService
|
||||||
|
|
||||||
public async Task<ForumTopicDto> GetTopicAsync(Guid id)
|
public async Task<ForumTopicDto> GetTopicAsync(Guid id)
|
||||||
{
|
{
|
||||||
var topic = await _topicRepository.GetAsync(id);
|
var queryable = await _topicRepository.GetQueryableAsync();
|
||||||
|
var affectedRows = await queryable
|
||||||
|
.Where(t => t.Id == id)
|
||||||
|
.ExecuteUpdateAsync(setters =>
|
||||||
|
setters.SetProperty(t => t.ViewCount, t => t.ViewCount + 1)
|
||||||
|
);
|
||||||
|
|
||||||
// View count artırma işlemi arka planda yapılmalı (performans için)
|
if (affectedRows == 0)
|
||||||
// Her okumada update yapmak performans sorununa neden olur
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
var t = await _topicRepository.GetAsync(id);
|
throw new EntityNotFoundException(typeof(ForumTopic), id);
|
||||||
t.ViewCount++;
|
}
|
||||||
await _topicRepository.UpdateAsync(t);
|
|
||||||
});
|
var topic = await AsyncExecuter.FirstOrDefaultAsync(
|
||||||
|
queryable
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(t => t.Id == id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (topic == null)
|
||||||
|
{
|
||||||
|
throw new EntityNotFoundException(typeof(ForumTopic), id);
|
||||||
|
}
|
||||||
|
|
||||||
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
|
return ObjectMapper.Map<ForumTopic, ForumTopicDto>(topic);
|
||||||
}
|
}
|
||||||
|
|
@ -454,14 +467,13 @@ public class ForumAppService : PlatformAppService, IForumAppService
|
||||||
topic.ReplyCount = Math.Max(0, topic.ReplyCount - 1);
|
topic.ReplyCount = Math.Max(0, topic.ReplyCount - 1);
|
||||||
category.PostCount = Math.Max(0, category.PostCount ?? 0 - 1);
|
category.PostCount = Math.Max(0, category.PostCount ?? 0 - 1);
|
||||||
|
|
||||||
// 🔠Last post değişti mi kontrol et
|
// Last post değişti mi kontrol et
|
||||||
var latestPost = await _postRepository
|
var postsQueryable = await _postRepository.GetQueryableAsync();
|
||||||
.GetQueryableAsync()
|
var latestPost = await AsyncExecuter.FirstOrDefaultAsync(
|
||||||
.ContinueWith(q => q.Result
|
postsQueryable
|
||||||
.Where(p => p.TopicId == topic.Id)
|
.Where(p => p.TopicId == topic.Id)
|
||||||
.OrderByDescending(p => p.CreationTime)
|
.OrderByDescending(p => p.CreationTime)
|
||||||
.FirstOrDefault()
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (latestPost != null)
|
if (latestPost != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ public class ListFormQueryPreviewAppService : PlatformAppService
|
||||||
|
|
||||||
var (_, _, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
var (_, _, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
||||||
|
|
||||||
selectQueryManager.PrepareQueries(listForm, readableFields, dataSourceType, customizations);
|
await selectQueryManager.PrepareQueriesAsync(listForm, readableFields, dataSourceType, customizations);
|
||||||
|
|
||||||
return selectQueryManager.SelectQuery;
|
return selectQueryManager.SelectQuery;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,12 +55,12 @@ public class ListFormDataAppService : PlatformAppService
|
||||||
|
|
||||||
var queryParameters = httpContext.Request.Query.ToDictionary(x => x.Key, x => x.Value);
|
var queryParameters = httpContext.Request.Query.ToDictionary(x => x.Key, x => x.Value);
|
||||||
|
|
||||||
object filter = new object[] { input.Data[0], "=", input.Keys[0] };
|
object[] filter = [input.Data[0], "=", input.Keys[0]];
|
||||||
|
|
||||||
var selectRequest = new SelectRequestDto
|
var selectRequest = new SelectRequestDto
|
||||||
{
|
{
|
||||||
ListFormCode = input.ListFormCode,
|
ListFormCode = input.ListFormCode,
|
||||||
Filter = filter.ToString(),
|
Filter = JsonSerializer.Serialize(filter),
|
||||||
Skip = 0,
|
Skip = 0,
|
||||||
Take = 1,
|
Take = 1,
|
||||||
RequireTotalCount = false,
|
RequireTotalCount = false,
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
|
||||||
|
|
||||||
var (dynamicDataRepository, connectionString, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
var (dynamicDataRepository, connectionString, dataSourceType) = await dynamicDataManager.GetAsync(listForm.IsTenant, listForm.DataSourceCode);
|
||||||
|
|
||||||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
await selectQueryManager.PrepareQueriesAsync(listForm, fields, dataSourceType, customizations, queryParams);
|
||||||
var param = selectQueryManager.SelectQueryParameters;
|
var param = selectQueryManager.SelectQueryParameters;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(queryParams.Group) && !string.IsNullOrEmpty(selectQueryManager.GroupQuery))
|
if (!string.IsNullOrEmpty(queryParams.Group) && !string.IsNullOrEmpty(selectQueryManager.GroupQuery))
|
||||||
|
|
@ -293,11 +293,6 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
|
||||||
//kullaniciya ait ListFormField verilerini al
|
//kullaniciya ait ListFormField verilerini al
|
||||||
var fields = await listFormFieldManager.GetUserListFormFields(ListFormCode);
|
var fields = await listFormFieldManager.GetUserListFormFields(ListFormCode);
|
||||||
|
|
||||||
// selectQueryManager.PrepareQueries(listForm, fields);
|
|
||||||
// fields = fields.Where(c => selectQueryManager.SelectFields.Any(sc => sc.FieldName == c.FieldName))
|
|
||||||
// .OrderBy(c => c.ListOrderNo)
|
|
||||||
// .ThenBy(c => c.CaptionName)
|
|
||||||
// .ToList();
|
|
||||||
fields = fields.OrderBy(c => c.ListOrderNo)
|
fields = fields.OrderBy(c => c.ListOrderNo)
|
||||||
.ThenBy(c => c.CaptionName)
|
.ThenBy(c => c.CaptionName)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using System.Data;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Sozsoft.Platform.Entities;
|
using Sozsoft.Platform.Entities;
|
||||||
using Sozsoft.Platform.Enums;
|
using Sozsoft.Platform.Enums;
|
||||||
using Sozsoft.Platform.Extensions;
|
using Sozsoft.Platform.Extensions;
|
||||||
|
|
@ -32,11 +33,11 @@ public interface ISelectQueryManager
|
||||||
List<string> SummaryQueries { get; }
|
List<string> SummaryQueries { get; }
|
||||||
public bool IsAppliedGridFilter { get; }
|
public bool IsAppliedGridFilter { get; }
|
||||||
public bool IsAppliedServerFilter { get; }
|
public bool IsAppliedServerFilter { get; }
|
||||||
void PrepareQueries(ListForm listform,
|
Task PrepareQueriesAsync(ListForm listform,
|
||||||
List<ListFormField> listFormFields,
|
List<ListFormField> listFormFields,
|
||||||
DataSourceTypeEnum dataSourceType,
|
DataSourceTypeEnum dataSourceType,
|
||||||
List<ListFormCustomization> listFormCustomizations = null,
|
List<ListFormCustomization> listFormCustomizations = null,
|
||||||
QueryParameters queryParams = null);
|
QueryParameters queryParams = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SelectField
|
public class SelectField
|
||||||
|
|
@ -129,11 +130,11 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
DefaultValueHelper = defaultValueHelper;
|
DefaultValueHelper = defaultValueHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareQueries(ListForm listform,
|
public async Task PrepareQueriesAsync(ListForm listform,
|
||||||
List<ListFormField> listFormFields,
|
List<ListFormField> listFormFields,
|
||||||
DataSourceTypeEnum dataSourceType,
|
DataSourceTypeEnum dataSourceType,
|
||||||
List<ListFormCustomization> listFormCustomizations = null,
|
List<ListFormCustomization> listFormCustomizations = null,
|
||||||
QueryParameters queryParams = null)
|
QueryParameters queryParams = null)
|
||||||
{
|
{
|
||||||
ResetQueryState();
|
ResetQueryState();
|
||||||
|
|
||||||
|
|
@ -170,7 +171,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
var joinParts = SelectFields.Where(c => !string.IsNullOrEmpty(c.JoinSql)).Select(c => c.JoinSql).Distinct();
|
var joinParts = SelectFields.Where(c => !string.IsNullOrEmpty(c.JoinSql)).Select(c => c.JoinSql).Distinct();
|
||||||
JoinParts.AddRange(joinParts);
|
JoinParts.AddRange(joinParts);
|
||||||
|
|
||||||
List<string> whereFields = GetWhereFields(listform, listFormFields, queryParams);
|
List<string> whereFields = await GetWhereFieldsAsync(listform, listFormFields, queryParams);
|
||||||
WhereParts.AddRange(whereFields);
|
WhereParts.AddRange(whereFields);
|
||||||
|
|
||||||
List<string> sortFields = GetSortFields(listform, listFormFields, queryParams?.Sort);
|
List<string> sortFields = GetSortFields(listform, listFormFields, queryParams?.Sort);
|
||||||
|
|
@ -343,7 +344,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetWhereFields(ListForm listform, List<ListFormField> listFormFields, QueryParameters queryParams = null)
|
private async Task<List<string>> GetWhereFieldsAsync(ListForm listform, List<ListFormField> listFormFields, QueryParameters queryParams = null)
|
||||||
{
|
{
|
||||||
var whereParts = new List<string>();
|
var whereParts = new List<string>();
|
||||||
|
|
||||||
|
|
@ -436,7 +437,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
whereParts.Add("AND");
|
whereParts.Add("AND");
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids = BranchUsersRepository.GetListAsync((a) => a.UserId == CurrentUser.Id.Value).Result;
|
var ids = await BranchUsersRepository.GetListAsync((a) => a.UserId == CurrentUser.Id.Value);
|
||||||
if (ids.Count > 0)
|
if (ids.Count > 0)
|
||||||
{
|
{
|
||||||
whereParts.Add($"\"BranchId\" IN ({string.Join(",", ids.Select(a => $"'{a.BranchId}'"))})");
|
whereParts.Add($"\"BranchId\" IN ({string.Join(",", ids.Select(a => $"'{a.BranchId}'"))})");
|
||||||
|
|
@ -454,7 +455,7 @@ public class SelectQueryManager : PlatformDomainService, ISelectQueryManager
|
||||||
whereParts.Add("AND");
|
whereParts.Add("AND");
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids = OuRepository.GetOrganizationUnitIdsWithChildren(CurrentUser.Id.Value).Result;
|
var ids = await OuRepository.GetOrganizationUnitIdsWithChildren(CurrentUser.Id.Value);
|
||||||
if (ids.Count > 0)
|
if (ids.Count > 0)
|
||||||
{
|
{
|
||||||
whereParts.Add($"\"OrganizationUnitId\" IN ({string.Join(",", ids.Select(a => $"'{a}'"))})");
|
whereParts.Add($"\"OrganizationUnitId\" IN ({string.Join(",", ids.Select(a => $"'{a}'"))})");
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ public class DynamicFormReport : XtraReport
|
||||||
Skip = 0
|
Skip = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
await selectQueryManager.PrepareQueriesAsync(listForm, fields, dataSourceType, customizations, queryParams);
|
||||||
|
|
||||||
var reportFields = GetReportFields(listForm, fields, selectQueryManager, defaultValueHelper);
|
var reportFields = GetReportFields(listForm, fields, selectQueryManager, defaultValueHelper);
|
||||||
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ public class DynamicGridReport : XtraReport
|
||||||
Skip = 0
|
Skip = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
await selectQueryManager.PrepareQueriesAsync(listForm, fields, dataSourceType, customizations, queryParams);
|
||||||
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
||||||
var editorTypesByField = DynamicReportImageHelper.CreateEditorTypeMap(listForm);
|
var editorTypesByField = DynamicReportImageHelper.CreateEditorTypeMap(listForm);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ public class DynamicTreeReport : XtraReport
|
||||||
Skip = 0
|
Skip = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
await selectQueryManager.PrepareQueriesAsync(listForm, fields, dataSourceType, customizations, queryParams);
|
||||||
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
||||||
var editorTypesByField = DynamicReportImageHelper.CreateEditorTypeMap(listForm);
|
var editorTypesByField = DynamicReportImageHelper.CreateEditorTypeMap(listForm);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { AUTH_API_NAME, DEFAULT_API_NAME } from '@/constants/app.constant'
|
import { AUTH_API_NAME, DEFAULT_API_NAME } from '@/constants/app.constant'
|
||||||
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
|
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||||
import authApiService from './authApi.service'
|
import authApiService from './authApi.service'
|
||||||
import platformApiService from './platformApi.service'
|
import platformApiService from './platformApi.service'
|
||||||
|
|
||||||
|
|
@ -11,29 +11,18 @@ export default {
|
||||||
fetchData<Response = unknown, Request = Record<string, unknown>>(
|
fetchData<Response = unknown, Request = Record<string, unknown>>(
|
||||||
param: AxiosRequestConfig<Request>,
|
param: AxiosRequestConfig<Request>,
|
||||||
config?: Config,
|
config?: Config,
|
||||||
) {
|
): Promise<AxiosResponse<Response>> {
|
||||||
return new Promise<AxiosResponse<Response>>((resolve, reject) => {
|
if (!config || config?.apiName === DEFAULT_API_NAME) {
|
||||||
if (!config || config?.apiName === DEFAULT_API_NAME) {
|
return platformApiService(param) as Promise<AxiosResponse<Response>>
|
||||||
platformApiService(param)
|
}
|
||||||
.then((response: AxiosResponse<Response>) => {
|
|
||||||
resolve(response)
|
if (config?.apiName === AUTH_API_NAME) {
|
||||||
})
|
return authApiService(param).catch((errors) => {
|
||||||
.catch((errors: AxiosError) => {
|
console.error(errors)
|
||||||
// console.error(errors)
|
throw errors
|
||||||
reject(errors)
|
}) as Promise<AxiosResponse<Response>>
|
||||||
})
|
}
|
||||||
} else if (config?.apiName === AUTH_API_NAME) {
|
|
||||||
authApiService(param)
|
return Promise.reject(new Error('NO API DEFINED'))
|
||||||
.then((response: AxiosResponse<Response>) => {
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((errors: AxiosError) => {
|
|
||||||
console.error(errors)
|
|
||||||
reject(errors)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
reject(new Error('NO API DEFINED'))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
import { ColumnFormatDto, GridBoxOptionsDto } from '@/proxy/form/models'
|
import { ColumnFormatDto, GridBoxOptionsDto } from '@/proxy/form/models'
|
||||||
import { Button } from 'devextreme-react/button'
|
import { ReactElement } from 'react'
|
||||||
import DataGrid, { DataGridRef } from 'devextreme-react/data-grid'
|
import { SharedGridBoxEditor } from '@/views/shared/SharedGridBoxEditor'
|
||||||
import DropDownBox from 'devextreme-react/drop-down-box'
|
|
||||||
import { ReactElement, useRef } from 'react'
|
|
||||||
|
|
||||||
const GridBoxEditorComponent = ({
|
const GridBoxEditorComponent = ({
|
||||||
value,
|
value,
|
||||||
values,
|
|
||||||
options,
|
options,
|
||||||
onValueChanged,
|
onValueChanged,
|
||||||
col,
|
col,
|
||||||
|
|
@ -19,99 +16,17 @@ const GridBoxEditorComponent = ({
|
||||||
col?: ColumnFormatDto
|
col?: ColumnFormatDto
|
||||||
editorOptions: any
|
editorOptions: any
|
||||||
}): ReactElement => {
|
}): ReactElement => {
|
||||||
const gridRef = useRef<DataGridRef>(null)
|
|
||||||
const val = Array.isArray(value) ? value : [value]
|
|
||||||
const lookupDto = col?.lookupDto
|
const lookupDto = col?.lookupDto
|
||||||
|
|
||||||
// const dataSource: any =
|
|
||||||
// typeof lookupDto?.dataSource === 'function'
|
|
||||||
// ? lookupDto.dataSource(values)
|
|
||||||
// : (lookupDto?.dataSource ?? [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropDownBox
|
<SharedGridBoxEditor
|
||||||
{...editorOptions}
|
value={value}
|
||||||
value={val}
|
|
||||||
dataSource={editorOptions?.dataSource}
|
dataSource={editorOptions?.dataSource}
|
||||||
valueExpr={lookupDto?.valueExpr}
|
lookup={lookupDto}
|
||||||
displayExpr={lookupDto?.displayExpr}
|
options={options}
|
||||||
showClearButton={options?.showClearButton}
|
editorOptions={editorOptions}
|
||||||
acceptCustomValue={options?.acceptCustomValue}
|
onValueChanged={onValueChanged}
|
||||||
onValueChanged={(e) => {
|
/>
|
||||||
onValueChanged?.(typeof e.value === 'string' ? e.value?.trim() : e.value)
|
|
||||||
|
|
||||||
//Eğer clear button kullanılırsa altında grid bilgileride otomatik deselect olmalı ve dropbox kapanmalı
|
|
||||||
if (e.value === null) {
|
|
||||||
e.component.close()
|
|
||||||
gridRef.current?.instance().deselectAll()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
contentRender={(e) => (
|
|
||||||
<>
|
|
||||||
<DataGrid
|
|
||||||
ref={gridRef}
|
|
||||||
dataSource={editorOptions?.dataSource}
|
|
||||||
key={lookupDto?.valueExpr}
|
|
||||||
filterRow={{
|
|
||||||
visible: options?.filterRowVisible,
|
|
||||||
applyFilter: 'auto',
|
|
||||||
}}
|
|
||||||
columns={options?.columns.map((c: string) => c.toLowerCase())} //AliBaba -> alibaba
|
|
||||||
hoverStateEnabled={true}
|
|
||||||
height={options?.height}
|
|
||||||
selection={{
|
|
||||||
mode: options?.selectionMode,
|
|
||||||
}}
|
|
||||||
onRowClick={(event) => {
|
|
||||||
//Single seçimlerde otomatik kapanmalı ve seçtiği değer yazılmalı
|
|
||||||
if (options?.selectionMode === 'single') {
|
|
||||||
e.component.option('value', event.data.key)
|
|
||||||
e.component.close()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onEditorPrepared={(event) => {
|
|
||||||
const getListValues = async () => {
|
|
||||||
const data = await editorOptions?.dataSource?.load()
|
|
||||||
|
|
||||||
const listValues = data.map((a: any) => ({
|
|
||||||
key: a.key,
|
|
||||||
name: a.name,
|
|
||||||
}))
|
|
||||||
|
|
||||||
event.component.option(
|
|
||||||
'selectedRowKeys',
|
|
||||||
listValues.filter((a: any) => val.includes(a.key)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
getListValues()
|
|
||||||
}}
|
|
||||||
></DataGrid>
|
|
||||||
{options?.selectionMode === 'multiple' && (
|
|
||||||
<div className="text-center">
|
|
||||||
<Button
|
|
||||||
text="OK"
|
|
||||||
className="mr-2 min-w-[100px]"
|
|
||||||
onClick={() => {
|
|
||||||
e.component.option(
|
|
||||||
'value',
|
|
||||||
gridRef.current?.instance().getSelectedRowKeys().map((a: any) => a.key),
|
|
||||||
)
|
|
||||||
e.component.close()
|
|
||||||
}}
|
|
||||||
></Button>
|
|
||||||
<Button
|
|
||||||
text="Cancel"
|
|
||||||
className="min-w-[100px]"
|
|
||||||
onClick={() => {
|
|
||||||
e.component.close()
|
|
||||||
}}
|
|
||||||
></Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
></DropDownBox>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,9 @@ import {
|
||||||
EditingFormItemDto,
|
EditingFormItemDto,
|
||||||
FieldCustomValueTypeEnum,
|
FieldCustomValueTypeEnum,
|
||||||
GridDto,
|
GridDto,
|
||||||
ListFormCustomizationTypeEnum,
|
|
||||||
PlatformEditorTypes,
|
PlatformEditorTypes,
|
||||||
SubFormTabTypeEnum,
|
SubFormTabTypeEnum,
|
||||||
} from '@/proxy/form/models'
|
} from '@/proxy/form/models'
|
||||||
import {
|
|
||||||
getListFormCustomization,
|
|
||||||
postListFormCustomization,
|
|
||||||
} from '@/services/list-form-customization.service'
|
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { executeEditorScript } from '@/utils/editorScriptRuntime'
|
import { executeEditorScript } from '@/utils/editorScriptRuntime'
|
||||||
import { Template } from 'devextreme-react/core/template'
|
import { Template } from 'devextreme-react/core/template'
|
||||||
|
|
@ -58,8 +53,8 @@ import {
|
||||||
controlStyleCondition,
|
controlStyleCondition,
|
||||||
extractSearchParamsFields,
|
extractSearchParamsFields,
|
||||||
GridExtraFilterState,
|
GridExtraFilterState,
|
||||||
|
safeJsonParse,
|
||||||
setFormEditingExtraItemValues,
|
setFormEditingExtraItemValues,
|
||||||
setGridPanelColor,
|
|
||||||
} from './Utils'
|
} from './Utils'
|
||||||
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
||||||
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
import { ImageUploadEditorComponent } from './editors/ImageUploadEditorComponent'
|
||||||
|
|
@ -79,6 +74,7 @@ import { Loading } from '@/components/shared'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import { NotePanel } from '../form/notes/NotePanel'
|
import { NotePanel } from '../form/notes/NotePanel'
|
||||||
|
import { useListFormStateStoring } from './useListFormStateStoring'
|
||||||
|
|
||||||
interface GridProps {
|
interface GridProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -89,8 +85,6 @@ interface GridProps {
|
||||||
gridDto?: GridDto
|
gridDto?: GridDto
|
||||||
}
|
}
|
||||||
|
|
||||||
const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' // kullanici tanimli gridState ile islem gormus gridin paneline ait renk
|
|
||||||
|
|
||||||
const isTemporaryDxKey = (key: unknown) => typeof key === 'string' && key.startsWith('_DX_KEY_')
|
const isTemporaryDxKey = (key: unknown) => typeof key === 'string' && key.startsWith('_DX_KEY_')
|
||||||
|
|
||||||
const getPersistedInsertedKey = (
|
const getPersistedInsertedKey = (
|
||||||
|
|
@ -322,47 +316,12 @@ const Grid = (props: GridProps) => {
|
||||||
}
|
}
|
||||||
}, [searchParams])
|
}, [searchParams])
|
||||||
|
|
||||||
// StateStoring için storageKey'i memoize et
|
const { customSaveState, customLoadState } = useListFormStateStoring({
|
||||||
const storageKey = useMemo(() => {
|
listFormCode,
|
||||||
return gridDto?.gridOptions.stateStoringDto?.storageKey ?? ''
|
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
|
||||||
}, [gridDto?.gridOptions.stateStoringDto?.storageKey])
|
filterPrefix: 'list',
|
||||||
|
skipSaveRef: isEditingRef,
|
||||||
const customSaveState = useCallback(
|
})
|
||||||
(state: any) => {
|
|
||||||
if (isEditingRef.current) {
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
|
||||||
return postListFormCustomization({
|
|
||||||
listFormCode: listFormCode,
|
|
||||||
customizationType: ListFormCustomizationTypeEnum.GridState,
|
|
||||||
filterName: `list-${storageKey}`,
|
|
||||||
customizationData: JSON.stringify(state),
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
setGridPanelColor(statedGridPanelColor)
|
|
||||||
toast.push(
|
|
||||||
<Notification type="success" duration={2000}>
|
|
||||||
{translate('::ListForms.ListForm.GridStateSaved')}
|
|
||||||
</Notification>,
|
|
||||||
{
|
|
||||||
placement: 'bottom-end',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.push(
|
|
||||||
<Notification type="danger" duration={2500}>
|
|
||||||
{translate('::ListForms.ListForm.GridStateSaveError')}
|
|
||||||
</Notification>,
|
|
||||||
{
|
|
||||||
placement: 'bottom-end',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
throw err
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[listFormCode, storageKey, translate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const { filterToolbarData, ...filterData } = useFilters({
|
const { filterToolbarData, ...filterData } = useFilters({
|
||||||
gridDto,
|
gridDto,
|
||||||
|
|
@ -839,7 +798,9 @@ const Grid = (props: GridProps) => {
|
||||||
} else {
|
} else {
|
||||||
childEditor?.option('disabled', shouldDisable)
|
childEditor?.option('disabled', shouldDisable)
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.log(`Error occurred while updating child editor for field "${childFieldName}":`, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -928,21 +889,6 @@ const Grid = (props: GridProps) => {
|
||||||
[gridDto, cascadeFieldsMap, parentToChildrenMap, mode],
|
[gridDto, cascadeFieldsMap, parentToChildrenMap, mode],
|
||||||
)
|
)
|
||||||
|
|
||||||
const customLoadState = useCallback(() => {
|
|
||||||
return getListFormCustomization(
|
|
||||||
listFormCode,
|
|
||||||
ListFormCustomizationTypeEnum.GridState,
|
|
||||||
`list-${storageKey}`,
|
|
||||||
).then((response: any) => {
|
|
||||||
if (response.data?.length > 0) {
|
|
||||||
setGridPanelColor(statedGridPanelColor)
|
|
||||||
return JSON.parse(response.data[0].customizationData)
|
|
||||||
}
|
|
||||||
// Veri yoksa null dön (DevExtreme bunu default state olarak algılar)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}, [listFormCode, storageKey])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// React state'i sıfırla - eski değerlerin customLoadState'i erken tetiklemesini önle
|
// React state'i sıfırla - eski değerlerin customLoadState'i erken tetiklemesini önle
|
||||||
setGridDataSource(undefined)
|
setGridDataSource(undefined)
|
||||||
|
|
@ -1005,7 +951,7 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
let base: any = null
|
let base: any = null
|
||||||
if (defaultSearchParams.current) {
|
if (defaultSearchParams.current) {
|
||||||
base = JSON.parse(defaultSearchParams.current)
|
base = safeJsonParse(defaultSearchParams.current, null, 'Search filter parse error:')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default filter tripletlerini çıkar
|
// Default filter tripletlerini çıkar
|
||||||
|
|
@ -1168,7 +1114,7 @@ const Grid = (props: GridProps) => {
|
||||||
instance.option('remoteOperations', remoteOps)
|
instance.option('remoteOperations', remoteOps)
|
||||||
instance.option('dataSource', gridDataSource)
|
instance.option('dataSource', gridDataSource)
|
||||||
})
|
})
|
||||||
}, [gridDto, gridDataSource, columnData])
|
}, [gridDto, gridDataSource, columnData, customLoadState])
|
||||||
|
|
||||||
// StateStoring'i devre dışı bırak - manuel kaydetme kullanılacak
|
// StateStoring'i devre dışı bırak - manuel kaydetme kullanılacak
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1201,7 +1147,7 @@ const Grid = (props: GridProps) => {
|
||||||
const parsed = JSON.parse(rawFilter)
|
const parsed = JSON.parse(rawFilter)
|
||||||
const filters = extractSearchParamsFields(parsed)
|
const filters = extractSearchParamsFields(parsed)
|
||||||
|
|
||||||
const hasFilter = filters.some(([field, op, val]) => field === i.dataField)
|
const hasFilter = filters.some(([field]) => field === i.dataField)
|
||||||
|
|
||||||
if (hasFilter) {
|
if (hasFilter) {
|
||||||
const existsInExtra = extraFilters.some((f) => f.fieldName === i.dataField && !!f.value)
|
const existsInExtra = extraFilters.some((f) => f.fieldName === i.dataField && !!f.value)
|
||||||
|
|
@ -1211,7 +1157,9 @@ const Grid = (props: GridProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.error('EditorOptions parse error:', i.dataField, err)
|
||||||
|
}
|
||||||
|
|
||||||
const fieldName = i.dataField.split(':')[0]
|
const fieldName = i.dataField.split(':')[0]
|
||||||
const listFormField = gridDto?.columnFormats.find((x: any) => x.fieldName === fieldName)
|
const listFormField = gridDto?.columnFormats.find((x: any) => x.fieldName === fieldName)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import Container from '@/components/shared/Container'
|
import Container from '@/components/shared/Container'
|
||||||
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
import { APP_NAME, DX_CLASSNAMES } from '@/constants/app.constant'
|
||||||
import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
import { GridDto, ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
||||||
import {
|
import { postListFormCustomization } from '@/services/list-form-customization.service'
|
||||||
getListFormCustomization,
|
|
||||||
postListFormCustomization,
|
|
||||||
} from '@/services/list-form-customization.service'
|
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import Chart, { ChartRef, CommonSeriesSettings, Size, Tooltip } from 'devextreme-react/chart'
|
import Chart, { ChartRef, CommonSeriesSettings, Size, Tooltip } from 'devextreme-react/chart'
|
||||||
import PivotGrid, {
|
import PivotGrid, {
|
||||||
|
|
@ -41,6 +38,7 @@ import { useListFormColumns } from './useListFormColumns'
|
||||||
import { useStoreState } from '@/store'
|
import { useStoreState } from '@/store'
|
||||||
import Checkbox from '@/components/ui/Checkbox'
|
import Checkbox from '@/components/ui/Checkbox'
|
||||||
import Toolbar, { Item } from 'devextreme-react/toolbar'
|
import Toolbar, { Item } from 'devextreme-react/toolbar'
|
||||||
|
import { useListFormStateStoring } from './useListFormStateStoring'
|
||||||
|
|
||||||
interface PivotProps {
|
interface PivotProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -52,8 +50,6 @@ interface PivotProps {
|
||||||
refreshGridDto: () => Promise<void>
|
refreshGridDto: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)' // kullanici tanimli gridState ile islem gormus gridin paneline ait renk
|
|
||||||
|
|
||||||
type LookupDisplayValues = Record<string, Map<string, string>>
|
type LookupDisplayValues = Record<string, Map<string, string>>
|
||||||
|
|
||||||
const Pivot = (props: PivotProps) => {
|
const Pivot = (props: PivotProps) => {
|
||||||
|
|
@ -72,11 +68,12 @@ const Pivot = (props: PivotProps) => {
|
||||||
const [lookupDisplayValues, setLookupDisplayValues] = useState<LookupDisplayValues>({})
|
const [lookupDisplayValues, setLookupDisplayValues] = useState<LookupDisplayValues>({})
|
||||||
const [showChart, setShowChart] = useState(false)
|
const [showChart, setShowChart] = useState(false)
|
||||||
|
|
||||||
// StateStoring için storageKey'i memoize et
|
const { customSaveState, customLoadState, storageKey } = useListFormStateStoring({
|
||||||
const storageKey = useMemo(() => {
|
listFormCode,
|
||||||
return gridDto?.gridOptions.stateStoringDto?.storageKey ?? ''
|
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
|
||||||
}, [gridDto?.gridOptions.stateStoringDto?.storageKey])
|
filterPrefix: 'pivot',
|
||||||
|
showSaveToast: false,
|
||||||
|
})
|
||||||
|
|
||||||
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
const { createSelectDataSource } = useListFormCustomDataSource({ gridRef })
|
||||||
const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({
|
const { getBandedColumns, loadLookupDisplayValues } = useListFormColumns({
|
||||||
|
|
@ -212,35 +209,6 @@ const Pivot = (props: PivotProps) => {
|
||||||
[listFormCode, translate],
|
[listFormCode, translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateStoring fonksiyonlarını ref'e kaydet
|
|
||||||
const customSaveState = useCallback(
|
|
||||||
(state: any) => {
|
|
||||||
return postListFormCustomization({
|
|
||||||
listFormCode: listFormCode,
|
|
||||||
customizationType: ListFormCustomizationTypeEnum.GridState,
|
|
||||||
filterName: `pivot-${storageKey}`,
|
|
||||||
customizationData: JSON.stringify(state),
|
|
||||||
}).then(() => {
|
|
||||||
setGridPanelColor(statedGridPanelColor)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[listFormCode, storageKey],
|
|
||||||
)
|
|
||||||
|
|
||||||
const customLoadState = useCallback(() => {
|
|
||||||
return getListFormCustomization(
|
|
||||||
listFormCode,
|
|
||||||
ListFormCustomizationTypeEnum.GridState,
|
|
||||||
`pivot-${storageKey}`,
|
|
||||||
).then((response: any) => {
|
|
||||||
if (response.data?.length > 0) {
|
|
||||||
setGridPanelColor(statedGridPanelColor)
|
|
||||||
return JSON.parse(response.data[0].customizationData)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}, [listFormCode, storageKey])
|
|
||||||
|
|
||||||
const savePivotGridState = useCallback(() => {
|
const savePivotGridState = useCallback(() => {
|
||||||
const instance = gridRef.current?.instance()
|
const instance = gridRef.current?.instance()
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,9 @@ import {
|
||||||
DbTypeEnum,
|
DbTypeEnum,
|
||||||
EditingFormItemDto,
|
EditingFormItemDto,
|
||||||
GridDto,
|
GridDto,
|
||||||
ListFormCustomizationTypeEnum,
|
|
||||||
PlatformEditorTypes,
|
PlatformEditorTypes,
|
||||||
SubFormTabTypeEnum,
|
SubFormTabTypeEnum,
|
||||||
} from '@/proxy/form/models'
|
} from '@/proxy/form/models'
|
||||||
import {
|
|
||||||
getListFormCustomization,
|
|
||||||
postListFormCustomization,
|
|
||||||
} from '@/services/list-form-customization.service'
|
|
||||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { captionize } from 'devextreme/core/utils/inflector'
|
import { captionize } from 'devextreme/core/utils/inflector'
|
||||||
import { Template } from 'devextreme-react/core/template'
|
import { Template } from 'devextreme-react/core/template'
|
||||||
|
|
@ -49,8 +44,8 @@ import {
|
||||||
autoNumber,
|
autoNumber,
|
||||||
controlStyleCondition,
|
controlStyleCondition,
|
||||||
GridExtraFilterState,
|
GridExtraFilterState,
|
||||||
|
safeJsonParse,
|
||||||
setFormEditingExtraItemValues,
|
setFormEditingExtraItemValues,
|
||||||
setGridPanelColor,
|
|
||||||
} from './Utils'
|
} from './Utils'
|
||||||
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
import { GridBoxEditorComponent } from './editors/GridBoxEditorComponent'
|
||||||
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
|
import { ImageViewerEditorComponent } from './editors/ImageViewerEditorComponent'
|
||||||
|
|
@ -70,6 +65,7 @@ import SubForms from '../form/SubForms'
|
||||||
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
|
import { ImportDashboard } from '@/components/importManager/ImportDashboard'
|
||||||
import { workflowService } from '@/services/workflow.service'
|
import { workflowService } from '@/services/workflow.service'
|
||||||
import { NotePanel } from '../form/notes/NotePanel'
|
import { NotePanel } from '../form/notes/NotePanel'
|
||||||
|
import { useListFormStateStoring } from './useListFormStateStoring'
|
||||||
|
|
||||||
interface TreeProps {
|
interface TreeProps {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -80,8 +76,6 @@ interface TreeProps {
|
||||||
gridDto?: GridDto
|
gridDto?: GridDto
|
||||||
}
|
}
|
||||||
|
|
||||||
const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)'
|
|
||||||
|
|
||||||
const isTemporaryDxKey = (key: unknown) => typeof key === 'string' && key.startsWith('_DX_KEY_')
|
const isTemporaryDxKey = (key: unknown) => typeof key === 'string' && key.startsWith('_DX_KEY_')
|
||||||
|
|
||||||
const getPersistedInsertedKey = (e: any, keyFieldName?: string) => {
|
const getPersistedInsertedKey = (e: any, keyFieldName?: string) => {
|
||||||
|
|
@ -312,62 +306,12 @@ const Tree = (props: TreeProps) => {
|
||||||
}
|
}
|
||||||
}, [searchParams])
|
}, [searchParams])
|
||||||
|
|
||||||
// StateStoring için storageKey'i memoize et
|
const { customSaveState, customLoadState } = useListFormStateStoring({
|
||||||
const storageKey = useMemo(() => {
|
listFormCode,
|
||||||
return gridDto?.gridOptions.stateStoringDto?.storageKey ?? ''
|
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
|
||||||
}, [gridDto?.gridOptions.stateStoringDto?.storageKey])
|
filterPrefix: 'tree',
|
||||||
|
skipSaveRef: isEditingRef,
|
||||||
const customSaveState = useCallback(
|
})
|
||||||
(state: any) => {
|
|
||||||
if (isEditingRef.current) {
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
|
||||||
return postListFormCustomization({
|
|
||||||
listFormCode: listFormCode,
|
|
||||||
customizationType: ListFormCustomizationTypeEnum.GridState,
|
|
||||||
filterName: `tree-${storageKey}`,
|
|
||||||
customizationData: JSON.stringify(state),
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
setGridPanelColor(statedGridPanelColor)
|
|
||||||
toast.push(
|
|
||||||
<Notification type="success" duration={2000}>
|
|
||||||
{translate('::ListForms.ListForm.GridStateSaved')}
|
|
||||||
</Notification>,
|
|
||||||
{
|
|
||||||
placement: 'bottom-end',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
toast.push(
|
|
||||||
<Notification type="danger" duration={2500}>
|
|
||||||
{translate('::ListForms.ListForm.GridStateSaveError')}
|
|
||||||
</Notification>,
|
|
||||||
{
|
|
||||||
placement: 'bottom-end',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
throw err
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[listFormCode, storageKey, translate],
|
|
||||||
)
|
|
||||||
|
|
||||||
const customLoadState = useCallback(() => {
|
|
||||||
return getListFormCustomization(
|
|
||||||
listFormCode,
|
|
||||||
ListFormCustomizationTypeEnum.GridState,
|
|
||||||
`tree-${storageKey}`,
|
|
||||||
).then((response: any) => {
|
|
||||||
if (response.data?.length > 0) {
|
|
||||||
setGridPanelColor(statedGridPanelColor)
|
|
||||||
return JSON.parse(response.data[0].customizationData)
|
|
||||||
}
|
|
||||||
// Veri yoksa null dön (DevExtreme bunu default state olarak algılar)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}, [listFormCode, storageKey])
|
|
||||||
|
|
||||||
const { filterToolbarData, ...filterData } = useFilters({
|
const { filterToolbarData, ...filterData } = useFilters({
|
||||||
gridDto,
|
gridDto,
|
||||||
|
|
@ -593,7 +537,7 @@ const Tree = (props: TreeProps) => {
|
||||||
typeof colFormat.defaultValue === 'string' &&
|
typeof colFormat.defaultValue === 'string' &&
|
||||||
colFormat.defaultValue === '@AUTONUMBER'
|
colFormat.defaultValue === '@AUTONUMBER'
|
||||||
) {
|
) {
|
||||||
e.data[colFormat.fieldName] = autoNumber
|
e.data[colFormat.fieldName] = autoNumber()
|
||||||
} else {
|
} else {
|
||||||
e.data[colFormat.fieldName] = colFormat.defaultValue
|
e.data[colFormat.fieldName] = colFormat.defaultValue
|
||||||
}
|
}
|
||||||
|
|
@ -631,8 +575,12 @@ const Tree = (props: TreeProps) => {
|
||||||
case 'object':
|
case 'object':
|
||||||
try {
|
try {
|
||||||
e.data[colFormat.fieldName] = JSON.parse(val)
|
e.data[colFormat.fieldName] = JSON.parse(val)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`Error occurred while parsing JSON for field "${colFormat.fieldName}":`,
|
||||||
|
err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
catch {}
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
e.data[colFormat.fieldName] = val
|
e.data[colFormat.fieldName] = val
|
||||||
|
|
@ -983,7 +931,7 @@ const Tree = (props: TreeProps) => {
|
||||||
|
|
||||||
let base: any = null
|
let base: any = null
|
||||||
if (defaultSearchParams.current) {
|
if (defaultSearchParams.current) {
|
||||||
base = JSON.parse(defaultSearchParams.current)
|
base = safeJsonParse(defaultSearchParams.current, null, 'Search filter parse error:')
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseTriplets = extractSearchParamsFields(base)
|
const baseTriplets = extractSearchParamsFields(base)
|
||||||
|
|
@ -1063,7 +1011,7 @@ const Tree = (props: TreeProps) => {
|
||||||
console.error('State load error:', err)
|
console.error('State load error:', err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [gridDto, treeListDataSource, columnData])
|
}, [gridDto, treeListDataSource, columnData, customLoadState])
|
||||||
|
|
||||||
// StateStoring'i devre dışı bırak - manuel kaydetme kullanılacak
|
// StateStoring'i devre dışı bırak - manuel kaydetme kullanılacak
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -1479,7 +1427,9 @@ const Tree = (props: TreeProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch (err){
|
||||||
|
console.error('EditorOptions parse error:', i.dataField, err)
|
||||||
|
}
|
||||||
|
|
||||||
const fieldName = i.dataField.split(':')[0]
|
const fieldName = i.dataField.split(':')[0]
|
||||||
const listFormField = gridDto.columnFormats.find(
|
const listFormField = gridDto.columnFormats.find(
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,22 @@ export function extractSearchParamsFields(filter: any): [string, string, any][]
|
||||||
return filter.flatMap((f) => extractSearchParamsFields(f))
|
return filter.flatMap((f) => extractSearchParamsFields(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function safeJsonParse<T>(value: unknown, fallback: T, errorMessage?: string): T {
|
||||||
|
if (typeof value !== 'string' || value.trim() === '') {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(value) as T
|
||||||
|
} catch (error) {
|
||||||
|
if (errorMessage) {
|
||||||
|
console.error(errorMessage, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sayfaya dinamik olarak css style ekler
|
// sayfaya dinamik olarak css style ekler
|
||||||
export function addCss(css: string) {
|
export function addCss(css: string) {
|
||||||
if (css.startsWith('http') || css.startsWith('/')) {
|
if (css.startsWith('http') || css.startsWith('/')) {
|
||||||
|
|
@ -53,42 +69,47 @@ export function controlStyleCondition(data: any, fieldName: any, style: any) {
|
||||||
// condition sadece data satırlarında yapılır
|
// condition sadece data satırlarında yapılır
|
||||||
if (style.rowType != 'data') return true
|
if (style.rowType != 'data') return true
|
||||||
|
|
||||||
|
const value = data?.[fieldName]
|
||||||
|
const conditionValue = style.conditionValue
|
||||||
|
const stringValue = value == null ? '' : String(value)
|
||||||
|
const stringConditionValue = conditionValue == null ? '' : String(conditionValue)
|
||||||
|
const getConditionValues = () =>
|
||||||
|
safeJsonParse<any[]>(conditionValue, [], 'Style condition parse error:')
|
||||||
|
|
||||||
switch (style.condition) {
|
switch (style.condition) {
|
||||||
case '=':
|
case '=':
|
||||||
return style.conditionValue == data[fieldName]
|
return conditionValue == value
|
||||||
case '<>':
|
case '<>':
|
||||||
return style.conditionValue != data[fieldName]
|
return conditionValue != value
|
||||||
case '<':
|
case '<':
|
||||||
return style.conditionValue > data[fieldName]
|
return conditionValue > value
|
||||||
case '<=':
|
case '<=':
|
||||||
return style.conditionValue >= data[fieldName]
|
return conditionValue >= value
|
||||||
case '>':
|
case '>':
|
||||||
return style.conditionValue < data[fieldName]
|
return conditionValue < value
|
||||||
case '>=':
|
case '>=':
|
||||||
return style.conditionValue <= data[fieldName]
|
return conditionValue <= value
|
||||||
case 'contains':
|
case 'contains':
|
||||||
return data[fieldName].includes(style.conditionValue)
|
return stringValue.includes(stringConditionValue)
|
||||||
case 'endswith':
|
case 'endswith':
|
||||||
return data[fieldName].endsWith(style.conditionValue)
|
return stringValue.endsWith(stringConditionValue)
|
||||||
case 'isblank':
|
case 'isblank':
|
||||||
return data[fieldName] == null
|
return value == null || value === ''
|
||||||
case 'isnotblank':
|
case 'isnotblank':
|
||||||
return data[fieldName] != null
|
return value != null && value !== ''
|
||||||
case 'notcontains':
|
case 'notcontains':
|
||||||
return data[fieldName].indexOf(style.conditionValue) === -1
|
return !stringValue.includes(stringConditionValue)
|
||||||
case 'startswith':
|
case 'startswith':
|
||||||
return data[fieldName].startsWith(style.conditionValue)
|
return stringValue.startsWith(stringConditionValue)
|
||||||
case 'between':
|
case 'between':
|
||||||
return false // TODO:
|
return false // TODO:
|
||||||
case 'anyof': // anyof icin style.conditionValue obje olmalıdır: ['Val1','Val2']
|
case 'anyof': // anyof icin style.conditionValue obje olmalıdır: ['Val1','Val2']
|
||||||
return JSON.parse(style.conditionValue).some(data[fieldName])
|
return getConditionValues().some((el) => el == value)
|
||||||
case 'noneof': // noneof icin style.conditionValue obje olmalıdır: ['Val1','Val2']
|
case 'noneof': // noneof icin style.conditionValue obje olmalıdır: ['Val1','Val2']
|
||||||
return JSON.parse(style.conditionValue).findIndex((el: any) => el == data[fieldName]) < 0
|
return getConditionValues().every((el) => el != value)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pivotFieldConvertDataType(fieldDbType?: DataType): PivotGridDataType {
|
export function pivotFieldConvertDataType(fieldDbType?: DataType): PivotGridDataType {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
import Button from 'devextreme-react/button'
|
|
||||||
import DataGrid from 'devextreme-react/data-grid'
|
|
||||||
import DropDownBox from 'devextreme-react/drop-down-box'
|
|
||||||
import dxDataGrid from 'devextreme/ui/data_grid'
|
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
|
import { SharedGridBoxEditor } from '@/views/shared/SharedGridBoxEditor'
|
||||||
|
|
||||||
const GridBoxEditorComponent = (cellElement: any): ReactElement => {
|
const GridBoxEditorComponent = (cellElement: any): ReactElement => {
|
||||||
const col = cellElement.column
|
const col = cellElement.column
|
||||||
|
|
@ -10,9 +7,6 @@ const GridBoxEditorComponent = (cellElement: any): ReactElement => {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
const options = col.extras.gridBoxOptions
|
const options = col.extras.gridBoxOptions
|
||||||
let gridRef: dxDataGrid<any, any>
|
|
||||||
|
|
||||||
let val = Array.isArray(cellElement.value) ? cellElement.value : [cellElement.value]
|
|
||||||
const lookup = col?.lookup
|
const lookup = col?.lookup
|
||||||
const dataSource: any =
|
const dataSource: any =
|
||||||
typeof lookup?.dataSource === 'function'
|
typeof lookup?.dataSource === 'function'
|
||||||
|
|
@ -20,97 +14,16 @@ const GridBoxEditorComponent = (cellElement: any): ReactElement => {
|
||||||
: (lookup?.dataSource ?? [])
|
: (lookup?.dataSource ?? [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropDownBox
|
<SharedGridBoxEditor
|
||||||
{...col.extras.editorOptions}
|
value={cellElement.value}
|
||||||
dropDownOptions={{ width: options.width, hideOnOutsideClick: true }}
|
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
valueExpr={lookup?.valueExpr}
|
lookup={lookup}
|
||||||
displayExpr={lookup?.displayExpr}
|
options={options}
|
||||||
showClearButton={options?.showClearButton}
|
editorOptions={col.extras.editorOptions}
|
||||||
acceptCustomValue={options?.acceptCustomValue}
|
dropDownOptions={{ width: options?.width, hideOnOutsideClick: true }}
|
||||||
onValueChanged={(e) => {
|
onCommit={(nextValue) => cellElement.setValue(nextValue)}
|
||||||
if (e.value === null) {
|
setInitialValueOnContentReady
|
||||||
val = null
|
/>
|
||||||
cellElement.setValue(val)
|
|
||||||
|
|
||||||
e.component.close()
|
|
||||||
|
|
||||||
if (gridRef) {
|
|
||||||
gridRef.deselectAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onContentReady={(e) => {
|
|
||||||
e.component.option('value', val)
|
|
||||||
}}
|
|
||||||
contentRender={(e) => (
|
|
||||||
<>
|
|
||||||
<DataGrid
|
|
||||||
dataSource={dataSource}
|
|
||||||
filterRow={{
|
|
||||||
visible: options?.filterRowVisible,
|
|
||||||
applyFilter: 'auto',
|
|
||||||
}}
|
|
||||||
columns={options?.columns.map((c: string) => c.toLowerCase())} //AliBaba -> alibaba
|
|
||||||
hoverStateEnabled={true}
|
|
||||||
height={options?.height}
|
|
||||||
selection={{
|
|
||||||
mode: options?.selectionMode,
|
|
||||||
}}
|
|
||||||
onContentReady={(event) => {
|
|
||||||
gridRef = event.component as dxDataGrid<any, any>
|
|
||||||
}}
|
|
||||||
onRowClick={(event) => {
|
|
||||||
if (options?.selectionMode === 'single') {
|
|
||||||
e.component.option('value', event.data.key)
|
|
||||||
cellElement.setValue(event.data.key)
|
|
||||||
e.component.close()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onEditorPrepared={(event) => {
|
|
||||||
const getListValues = async () => {
|
|
||||||
const data = await dataSource?.load()
|
|
||||||
|
|
||||||
const listValues = data.map((a: any) => ({
|
|
||||||
key: a.key,
|
|
||||||
name: a.name,
|
|
||||||
}))
|
|
||||||
|
|
||||||
event.component.option(
|
|
||||||
'selectedRowKeys',
|
|
||||||
listValues.filter((a: any) => val.includes(a.key)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
getListValues()
|
|
||||||
}}
|
|
||||||
></DataGrid>
|
|
||||||
{options?.selectionMode === 'multiple' && (
|
|
||||||
<div className="text-center">
|
|
||||||
<Button
|
|
||||||
text="OK"
|
|
||||||
className="mr-2 min-w-[100px]"
|
|
||||||
onClick={() => {
|
|
||||||
val = gridRef?.getSelectedRowKeys().map((a: any) => a.key)
|
|
||||||
|
|
||||||
e.component.option('value', val)
|
|
||||||
cellElement.setValue(val)
|
|
||||||
|
|
||||||
e.component.close()
|
|
||||||
}}
|
|
||||||
></Button>
|
|
||||||
<Button
|
|
||||||
text="Cancel"
|
|
||||||
className="min-w-[100px]"
|
|
||||||
onClick={() => {
|
|
||||||
e.component.close()
|
|
||||||
}}
|
|
||||||
></Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
></DropDownBox>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { PivotGridRef } from 'devextreme-react/pivot-grid'
|
||||||
import CustomStore from 'devextreme/data/custom_store'
|
import CustomStore from 'devextreme/data/custom_store'
|
||||||
import { MutableRefObject, useCallback } from 'react'
|
import { MutableRefObject, useCallback } from 'react'
|
||||||
import { layoutTypes, ListViewLayoutType } from '@/views/admin/listForm/edit/types'
|
import { layoutTypes, ListViewLayoutType } from '@/views/admin/listForm/edit/types'
|
||||||
import { getLoadOptions, getServiceAddress, setGridPanelColor } from './Utils'
|
import { getLoadOptions, getServiceAddress, safeJsonParse, setGridPanelColor } from './Utils'
|
||||||
import { GridOptionsDto } from '@/proxy/form/models'
|
import { GridOptionsDto } from '@/proxy/form/models'
|
||||||
import { GridColumnData } from './GridColumnData'
|
import { GridColumnData } from './GridColumnData'
|
||||||
import { dynamicFetch } from '@/services/form.service'
|
import { dynamicFetch } from '@/services/form.service'
|
||||||
|
|
@ -72,11 +72,7 @@ const useListFormCustomDataSource = ({
|
||||||
// URL'den sort parametresini al ve loadOptions'a ekle
|
// URL'den sort parametresini al ve loadOptions'a ekle
|
||||||
const urlSort = searchParams?.get('sort')
|
const urlSort = searchParams?.get('sort')
|
||||||
if (urlSort && !loadOptions.sort) {
|
if (urlSort && !loadOptions.sort) {
|
||||||
try {
|
loadOptions.sort = safeJsonParse(urlSort, undefined, 'Sort parse error:')
|
||||||
loadOptions.sort = JSON.parse(urlSort)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Sort parse error:', e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const parameters = getLoadOptions(loadOptions, {
|
const parameters = getLoadOptions(loadOptions, {
|
||||||
|
|
@ -125,9 +121,11 @@ const useListFormCustomDataSource = ({
|
||||||
Object.assign(parameters, chartParameters)
|
Object.assign(parameters, chartParameters)
|
||||||
}
|
}
|
||||||
// 1. Default filter'ı al
|
// 1. Default filter'ı al
|
||||||
const defaultFilter = searchParams?.get('filter')
|
const defaultFilter = safeJsonParse(
|
||||||
? JSON.parse(searchParams.get('filter')!)
|
searchParams?.get('filter'),
|
||||||
: null
|
null,
|
||||||
|
'Search filter parse error:',
|
||||||
|
)
|
||||||
|
|
||||||
let combinedFilter: any = parameters.filter
|
let combinedFilter: any = parameters.filter
|
||||||
|
|
||||||
|
|
@ -312,9 +310,11 @@ const useListFormCustomDataSource = ({
|
||||||
})
|
})
|
||||||
|
|
||||||
// 1. Default filter'ı al
|
// 1. Default filter'ı al
|
||||||
const defaultFilter = searchParams?.get('filter')
|
const defaultFilter = safeJsonParse(
|
||||||
? JSON.parse(searchParams.get('filter')!)
|
searchParams?.get('filter'),
|
||||||
: null
|
null,
|
||||||
|
'Search filter parse error:',
|
||||||
|
)
|
||||||
|
|
||||||
let combinedFilter: any = parameters.filter
|
let combinedFilter: any = parameters.filter
|
||||||
|
|
||||||
|
|
|
||||||
188
ui/src/views/list/useListFormStateStoring.tsx
Normal file
188
ui/src/views/list/useListFormStateStoring.tsx
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
import { Notification, toast } from '@/components/ui'
|
||||||
|
import { ListFormCustomizationTypeEnum } from '@/proxy/form/models'
|
||||||
|
import {
|
||||||
|
getListFormCustomization,
|
||||||
|
postListFormCustomization,
|
||||||
|
} from '@/services/list-form-customization.service'
|
||||||
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
|
import { MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||||
|
import { setGridPanelColor } from './Utils'
|
||||||
|
|
||||||
|
const statedGridPanelColor = 'rgba(50, 200, 200, 0.5)'
|
||||||
|
const defaultSaveDebounceMs = 500
|
||||||
|
|
||||||
|
type PendingSave = {
|
||||||
|
promise: Promise<void>
|
||||||
|
resolve: () => void
|
||||||
|
reject: (reason?: unknown) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
type UseListFormStateStoringParams = {
|
||||||
|
listFormCode: string
|
||||||
|
storageKey?: string | null
|
||||||
|
filterPrefix: 'list' | 'tree' | 'pivot'
|
||||||
|
skipSaveRef?: MutableRefObject<boolean>
|
||||||
|
showSaveToast?: boolean
|
||||||
|
saveDebounceMs?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useListFormStateStoring = ({
|
||||||
|
listFormCode,
|
||||||
|
storageKey,
|
||||||
|
filterPrefix,
|
||||||
|
skipSaveRef,
|
||||||
|
showSaveToast = true,
|
||||||
|
saveDebounceMs = defaultSaveDebounceMs,
|
||||||
|
}: UseListFormStateStoringParams) => {
|
||||||
|
const { translate } = useLocalization()
|
||||||
|
const normalizedStorageKey = useMemo(() => storageKey?.trim() ?? '', [storageKey])
|
||||||
|
const filterName = useMemo(
|
||||||
|
() => (normalizedStorageKey ? `${filterPrefix}-${normalizedStorageKey}` : ''),
|
||||||
|
[filterPrefix, normalizedStorageKey],
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveTimerRef = useRef<ReturnType<typeof setTimeout>>()
|
||||||
|
const pendingStateRef = useRef<any>()
|
||||||
|
const pendingSaveRef = useRef<PendingSave>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (saveTimerRef.current) {
|
||||||
|
clearTimeout(saveTimerRef.current)
|
||||||
|
}
|
||||||
|
pendingSaveRef.current?.resolve()
|
||||||
|
saveTimerRef.current = undefined
|
||||||
|
pendingSaveRef.current = undefined
|
||||||
|
pendingStateRef.current = undefined
|
||||||
|
}
|
||||||
|
}, [filterName, listFormCode])
|
||||||
|
|
||||||
|
const showSaveSuccessToast = useCallback(() => {
|
||||||
|
if (!showSaveToast) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.push(
|
||||||
|
<Notification type="success" duration={2000}>
|
||||||
|
{translate('::ListForms.ListForm.GridStateSaved')}
|
||||||
|
</Notification>,
|
||||||
|
{
|
||||||
|
placement: 'bottom-end',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}, [showSaveToast, translate])
|
||||||
|
|
||||||
|
const showSaveErrorToast = useCallback(() => {
|
||||||
|
if (!showSaveToast) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.push(
|
||||||
|
<Notification type="danger" duration={2500}>
|
||||||
|
{translate('::ListForms.ListForm.GridStateSaveError')}
|
||||||
|
</Notification>,
|
||||||
|
{
|
||||||
|
placement: 'bottom-end',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}, [showSaveToast, translate])
|
||||||
|
|
||||||
|
const customSaveState = useCallback(
|
||||||
|
(state: any) => {
|
||||||
|
if (skipSaveRef?.current || !listFormCode || !filterName) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingStateRef.current = state
|
||||||
|
|
||||||
|
if (!pendingSaveRef.current) {
|
||||||
|
let resolveSave!: () => void
|
||||||
|
let rejectSave!: (reason?: unknown) => void
|
||||||
|
|
||||||
|
const promise = new Promise<void>((resolve, reject) => {
|
||||||
|
resolveSave = resolve
|
||||||
|
rejectSave = reject
|
||||||
|
})
|
||||||
|
|
||||||
|
pendingSaveRef.current = {
|
||||||
|
promise,
|
||||||
|
resolve: resolveSave,
|
||||||
|
reject: rejectSave,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saveTimerRef.current) {
|
||||||
|
clearTimeout(saveTimerRef.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveTimerRef.current = setTimeout(() => {
|
||||||
|
const pendingSave = pendingSaveRef.current
|
||||||
|
const pendingState = pendingStateRef.current
|
||||||
|
|
||||||
|
saveTimerRef.current = undefined
|
||||||
|
pendingSaveRef.current = undefined
|
||||||
|
pendingStateRef.current = undefined
|
||||||
|
|
||||||
|
let customizationData = ''
|
||||||
|
try {
|
||||||
|
customizationData = JSON.stringify(pendingState)
|
||||||
|
} catch (err) {
|
||||||
|
showSaveErrorToast()
|
||||||
|
pendingSave?.reject(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
postListFormCustomization({
|
||||||
|
listFormCode,
|
||||||
|
customizationType: ListFormCustomizationTypeEnum.GridState,
|
||||||
|
filterName,
|
||||||
|
customizationData,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setGridPanelColor(statedGridPanelColor)
|
||||||
|
showSaveSuccessToast()
|
||||||
|
pendingSave?.resolve()
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
showSaveErrorToast()
|
||||||
|
pendingSave?.reject(err)
|
||||||
|
})
|
||||||
|
}, saveDebounceMs)
|
||||||
|
|
||||||
|
return pendingSaveRef.current.promise
|
||||||
|
},
|
||||||
|
[filterName, listFormCode, saveDebounceMs, showSaveErrorToast, showSaveSuccessToast, skipSaveRef],
|
||||||
|
)
|
||||||
|
|
||||||
|
const customLoadState = useCallback(() => {
|
||||||
|
if (!listFormCode || !filterName) {
|
||||||
|
return Promise.resolve(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return getListFormCustomization(
|
||||||
|
listFormCode,
|
||||||
|
ListFormCustomizationTypeEnum.GridState,
|
||||||
|
filterName,
|
||||||
|
).then((response: any) => {
|
||||||
|
const customizationData = response.data?.[0]?.customizationData
|
||||||
|
if (!customizationData) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const state = JSON.parse(customizationData)
|
||||||
|
setGridPanelColor(statedGridPanelColor)
|
||||||
|
return state
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Grid state parse error:', err)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [filterName, listFormCode])
|
||||||
|
|
||||||
|
return {
|
||||||
|
customSaveState,
|
||||||
|
customLoadState,
|
||||||
|
storageKey: normalizedStorageKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
160
ui/src/views/shared/SharedGridBoxEditor.tsx
Normal file
160
ui/src/views/shared/SharedGridBoxEditor.tsx
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
import Button from 'devextreme-react/button'
|
||||||
|
import DataGrid, { DataGridRef } from 'devextreme-react/data-grid'
|
||||||
|
import DropDownBox from 'devextreme-react/drop-down-box'
|
||||||
|
import { ReactElement, useRef } from 'react'
|
||||||
|
import { GridBoxOptionsDto } from '@/proxy/form/models'
|
||||||
|
|
||||||
|
type GridBoxLookup = {
|
||||||
|
valueExpr?: string
|
||||||
|
displayExpr?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharedGridBoxEditorProps = {
|
||||||
|
value: any
|
||||||
|
dataSource: any
|
||||||
|
lookup?: GridBoxLookup
|
||||||
|
options?: GridBoxOptionsDto
|
||||||
|
editorOptions?: any
|
||||||
|
dropDownOptions?: any
|
||||||
|
onValueChanged?: (value: any) => void
|
||||||
|
onCommit?: (value: any) => void
|
||||||
|
setInitialValueOnContentReady?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeValue = (value: any) => (Array.isArray(value) ? value : [value])
|
||||||
|
|
||||||
|
const includesValue = (value: any, itemKey: any) =>
|
||||||
|
Array.isArray(value) ? value.includes(itemKey) : value === itemKey
|
||||||
|
|
||||||
|
const loadDataSourceItems = async (dataSource: any) => {
|
||||||
|
if (typeof dataSource?.load === 'function') {
|
||||||
|
return await dataSource.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(dataSource)) {
|
||||||
|
return dataSource
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const SharedGridBoxEditor = ({
|
||||||
|
value,
|
||||||
|
dataSource,
|
||||||
|
lookup,
|
||||||
|
options,
|
||||||
|
editorOptions,
|
||||||
|
dropDownOptions,
|
||||||
|
onValueChanged,
|
||||||
|
onCommit,
|
||||||
|
setInitialValueOnContentReady,
|
||||||
|
}: SharedGridBoxEditorProps): ReactElement => {
|
||||||
|
const gridRef = useRef<DataGridRef>(null)
|
||||||
|
const currentValueRef = useRef<any>(normalizeValue(value))
|
||||||
|
currentValueRef.current = normalizeValue(value)
|
||||||
|
|
||||||
|
const commitValue = (nextValue: any) => {
|
||||||
|
currentValueRef.current = nextValue
|
||||||
|
onCommit?.(nextValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropDownBox
|
||||||
|
{...editorOptions}
|
||||||
|
value={currentValueRef.current}
|
||||||
|
dataSource={dataSource}
|
||||||
|
valueExpr={lookup?.valueExpr}
|
||||||
|
displayExpr={lookup?.displayExpr}
|
||||||
|
dropDownOptions={dropDownOptions}
|
||||||
|
showClearButton={options?.showClearButton}
|
||||||
|
acceptCustomValue={options?.acceptCustomValue}
|
||||||
|
onContentReady={(e) => {
|
||||||
|
if (setInitialValueOnContentReady) {
|
||||||
|
e.component.option('value', currentValueRef.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
editorOptions?.onContentReady?.(e)
|
||||||
|
}}
|
||||||
|
onValueChanged={(e) => {
|
||||||
|
const nextValue = typeof e.value === 'string' ? e.value.trim() : e.value
|
||||||
|
currentValueRef.current = nextValue
|
||||||
|
onValueChanged?.(nextValue)
|
||||||
|
|
||||||
|
if (e.value === null) {
|
||||||
|
commitValue(null)
|
||||||
|
e.component.close()
|
||||||
|
gridRef.current?.instance().deselectAll()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
contentRender={(dropDown) => (
|
||||||
|
<>
|
||||||
|
<DataGrid
|
||||||
|
ref={gridRef}
|
||||||
|
dataSource={dataSource}
|
||||||
|
key={lookup?.valueExpr}
|
||||||
|
filterRow={{
|
||||||
|
visible: options?.filterRowVisible,
|
||||||
|
applyFilter: 'auto',
|
||||||
|
}}
|
||||||
|
columns={options?.columns?.map((column: string) => column.toLowerCase())}
|
||||||
|
hoverStateEnabled={true}
|
||||||
|
height={options?.height}
|
||||||
|
selection={{
|
||||||
|
mode: options?.selectionMode,
|
||||||
|
}}
|
||||||
|
onRowClick={(event) => {
|
||||||
|
if (options?.selectionMode === 'single') {
|
||||||
|
const nextValue = event.data.key
|
||||||
|
dropDown.component.option('value', nextValue)
|
||||||
|
commitValue(nextValue)
|
||||||
|
dropDown.component.close()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onEditorPrepared={(event) => {
|
||||||
|
const selectCurrentRows = async () => {
|
||||||
|
const data = await loadDataSourceItems(dataSource)
|
||||||
|
const selectedRows = data
|
||||||
|
.map((item: any) => ({
|
||||||
|
key: item.key,
|
||||||
|
name: item.name,
|
||||||
|
}))
|
||||||
|
.filter((item: any) => includesValue(currentValueRef.current, item.key))
|
||||||
|
|
||||||
|
event.component.option('selectedRowKeys', selectedRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
selectCurrentRows()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{options?.selectionMode === 'multiple' && (
|
||||||
|
<div className="text-center">
|
||||||
|
<Button
|
||||||
|
text="OK"
|
||||||
|
className="mr-2 min-w-[100px]"
|
||||||
|
onClick={() => {
|
||||||
|
const selectedValue = gridRef.current
|
||||||
|
?.instance()
|
||||||
|
.getSelectedRowKeys()
|
||||||
|
.map((item: any) => item.key)
|
||||||
|
|
||||||
|
dropDown.component.option('value', selectedValue)
|
||||||
|
commitValue(selectedValue)
|
||||||
|
dropDown.component.close()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
text="Cancel"
|
||||||
|
className="min-w-[100px]"
|
||||||
|
onClick={() => {
|
||||||
|
dropDown.component.close()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SharedGridBoxEditor }
|
||||||
Loading…
Reference in a new issue