2025-10-22 14:58:27 +00:00
|
|
|
|
// src/.../useLookupDataSource.ts
|
2025-09-21 20:05:13 +00:00
|
|
|
|
import { dynamicFetch } from '@/services/form.service'
|
|
|
|
|
|
import { UiLookupDataSourceTypeEnum } from '@/proxy/form/models'
|
|
|
|
|
|
import CustomStore from 'devextreme/data/custom_store'
|
|
|
|
|
|
import { useCallback } from 'react'
|
|
|
|
|
|
|
2025-10-22 14:58:27 +00:00
|
|
|
|
/** Module-scope cache: key -> Promise<array>
|
|
|
|
|
|
* - Aynı anda açılan editörler tek ağ çağrısı üzerinden paylaşır
|
|
|
|
|
|
* - Hata olursa cache temizlenir (yeniden denenebilir)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const __lookupCache = new Map<string, Promise<any[]>>()
|
|
|
|
|
|
|
|
|
|
|
|
const cachedLoader = (key: string, loader: () => Promise<any[]>) => {
|
|
|
|
|
|
if (__lookupCache.has(key)) return __lookupCache.get(key)!
|
|
|
|
|
|
const p = Promise.resolve()
|
|
|
|
|
|
.then(() => loader())
|
|
|
|
|
|
.then((res) => res ?? [])
|
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
|
__lookupCache.delete(key)
|
|
|
|
|
|
throw err
|
|
|
|
|
|
})
|
|
|
|
|
|
__lookupCache.set(key, p)
|
|
|
|
|
|
return p
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-21 20:05:13 +00:00
|
|
|
|
const createLookupStaticDataSource = (
|
|
|
|
|
|
load: () => any,
|
|
|
|
|
|
filter: any = null,
|
2025-10-22 14:58:27 +00:00
|
|
|
|
key: any = 'static',
|
2025-09-21 20:05:13 +00:00
|
|
|
|
sort: any = 'name',
|
|
|
|
|
|
) => ({
|
|
|
|
|
|
store: new CustomStore({
|
|
|
|
|
|
key,
|
|
|
|
|
|
loadMode: 'raw',
|
2025-10-22 14:58:27 +00:00
|
|
|
|
load: async () => {
|
|
|
|
|
|
const cacheKey = `static:${key}`
|
|
|
|
|
|
return cachedLoader(cacheKey, async () => {
|
|
|
|
|
|
const res = await Promise.resolve(load())
|
|
|
|
|
|
return Array.isArray(res) ? res : [res]
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
2025-09-21 20:05:13 +00:00
|
|
|
|
}),
|
|
|
|
|
|
sort,
|
|
|
|
|
|
filter,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const createLookupQueryDataSource = (
|
|
|
|
|
|
listFormCode?: string,
|
|
|
|
|
|
listFormFieldName?: string,
|
|
|
|
|
|
filters?: any[],
|
|
|
|
|
|
isSubForm?: boolean,
|
|
|
|
|
|
) => {
|
|
|
|
|
|
return new CustomStore({
|
|
|
|
|
|
loadMode: 'raw',
|
|
|
|
|
|
load: async () => {
|
|
|
|
|
|
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-22 14:58:27 +00:00
|
|
|
|
|
|
|
|
|
|
const cacheKey = `query:${listFormCode}:${listFormFieldName}:${JSON.stringify(filters ?? null)}`
|
|
|
|
|
|
return cachedLoader(cacheKey, async () => {
|
2025-09-21 20:05:13 +00:00
|
|
|
|
const response = await dynamicFetch('list-form-select/lookup', 'POST', null, {
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
listFormFieldName,
|
|
|
|
|
|
filters,
|
|
|
|
|
|
})
|
2025-10-22 14:58:27 +00:00
|
|
|
|
const data = response?.data ?? []
|
|
|
|
|
|
return (Array.isArray(data) ? data : [data]).map((a: any) => ({
|
2025-09-21 20:05:13 +00:00
|
|
|
|
key: a.Key,
|
|
|
|
|
|
name: a.Name,
|
|
|
|
|
|
group: a.Group,
|
|
|
|
|
|
}))
|
2025-10-22 14:58:27 +00:00
|
|
|
|
}).catch(() => null as any)
|
2025-09-21 20:05:13 +00:00
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const createLookupApiDataSource = (
|
|
|
|
|
|
listFormCode?: string,
|
|
|
|
|
|
lookupQuery?: string,
|
|
|
|
|
|
filters?: any[],
|
|
|
|
|
|
keyName?: string,
|
|
|
|
|
|
isSubForm?: boolean,
|
|
|
|
|
|
) => {
|
|
|
|
|
|
return new CustomStore({
|
|
|
|
|
|
key: keyName,
|
|
|
|
|
|
loadMode: 'raw',
|
|
|
|
|
|
load: async () => {
|
|
|
|
|
|
if (!isSubForm && listFormCode && !window.location.pathname.includes(listFormCode)) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!lookupQuery) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-22 14:58:27 +00:00
|
|
|
|
const parts = lookupQuery.split(';')
|
|
|
|
|
|
const [
|
|
|
|
|
|
method = 'GET',
|
|
|
|
|
|
url = '',
|
|
|
|
|
|
bodyTemplate = '',
|
|
|
|
|
|
keySelector = 'a=>a.id',
|
|
|
|
|
|
nameSelector = 'a=>a.name',
|
|
|
|
|
|
groupSelector = 'a=>a.group',
|
|
|
|
|
|
] = parts
|
2025-09-21 20:05:13 +00:00
|
|
|
|
|
2025-10-22 14:58:27 +00:00
|
|
|
|
let resolvedBody = bodyTemplate ?? ''
|
2025-09-21 20:05:13 +00:00
|
|
|
|
if (filters?.length) {
|
|
|
|
|
|
for (let i = 0; i < filters.length; i++) {
|
2025-10-22 14:58:27 +00:00
|
|
|
|
resolvedBody = resolvedBody.replace(new RegExp(`@param${i}`, 'g'), String(filters[i]))
|
2025-09-21 20:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-22 14:58:27 +00:00
|
|
|
|
const cacheKey = `api:${lookupQuery}:${JSON.stringify(filters ?? null)}`
|
|
|
|
|
|
return cachedLoader(cacheKey, async () => {
|
|
|
|
|
|
const response = await dynamicFetch(url, method, null, resolvedBody)
|
2025-09-21 20:05:13 +00:00
|
|
|
|
let { data } = response
|
2025-10-22 14:58:27 +00:00
|
|
|
|
if (!data) return []
|
|
|
|
|
|
if (!Array.isArray(data)) data = [data]
|
2025-09-21 20:05:13 +00:00
|
|
|
|
return data.map((a: any) => ({
|
|
|
|
|
|
key: eval(keySelector),
|
|
|
|
|
|
name: eval(nameSelector),
|
|
|
|
|
|
group: eval(groupSelector),
|
|
|
|
|
|
}))
|
2025-10-22 14:58:27 +00:00
|
|
|
|
}).catch(() => [] as any)
|
2025-09-21 20:05:13 +00:00
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const useLookupDataSource = ({
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
isSubForm,
|
|
|
|
|
|
}: {
|
|
|
|
|
|
listFormCode: string
|
|
|
|
|
|
isSubForm?: boolean
|
|
|
|
|
|
}) => {
|
|
|
|
|
|
const getLookupDataSource = useCallback(
|
2025-10-22 14:58:27 +00:00
|
|
|
|
(options: any, colData: any, data: any) => {
|
2025-09-21 20:05:13 +00:00
|
|
|
|
const { lookupDto } = colData
|
2025-10-22 14:58:27 +00:00
|
|
|
|
const filters: any[] = []
|
|
|
|
|
|
|
|
|
|
|
|
// Eğer cascadeParentFields varsa ve row verisi (data) yoksa boş döneriz
|
2025-10-06 22:16:21 +00:00
|
|
|
|
if (lookupDto.cascadeParentFields && !data) {
|
2025-10-22 14:58:27 +00:00
|
|
|
|
return { store: [] }
|
2025-10-06 22:16:21 +00:00
|
|
|
|
}
|
2025-09-21 20:05:13 +00:00
|
|
|
|
if (lookupDto.cascadeParentFields) {
|
|
|
|
|
|
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
|
|
|
|
|
|
filters.push([
|
|
|
|
|
|
lookupDto?.cascadeRelationField,
|
|
|
|
|
|
lookupDto?.cascadeFilterOperator,
|
|
|
|
|
|
options?.data[lookupDto?.cascadeParentField],
|
|
|
|
|
|
])
|
|
|
|
|
|
} else {
|
|
|
|
|
|
for (const cascadeParentField of lookupDto.cascadeParentFields.split(',')) {
|
2025-10-02 07:47:14 +00:00
|
|
|
|
filters.push(data?.[cascadeParentField] ?? null)
|
2025-09-21 20:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.StaticData) {
|
2025-10-22 14:58:27 +00:00
|
|
|
|
const staticKey = `static:${listFormCode}:${colData.fieldName}`
|
2025-09-21 20:05:13 +00:00
|
|
|
|
return createLookupStaticDataSource(
|
|
|
|
|
|
() => JSON.parse(lookupDto?.lookupQuery),
|
|
|
|
|
|
filters.length ? filters : null,
|
2025-10-22 14:58:27 +00:00
|
|
|
|
staticKey,
|
2025-09-21 20:05:13 +00:00
|
|
|
|
)
|
|
|
|
|
|
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.Query) {
|
|
|
|
|
|
return createLookupQueryDataSource(listFormCode, colData.fieldName, filters, isSubForm)
|
|
|
|
|
|
} else if (lookupDto.dataSourceType == UiLookupDataSourceTypeEnum.WebService) {
|
|
|
|
|
|
return createLookupApiDataSource(
|
|
|
|
|
|
listFormCode,
|
|
|
|
|
|
lookupDto?.lookupQuery,
|
|
|
|
|
|
filters,
|
|
|
|
|
|
colData.lookupDto?.valueExpr?.toLowerCase(),
|
|
|
|
|
|
isSubForm,
|
|
|
|
|
|
)
|
|
|
|
|
|
} else {
|
2025-10-22 14:58:27 +00:00
|
|
|
|
return { store: [] }
|
2025-09-21 20:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
[listFormCode, isSubForm],
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return { getLookupDataSource }
|
|
|
|
|
|
}
|
2025-10-22 14:58:27 +00:00
|
|
|
|
|
|
|
|
|
|
/** Opsiyonel: dışarıdan cache temizlemek istersen export et */
|
|
|
|
|
|
export const clearLookupCache = (key?: string) => {
|
|
|
|
|
|
if (!key) __lookupCache.clear()
|
|
|
|
|
|
else __lookupCache.delete(key)
|
|
|
|
|
|
}
|