Grid performans çalışması

This commit is contained in:
Sedat Öztürk 2026-01-31 00:28:40 +03:00
parent 4eb43bd6de
commit c366b6499c
6 changed files with 157 additions and 70 deletions

View file

@ -394,5 +394,92 @@ public class ListFormSelectAppService : PlatformAppService, IListFormSelectAppSe
return new List<LookupDataDto>();
}
private static string WrapLookupQueryWithInFilter(
string lookupQuery,
string listFormSelectCommand,
string listFormFieldName)
{
if (string.IsNullOrWhiteSpace(lookupQuery))
throw new ArgumentException("LookupQuery boş olamaz.");
if (string.IsNullOrWhiteSpace(listFormSelectCommand))
throw new ArgumentException("SelectCommand boş olamaz.");
if (string.IsNullOrWhiteSpace(listFormFieldName))
throw new ArgumentException("ListFormFieldName boş olamaz.");
return $@"
SELECT *
FROM (
{lookupQuery}
) t
WHERE ""Key"" IN (
SELECT ""{listFormFieldName}""
FROM ""{listFormSelectCommand}""
GROUP BY ""{listFormFieldName}""
)";
}
private static string AddCascadeWhereCondition(
string sql,
string cascadeParentFields,
int paramStartIndex = 0)
{
if (string.IsNullOrWhiteSpace(sql))
throw new ArgumentException("SQL boş olamaz.");
if (string.IsNullOrWhiteSpace(cascadeParentFields))
return sql;
var fields = cascadeParentFields
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(f => f.Trim())
.ToArray();
if (!fields.Any())
return sql;
var conditions = new List<string>();
for (int i = 0; i < fields.Length; i++)
{
var field = fields[i];
// Zaten " ile başlıyorsa dokunma
if (!field.StartsWith("\""))
field = $"\"{field}\"";
conditions.Add($"{field} = @param{paramStartIndex + i}");
}
string whereClause = string.Join(" AND ", conditions);
var upperSql = sql.ToUpperInvariant();
if (upperSql.Contains(" WHERE "))
{
return InsertBeforeClause(sql, "AND " + whereClause);
}
else
{
return InsertBeforeClause(sql, "WHERE " + whereClause);
}
}
private static string InsertBeforeClause(string sql, string insertText)
{
var upperSql = sql.ToUpperInvariant();
string[] clauses = { " GROUP BY ", " ORDER BY ", " HAVING " };
foreach (var clause in clauses)
{
int index = upperSql.IndexOf(clause);
if (index > -1)
{
return sql.Insert(index, " " + insertText + " ");
}
}
return sql + " " + insertText;
}
}

View file

@ -1826,7 +1826,7 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency
new() {
ListFormCode = listForm.ListFormCode,
CultureName = LanguageCodes.En,
SourceDbType = DbType.Guid,
SourceDbType = DbType.String,
FieldName = "JobPositionId",
CaptionName = "App.Listform.ListformField.JobPositionId",
Width = 200,
@ -1948,9 +1948,9 @@ public class ListFormSeeder_Hr : IDataSeedContributor, ITransientDependency
DisplayExpr = "name",
ValueExpr = "key",
LookupQuery = JsonSerializer.Serialize(new LookupDataDto[] {
new () { Key= "Hours", Name= "Hours" },
new () { Key= "Weeks", Name= "Weeks" },
new () { Key= "Months", Name= "Months" }
new () { Key= "Hourly", Name= "Hourly" },
new () { Key= "Weekly", Name= "Weekly" },
new () { Key= "Monthly", Name= "Monthly" }
}),
}),
ValidationRuleJson = DefaultValidationRuleRequiredJson,

View file

@ -2527,7 +2527,7 @@
"Url": null,
"Icon": "FcServices",
"RequiredPermissionName": null,
"IsDisabled": false,
"IsDisabled": true,
"ShortName": "Mrp"
},
{

View file

@ -68,7 +68,7 @@ const FormButtons = (props: {
const navigate = useNavigate()
const { translate } = useLocalization()
const layout = layoutTypes.grid
const { toolbarData } = useToolbar({
gridDto,
@ -113,18 +113,25 @@ const FormButtons = (props: {
}
}
const toolbarExcludedNames = ['deleteAllRecords', 'deleteSelectedRecords', 'refreshButton']
const hasVisibleToolbarButtons = toolbarData?.some(
(item) => item.widget == 'dxButton' && !toolbarExcludedNames.includes(item.name!),
)
const commandExcludedNames = ['edit', 'delete']
const hasVisibleCommandButtons = commandColumnData?.buttons?.some(
(item) => typeof item !== 'string' && !commandExcludedNames.includes(item.name!),
)
return (
<>
<div className="flex flex-row items-center gap-1">
{toolbarData
?.filter(
(item) =>
item.widget == 'dxButton' &&
item.name != 'deleteAllRecords' &&
item.name != 'deleteSelectedRecords',
(item) => item.widget == 'dxButton' && !toolbarExcludedNames.includes(item.name!),
)
.map((item, i) => {
const IconComp = navigationIcon[item.options?.icon] // React bileşeni olabilir ya da undefined
const IconComp = navigationIcon[item.options?.icon]
const hasValidIcon =
IconComp &&
(typeof IconComp === 'function' ||
@ -135,24 +142,21 @@ const FormButtons = (props: {
key={'toolbarButton-' + i}
variant="default"
size="xs"
icon={
hasValidIcon ? <IconComp className="text-gray-400" /> : null // 🔒 güvenli render
}
icon={hasValidIcon ? <IconComp className="text-gray-400" /> : null}
onClick={item.options?.onClick}
>
{item.options?.text}
</Button>
)
})}
{!!toolbarData?.filter(
(item) =>
item.widget == 'dxButton' &&
item.name != 'deleteAllRecords' &&
item.name != 'deleteSelectedRecords',
).length && <Badge innerClass="bg-blue-500" />}
{hasVisibleToolbarButtons && <Badge innerClass="bg-blue-500" />}
{commandColumnData?.buttons
?.filter((item) => typeof item !== 'string')
.map((item, i) => {
?.filter((item) => typeof item !== 'string' && !commandExcludedNames.includes(item.name!))
.map((item: any, i) => {
return (
<Button
key={'commandColumnButton-' + i}
@ -171,9 +175,8 @@ const FormButtons = (props: {
</Button>
)
})}
{!!commandColumnData?.buttons?.filter((item) => typeof item !== 'string').length && (
<Badge innerClass="bg-blue-500" />
)}
{hasVisibleCommandButtons && <Badge innerClass="bg-blue-500" />}
{!isSubForm && (
<Button

View file

@ -426,31 +426,29 @@ const Grid = (props: GridProps) => {
}
// Tüm cascade fieldlerin disabled durumlarını güncelle
setTimeout(() => {
const popup = grid.option('editing.popup')
if (popup) {
const formInstance = grid.option('editing.form') as any
if (formInstance && formInstance.getEditor) {
gridDto.columnFormats.forEach((col) => {
if (col.lookupDto?.cascadeParentFields) {
const colParentFields = col.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const shouldDisable = colParentFields.some((pf: string) => !rowData[pf])
const popup = grid.option('editing.popup')
if (popup) {
const formInstance = grid.option('editing.form') as any
if (formInstance && formInstance.getEditor) {
gridDto.columnFormats.forEach((col) => {
if (col.lookupDto?.cascadeParentFields) {
const colParentFields = col.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const shouldDisable = colParentFields.some((pf: string) => !rowData[pf])
try {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
editorInstance.option('disabled', shouldDisable)
}
} catch (err) {
console.debug('Cascade disabled update skipped for', col.fieldName, err)
try {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
editorInstance.option('disabled', shouldDisable)
}
} catch (err) {
console.debug('Cascade disabled update skipped for', col.fieldName, err)
}
})
}
}
})
}
}, 50)
}
}
// İlk açılışta disabled durumunu kontrol et
@ -931,6 +929,9 @@ const Grid = (props: GridProps) => {
onEditingStart={onEditingStart}
onDataErrorOccurred={onDataErrorOccurred}
onExporting={onExporting}
onEditCanceling={() => {
setMode('view')
}}
onEditCanceled={() => {
isEditingRef.current = false
setMode('view')

View file

@ -445,31 +445,29 @@ const Tree = (props: TreeProps) => {
}
// Tüm cascade fieldlerin disabled durumlarını güncelle
setTimeout(() => {
const popup = grid.option('editing.popup')
if (popup) {
const formInstance = grid.option('editing.form') as any
if (formInstance && formInstance.getEditor) {
gridDto.columnFormats.forEach((col) => {
if (col.lookupDto?.cascadeParentFields) {
const colParentFields = col.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const shouldDisable = colParentFields.some((pf: string) => !rowData[pf])
const popup = grid.option('editing.popup')
if (popup) {
const formInstance = grid.option('editing.form') as any
if (formInstance && formInstance.getEditor) {
gridDto.columnFormats.forEach((col) => {
if (col.lookupDto?.cascadeParentFields) {
const colParentFields = col.lookupDto.cascadeParentFields
.split(',')
.map((f: string) => f.trim())
const shouldDisable = colParentFields.some((pf: string) => !rowData[pf])
try {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
editorInstance.option('disabled', shouldDisable)
}
} catch (err) {
console.debug('Cascade disabled update skipped for', col.fieldName, err)
try {
const editorInstance = formInstance.getEditor(col.fieldName!)
if (editorInstance) {
editorInstance.option('disabled', shouldDisable)
}
} catch (err) {
console.debug('Cascade disabled update skipped for', col.fieldName, err)
}
})
}
}
})
}
}, 50)
}
}
// İlk açılışta disabled durumunu kontrol et
@ -1163,9 +1161,7 @@ const Tree = (props: TreeProps) => {
<ColumnFixing
enabled={gridDto.gridOptions.columnOptionDto?.columnFixingEnabled}
></ColumnFixing>
<Scrolling
mode={gridDto.gridOptions.pagerOptionDto?.scrollingMode}
></Scrolling>
<Scrolling mode={gridDto.gridOptions.pagerOptionDto?.scrollingMode}></Scrolling>
<LoadPanel
enabled={gridDto.gridOptions.pagerOptionDto?.loadPanelEnabled}
text={gridDto.gridOptions.pagerOptionDto?.loadPanelText}