263 lines
9.2 KiB
Vue
263 lines
9.2 KiB
Vue
<template>
|
|
<div class="space-y-6">
|
|
<div v-if="loading" class="text-center py-8">Loading access settings...</div>
|
|
|
|
<div v-else class="space-y-6">
|
|
<!-- Global Access Model -->
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Global Access Model</CardTitle>
|
|
<CardDescription>
|
|
Define the default access control model for this object
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent class="space-y-4">
|
|
<div class="space-y-2">
|
|
<Label>Access Model</Label>
|
|
<Select v-model="accessModel">
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select access model" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="public">Public - Anyone can access</SelectItem>
|
|
<SelectItem value="owner">Owner Only - Only record owner can access</SelectItem>
|
|
<SelectItem value="mixed">Mixed - Owner plus role/share-based access</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<p class="text-sm text-muted-foreground">
|
|
<span v-if="accessModel === 'public'">
|
|
All users can access records by default
|
|
</span>
|
|
<span v-else-if="accessModel === 'owner'">
|
|
Only the record owner can access records
|
|
</span>
|
|
<span v-else-if="accessModel === 'mixed'">
|
|
Record owner has access, plus role-based and sharing rules apply
|
|
</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<Label>Owner Field</Label>
|
|
<Input v-model="ownerField" placeholder="ownerId" />
|
|
<p class="text-sm text-muted-foreground">
|
|
The field name that stores the record owner's ID
|
|
</p>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
<Label>Public Permissions</Label>
|
|
<div class="space-y-2">
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="public-read"
|
|
v-model:checked="publicRead"
|
|
/>
|
|
<Label for="public-read" class="cursor-pointer font-normal">Public Read</Label>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="public-create"
|
|
v-model:checked="publicCreate"
|
|
/>
|
|
<Label for="public-create" class="cursor-pointer font-normal">Public Create</Label>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="public-update"
|
|
v-model:checked="publicUpdate"
|
|
/>
|
|
<Label for="public-update" class="cursor-pointer font-normal">Public Update</Label>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="public-delete"
|
|
v-model:checked="publicDelete"
|
|
/>
|
|
<Label for="public-delete" class="cursor-pointer font-normal">Public Delete</Label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<!-- Field-Level Permissions -->
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Field-Level Permissions</CardTitle>
|
|
<CardDescription>
|
|
Set default read/write permissions for individual fields
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div class="space-y-2">
|
|
<div
|
|
v-for="field in fields"
|
|
:key="field.apiName"
|
|
class="flex items-center justify-between p-3 border rounded-lg"
|
|
>
|
|
<div class="flex-1">
|
|
<div class="font-medium">{{ field.label }}</div>
|
|
<div class="text-sm text-muted-foreground">{{ field.apiName }}</div>
|
|
</div>
|
|
<div class="flex items-center gap-4">
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
:id="`${field.apiName}-read`"
|
|
:checked="getFieldPermission(field.apiName, 'read')"
|
|
@update:checked="(val) => setFieldPermission(field.apiName, 'read', val)"
|
|
/>
|
|
<Label :for="`${field.apiName}-read`" class="cursor-pointer">Read</Label>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<Checkbox
|
|
:id="`${field.apiName}-write`"
|
|
:checked="getFieldPermission(field.apiName, 'write')"
|
|
@update:checked="(val) => setFieldPermission(field.apiName, 'write', val)"
|
|
/>
|
|
<Label :for="`${field.apiName}-write`" class="cursor-pointer">Write</Label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<!-- Save Button -->
|
|
<div class="flex justify-end">
|
|
<Button @click="saveChanges" :disabled="saving">
|
|
{{ saving ? 'Saving...' : 'Save Changes' }}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Label } from '@/components/ui/label'
|
|
import { Checkbox } from '@/components/ui/checkbox'
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
|
|
|
interface Props {
|
|
objectApiName: string
|
|
fields: any[]
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
const emit = defineEmits(['updated'])
|
|
|
|
const { api } = useApi()
|
|
const { toast } = useToast()
|
|
|
|
const loading = ref(true)
|
|
const saving = ref(false)
|
|
|
|
const accessModel = ref<string>('owner')
|
|
const publicRead = ref<boolean>(false)
|
|
const publicCreate = ref<boolean>(false)
|
|
const publicUpdate = ref<boolean>(false)
|
|
const publicDelete = ref<boolean>(false)
|
|
const ownerField = ref<string>('ownerId')
|
|
|
|
const fieldPermissions = ref<Record<string, { defaultReadable: boolean; defaultWritable: boolean }>>({})
|
|
|
|
const fetchAccessConfig = async () => {
|
|
try {
|
|
loading.value = true
|
|
const data = await api.get(`/setup/objects/${props.objectApiName}/access`)
|
|
|
|
accessModel.value = data.accessModel || 'owner'
|
|
publicRead.value = Boolean(data.publicRead)
|
|
publicCreate.value = Boolean(data.publicCreate)
|
|
publicUpdate.value = Boolean(data.publicUpdate)
|
|
publicDelete.value = Boolean(data.publicDelete)
|
|
ownerField.value = data.ownerField || 'ownerId'
|
|
|
|
// Initialize field permissions from field definitions
|
|
fieldPermissions.value = {}
|
|
if (data.fields && data.fields.length > 0) {
|
|
data.fields.forEach((field: any) => {
|
|
fieldPermissions.value[field.apiName] = {
|
|
defaultReadable: Boolean(field.defaultReadable ?? true),
|
|
defaultWritable: Boolean(field.defaultWritable ?? true),
|
|
}
|
|
})
|
|
} else {
|
|
// Initialize all fields with default permissions
|
|
props.fields.forEach((field) => {
|
|
fieldPermissions.value[field.apiName] = {
|
|
defaultReadable: true,
|
|
defaultWritable: true,
|
|
}
|
|
})
|
|
}
|
|
} catch (e: any) {
|
|
console.error('Error fetching access config:', e)
|
|
toast.error('Failed to load access settings')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const getFieldPermission = (fieldKey: string, type: 'read' | 'write'): boolean => {
|
|
const perms = fieldPermissions.value[fieldKey]
|
|
if (!perms) return true
|
|
const value = type === 'read' ? perms.defaultReadable : perms.defaultWritable
|
|
return Boolean(value)
|
|
}
|
|
|
|
const setFieldPermission = (fieldKey: string, type: 'read' | 'write', value: boolean) => {
|
|
if (!fieldPermissions.value[fieldKey]) {
|
|
fieldPermissions.value[fieldKey] = { defaultReadable: true, defaultWritable: true }
|
|
}
|
|
if (type === 'read') {
|
|
fieldPermissions.value[fieldKey].defaultReadable = Boolean(value)
|
|
} else {
|
|
fieldPermissions.value[fieldKey].defaultWritable = Boolean(value)
|
|
}
|
|
}
|
|
|
|
const saveChanges = async () => {
|
|
try {
|
|
saving.value = true
|
|
|
|
// Ensure all values are proper booleans
|
|
const payload = {
|
|
accessModel: accessModel.value,
|
|
publicRead: Boolean(publicRead.value),
|
|
publicCreate: Boolean(publicCreate.value),
|
|
publicUpdate: Boolean(publicUpdate.value),
|
|
publicDelete: Boolean(publicDelete.value),
|
|
ownerField: ownerField.value,
|
|
}
|
|
|
|
// Update global access config
|
|
await api.put(`/setup/objects/${props.objectApiName}/access`, payload)
|
|
|
|
// Update field permissions
|
|
const fieldPermsArray = Object.entries(fieldPermissions.value).map(([fieldKey, perms]) => ({
|
|
fieldKey,
|
|
defaultReadable: perms.defaultReadable,
|
|
defaultWritable: perms.defaultWritable,
|
|
}))
|
|
|
|
await api.put(`/setup/objects/${props.objectApiName}/field-permissions`, fieldPermsArray)
|
|
|
|
toast.success('Access settings saved successfully')
|
|
emit('updated')
|
|
} catch (e: any) {
|
|
console.error('Error saving access config:', e)
|
|
toast.error('Failed to save access settings')
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchAccessConfig()
|
|
})
|
|
</script>
|