WIP - manage tenant users from central

This commit is contained in:
Francisco Gaona
2025-12-24 12:17:22 +01:00
parent b9fa3bd008
commit 52c0849de2
7 changed files with 773 additions and 60 deletions

View File

@@ -52,7 +52,10 @@ const fetchRelatedRecords = async () => {
error.value = null
try {
const response = await api.get(`${props.baseUrl}/${props.config.objectApiName}`, {
// Replace :parentId placeholder in the API path
let apiPath = props.config.objectApiName.replace(':parentId', props.parentId)
const response = await api.get(`${props.baseUrl}/${apiPath}`, {
params: {
parentId: props.parentId,
},

View File

@@ -0,0 +1,136 @@
<script setup lang="ts">
import { ref } from 'vue'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
interface Props {
open: boolean
tenantId: string
tenantName?: string
}
const props = defineProps<Props>()
const emit = defineEmits<{
'update:open': [value: boolean]
'created': [user: any]
}>()
const { api } = useApi()
const { toast } = useToast()
const formData = ref({
email: '',
password: '',
firstName: '',
lastName: '',
})
const saving = ref(false)
const handleSubmit = async () => {
if (!formData.value.email || !formData.value.password) {
toast.error('Email and password are required')
return
}
saving.value = true
try {
const response = await api.post(`/central/tenants/${props.tenantId}/users`, formData.value)
toast.success('User created successfully')
emit('created', response)
emit('update:open', false)
// Reset form
formData.value = {
email: '',
password: '',
firstName: '',
lastName: '',
}
} catch (error: any) {
console.error('Error creating user:', error)
toast.error(error.message || 'Failed to create user')
} finally {
saving.value = false
}
}
const handleCancel = () => {
emit('update:open', false)
// Reset form
formData.value = {
email: '',
password: '',
firstName: '',
lastName: '',
}
}
</script>
<template>
<Dialog :open="open" @update:open="(val) => emit('update:open', val)">
<DialogContent class="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>Create Tenant User</DialogTitle>
<DialogDescription v-if="tenantName">
Add a new user to {{ tenantName }}
</DialogDescription>
</DialogHeader>
<div class="grid gap-4 py-4">
<div class="grid gap-2">
<Label for="email">Email *</Label>
<Input
id="email"
v-model="formData.email"
type="email"
placeholder="user@example.com"
required
/>
</div>
<div class="grid gap-2">
<Label for="password">Password *</Label>
<Input
id="password"
v-model="formData.password"
type="password"
placeholder="Enter password"
required
/>
</div>
<div class="grid gap-2">
<Label for="firstName">First Name</Label>
<Input
id="firstName"
v-model="formData.firstName"
type="text"
placeholder="John"
/>
</div>
<div class="grid gap-2">
<Label for="lastName">Last Name</Label>
<Input
id="lastName"
v-model="formData.lastName"
type="text"
placeholder="Doe"
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" @click="handleCancel" :disabled="saving">
Cancel
</Button>
<Button @click="handleSubmit" :disabled="saving">
{{ saving ? 'Creating...' : 'Create User' }}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>

View File

@@ -167,6 +167,18 @@ export const tenantDetailConfig: DetailViewConfig = {
],
canCreate: true,
},
{
title: 'Tenant Users',
relationName: 'users',
objectApiName: 'tenants/:parentId/users',
fields: [
{ id: 'email', apiName: 'email', label: 'Email', type: FieldType.EMAIL },
{ id: 'firstName', apiName: 'firstName', label: 'First Name', type: FieldType.TEXT },
{ id: 'lastName', apiName: 'lastName', label: 'Last Name', type: FieldType.TEXT },
{ id: 'createdAt', apiName: 'createdAt', label: 'Created', type: FieldType.DATETIME },
],
canCreate: true,
},
],
}

View File

@@ -12,11 +12,16 @@ import {
import ListView from '@/components/views/ListView.vue'
import DetailView from '@/components/views/DetailViewEnhanced.vue'
import EditView from '@/components/views/EditViewEnhanced.vue'
import TenantUserDialog from '@/components/TenantUserDialog.vue'
const route = useRoute()
const router = useRouter()
const { api } = useApi()
// Tenant user dialog state
const showTenantUserDialog = ref(false)
const tenantUserDialogTenantId = ref('')
const recordId = computed(() => route.params.recordId as string)
const view = computed(() => {
if (route.params.recordId === 'new' && !route.params.view) {
@@ -88,6 +93,13 @@ const handleNavigate = (objectApiName: string, recordId: string) => {
// Handle creating related records
const handleCreateRelated = (objectApiName: string, parentId: string) => {
// Special handling for tenant users
if (objectApiName.includes('tenants/:parentId/users')) {
tenantUserDialogTenantId.value = parentId
showTenantUserDialog.value = true
return
}
// Navigate to create page with parent context
router.push({
path: `/central/${objectApiName}/new`,
@@ -95,6 +107,14 @@ const handleCreateRelated = (objectApiName: string, parentId: string) => {
})
}
// Handle tenant user created
const handleTenantUserCreated = async () => {
// Refresh the current record to update related lists
if (recordId.value && recordId.value !== 'new') {
await fetchRecord(recordId.value)
}
}
const handleSaveRecord = async (data: any) => {
try {
const savedRecord = await handleSave(data)
@@ -167,6 +187,14 @@ onMounted(async () => {
@back="handleBack"
/>
</div>
<!-- Tenant User Creation Dialog -->
<TenantUserDialog
v-model:open="showTenantUserDialog"
:tenant-id="tenantUserDialogTenantId"
:tenant-name="(currentRecord as any)?.name"
@created="handleTenantUserCreated"
/>
</NuxtLayout>
</template>