Add Contact standard object, related lists, meilisearch, pagination, search, AI assistant
This commit is contained in:
@@ -28,22 +28,44 @@
|
||||
</div>
|
||||
|
||||
<!-- Available Fields Sidebar -->
|
||||
<div class="w-80 border-l bg-white dark:bg-slate-950 p-4 overflow-auto">
|
||||
<h3 class="text-lg font-semibold mb-4">Available Fields</h3>
|
||||
<p class="text-xs text-muted-foreground mb-4">Click or drag to add field to grid</p>
|
||||
<div class="space-y-2" id="sidebar-fields">
|
||||
<div
|
||||
v-for="field in availableFields"
|
||||
:key="field.id"
|
||||
class="p-3 border rounded cursor-move bg-white dark:bg-slate-900 hover:border-primary hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors"
|
||||
:data-field-id="field.id"
|
||||
draggable="true"
|
||||
@dragstart="handleDragStart($event, field)"
|
||||
@click="addFieldToGrid(field)"
|
||||
>
|
||||
<div class="font-medium text-sm">{{ field.label }}</div>
|
||||
<div class="text-xs text-muted-foreground">{{ field.apiName }}</div>
|
||||
<div class="text-xs text-muted-foreground">Type: {{ field.type }}</div>
|
||||
<div class="w-80 border-l bg-white dark:bg-slate-950 p-4 overflow-auto space-y-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold mb-4">Available Fields</h3>
|
||||
<p class="text-xs text-muted-foreground mb-4">Click or drag to add field to grid</p>
|
||||
<div class="space-y-2" id="sidebar-fields">
|
||||
<div
|
||||
v-for="field in availableFields"
|
||||
:key="field.id"
|
||||
class="p-3 border rounded cursor-move bg-white dark:bg-slate-900 hover:border-primary hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors"
|
||||
:data-field-id="field.id"
|
||||
draggable="true"
|
||||
@dragstart="handleDragStart($event, field)"
|
||||
@click="addFieldToGrid(field)"
|
||||
>
|
||||
<div class="font-medium text-sm">{{ field.label }}</div>
|
||||
<div class="text-xs text-muted-foreground">{{ field.apiName }}</div>
|
||||
<div class="text-xs text-muted-foreground">Type: {{ field.type }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="relatedLists.length > 0">
|
||||
<h3 class="text-lg font-semibold mb-2">Related Lists</h3>
|
||||
<p class="text-xs text-muted-foreground mb-4">Select related lists to show on detail views</p>
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
v-for="list in relatedLists"
|
||||
:key="list.relationName"
|
||||
class="flex items-center gap-2 text-sm"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="h-4 w-4"
|
||||
:value="list.relationName"
|
||||
v-model="selectedRelatedLists"
|
||||
/>
|
||||
<span>{{ list.title }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,26 +74,29 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
|
||||
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
|
||||
import { GridStack } from 'gridstack'
|
||||
import 'gridstack/dist/gridstack.min.css'
|
||||
import type { FieldLayoutItem } from '~/types/page-layout'
|
||||
import type { FieldConfig } from '~/types/field-types'
|
||||
import type { FieldConfig, RelatedListConfig } from '~/types/field-types'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
const props = defineProps<{
|
||||
fields: FieldConfig[]
|
||||
relatedLists?: RelatedListConfig[]
|
||||
initialLayout?: FieldLayoutItem[]
|
||||
initialRelatedLists?: string[]
|
||||
layoutName?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
save: [layout: FieldLayoutItem[]]
|
||||
save: [layout: { fields: FieldLayoutItem[]; relatedLists: string[] }]
|
||||
}>()
|
||||
|
||||
const gridContainer = ref<HTMLElement | null>(null)
|
||||
let grid: GridStack | null = null
|
||||
const gridItems = ref<Map<string, any>>(new Map())
|
||||
const selectedRelatedLists = ref<string[]>(props.initialRelatedLists || [])
|
||||
|
||||
// Fields that are already on the grid
|
||||
const placedFieldIds = ref<Set<string>>(new Set())
|
||||
@@ -81,6 +106,10 @@ const availableFields = computed(() => {
|
||||
return props.fields.filter(field => !placedFieldIds.value.has(field.id))
|
||||
})
|
||||
|
||||
const relatedLists = computed(() => {
|
||||
return props.relatedLists || []
|
||||
})
|
||||
|
||||
const initGrid = () => {
|
||||
if (!gridContainer.value) return
|
||||
|
||||
@@ -278,7 +307,10 @@ const handleSave = () => {
|
||||
}
|
||||
})
|
||||
|
||||
emit('save', layout)
|
||||
emit('save', {
|
||||
fields: layout,
|
||||
relatedLists: selectedRelatedLists.value,
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@@ -295,6 +327,13 @@ onBeforeUnmount(() => {
|
||||
watch(() => props.fields, () => {
|
||||
updatePlacedFields()
|
||||
}, { deep: true })
|
||||
|
||||
watch(
|
||||
() => props.initialRelatedLists,
|
||||
(value) => {
|
||||
selectedRelatedLists.value = value ? [...value] : []
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user