WIP - manage tenant users from central
This commit is contained in:
@@ -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,
|
||||
},
|
||||
|
||||
136
frontend/components/TenantUserDialog.vue
Normal file
136
frontend/components/TenantUserDialog.vue
Normal 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>
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user