Add Contact standard object, related lists, meilisearch, pagination, search, AI assistant

This commit is contained in:
Francisco Gaona
2026-01-16 18:01:26 +01:00
parent 51c82d3d95
commit 20fc90a3fb
62 changed files with 6613 additions and 278 deletions

View File

@@ -50,7 +50,9 @@ export const useFields = () => {
step: fieldDef.step,
accept: fieldDef.accept,
relationObject: fieldDef.relationObject,
relationObjects: fieldDef.relationObjects,
relationDisplayField: fieldDef.relationDisplayField,
relationTypeField: fieldDef.relationTypeField,
// Formatting
format: fieldDef.format,
@@ -76,7 +78,8 @@ export const useFields = () => {
objectApiName: objectDef.apiName,
mode: 'list' as ViewMode,
fields,
pageSize: 25,
pageSize: 10,
maxFrontendRecords: 500,
searchable: true,
filterable: true,
exportable: true,
@@ -97,6 +100,7 @@ export const useFields = () => {
objectApiName: objectDef.apiName,
mode: 'detail' as ViewMode,
fields,
relatedLists: objectDef.relatedLists || [],
...customConfig,
}
}
@@ -180,6 +184,7 @@ export const useViewState = <T extends { id?: string }>(
apiEndpoint: string
) => {
const records = ref<T[]>([])
const totalCount = ref(0)
const currentRecord = ref<T | null>(null)
const currentView = ref<'list' | 'detail' | 'edit'>('list')
const loading = ref(false)
@@ -188,13 +193,51 @@ export const useViewState = <T extends { id?: string }>(
const { api } = useApi()
const fetchRecords = async (params?: Record<string, any>) => {
const normalizeListResponse = (response: any) => {
const payload: { data: T[]; totalCount: number; page?: number; pageSize?: number } = {
data: [],
totalCount: 0,
}
if (Array.isArray(response)) {
payload.data = response
payload.totalCount = response.length
return payload
}
if (response && typeof response === 'object') {
if (Array.isArray(response.data)) {
payload.data = response.data
} else if (Array.isArray((response as any).records)) {
payload.data = (response as any).records
} else if (Array.isArray((response as any).results)) {
payload.data = (response as any).results
}
payload.totalCount =
response.totalCount ??
response.total ??
response.count ??
payload.data.length ??
0
payload.page = response.page
payload.pageSize = response.pageSize
}
return payload
}
const fetchRecords = async (params?: Record<string, any>, options?: { append?: boolean }) => {
loading.value = true
error.value = null
try {
const response = await api.get(apiEndpoint, { params })
// Handle response - data might be directly in response or in response.data
records.value = response.data || response || []
const normalized = normalizeListResponse(response)
totalCount.value = normalized.totalCount ?? normalized.data.length ?? 0
records.value = options?.append
? [...records.value, ...normalized.data]
: normalized.data
return normalized
} catch (e: any) {
error.value = e.message
console.error('Failed to fetch records:', e)
@@ -227,6 +270,7 @@ export const useViewState = <T extends { id?: string }>(
// Handle response - it might be the data directly or wrapped in { data: ... }
const recordData = response.data || response
records.value.push(recordData)
totalCount.value += 1
currentRecord.value = recordData
return recordData
} catch (e: any) {
@@ -269,6 +313,7 @@ export const useViewState = <T extends { id?: string }>(
try {
await api.delete(`${apiEndpoint}/${id}`)
records.value = records.value.filter(r => r.id !== id)
totalCount.value = Math.max(0, totalCount.value - 1)
if (currentRecord.value?.id === id) {
currentRecord.value = null
}
@@ -285,8 +330,25 @@ export const useViewState = <T extends { id?: string }>(
loading.value = true
error.value = null
try {
const useBulkEndpoint = apiEndpoint.includes('/runtime/objects/')
if (useBulkEndpoint) {
const response = await api.post(`${apiEndpoint}/bulk-delete`, { ids })
const deletedIds = Array.isArray(response?.deletedIds) ? response.deletedIds : ids
records.value = records.value.filter(r => !deletedIds.includes(r.id!))
totalCount.value = Math.max(0, totalCount.value - deletedIds.length)
return {
deletedIds,
deniedIds: Array.isArray(response?.deniedIds) ? response.deniedIds : [],
}
}
await Promise.all(ids.map(id => api.delete(`${apiEndpoint}/${id}`)))
records.value = records.value.filter(r => !ids.includes(r.id!))
totalCount.value = Math.max(0, totalCount.value - ids.length)
return {
deletedIds: ids,
deniedIds: [],
}
} catch (e: any) {
error.value = e.message
console.error('Failed to delete records:', e)
@@ -324,6 +386,7 @@ export const useViewState = <T extends { id?: string }>(
return {
// State
records,
totalCount,
currentRecord,
currentView,
loading,