import { mdiChartTimelineVariant, mdiOfficeBuildingCogOutline } from '@mdi/js' import axios from 'axios' import Head from 'next/head' import React, { ReactElement, useEffect, useMemo, useState } from 'react' import { Field, Form, Formik } from 'formik' import { useRouter } from 'next/router' import BaseButton from '../../components/BaseButton' import BaseButtons from '../../components/BaseButtons' import BaseDivider from '../../components/BaseDivider' import CardBox from '../../components/CardBox' import ConnectedEntityCard from '../../components/ConnectedEntityCard' import ConnectedEntityNotice from '../../components/ConnectedEntityNotice' import FormField from '../../components/FormField' import NotificationBar from '../../components/NotificationBar' import SectionMain from '../../components/SectionMain' import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton' import { SelectFieldMany } from '../../components/SelectFieldMany' import { SwitchField } from '../../components/SwitchField' import { getPageTitle } from '../../config' import { getOrganizationManageHref, getOrganizationViewHref, getTenantSetupHref, mergeEntityOptions, } from '../../helpers/organizationTenants' import { hasPermission } from '../../helpers/userPermissions' import LayoutAuthenticated from '../../layouts/Authenticated' import { useAppDispatch, useAppSelector } from '../../stores/hooks' import { fetch, update } from '../../stores/tenants/tenantsSlice' const defaultInitialValues = { name: '', slug: '', legal_name: '', primary_domain: '', timezone: '', default_currency: '', is_active: false, organizations: [], properties: [], audit_logs: [], } const EditTenantsPage = () => { const router = useRouter() const dispatch = useAppDispatch() const { tenants } = useAppSelector((state) => state.tenants) const { currentUser } = useAppSelector((state) => state.auth) const [baseInitialValues, setBaseInitialValues] = useState(defaultInitialValues) const [organizationPrefillOptions, setOrganizationPrefillOptions] = useState([]) const [linkedOrganization, setLinkedOrganization] = useState<{ id: string; name: string } | null>(null) const [prefillError, setPrefillError] = useState('') const { id } = router.query const tenantId = useMemo(() => (typeof id === 'string' ? id : ''), [id]) const organizationId = useMemo( () => (typeof router.query.organizationId === 'string' ? router.query.organizationId : ''), [router.query.organizationId], ) const organizationNameFromQuery = useMemo( () => (typeof router.query.organizationName === 'string' ? router.query.organizationName : ''), [router.query.organizationName], ) const canReadOrganizations = hasPermission(currentUser, 'READ_ORGANIZATIONS') const canUpdateOrganizations = hasPermission(currentUser, 'UPDATE_ORGANIZATIONS') const initialValues = useMemo( () => ({ ...baseInitialValues, organizations: mergeEntityOptions(baseInitialValues.organizations, organizationPrefillOptions), }), [baseInitialValues, organizationPrefillOptions], ) useEffect(() => { if (!id) { return } dispatch(fetch({ id })) }, [dispatch, id]) useEffect(() => { if (!tenants || typeof tenants !== 'object' || Array.isArray(tenants)) { return } setBaseInitialValues({ ...defaultInitialValues, name: tenants.name || '', slug: tenants.slug || '', legal_name: tenants.legal_name || '', primary_domain: tenants.primary_domain || '', timezone: tenants.timezone || '', default_currency: tenants.default_currency || '', is_active: Boolean(tenants.is_active), organizations: Array.isArray(tenants.organizations) ? tenants.organizations : [], properties: Array.isArray(tenants.properties) ? tenants.properties : [], audit_logs: Array.isArray(tenants.audit_logs) ? tenants.audit_logs : [], }) }, [tenants]) useEffect(() => { if (!router.isReady) { return } if (!organizationId) { setLinkedOrganization(null) setOrganizationPrefillOptions([]) setPrefillError('') return } let isActive = true const loadOrganization = async () => { try { setPrefillError('') const { data } = await axios.get(`/organizations/${organizationId}`) if (!isActive) { return } const organizationOption = data?.id ? [{ id: data.id, name: data.name }] : [] setLinkedOrganization(data?.id ? { id: data.id, name: data.name } : null) setOrganizationPrefillOptions(organizationOption) } catch (error) { console.error('Failed to prefill tenant edit from organization:', error) if (!isActive) { return } setLinkedOrganization( organizationId ? { id: organizationId, name: organizationNameFromQuery || 'Selected organization', } : null, ) setOrganizationPrefillOptions( organizationId ? [ { id: organizationId, name: organizationNameFromQuery || 'Selected organization', }, ] : [], ) setPrefillError('We could not load the full organization record, but you can still keep or remove the preselected organization below.') } } loadOrganization() return () => { isActive = false } }, [organizationId, organizationNameFromQuery, router.isReady]) const handleSubmit = async (data) => { const resultAction = await dispatch(update({ id, data })) if (!update.fulfilled.match(resultAction)) { return } await router.push('/tenants/tenants-list') } const linkedOrganizations = Array.isArray(initialValues.organizations) ? initialValues.organizations : [] return ( <> {getPageTitle('Edit tenant')} {''} } > Keep tenant-wide settings here, and use the organizations field below to control which workspaces belong to this tenant. {linkedOrganization ? (

Organization ready to link: {linkedOrganization.name}

This organization is preselected below so you can attach it to this tenant in the same save.

) : null} {prefillError ? (
{prefillError}
) : null} handleSubmit(values)}> {({ values }) => { const tenantNameForLinks = values.name || initialValues.name || tenants?.name || '' return (
Tenants hold shared settings, while organizations are the workspaces connected to them. {linkedOrganizations.length ? ` ${linkedOrganizations.length} organization${linkedOrganizations.length === 1 ? '' : 's'} currently linked.` : ' No organizations are linked yet.'} {linkedOrganization ? ` ${linkedOrganization.name} is already preselected from the organization flow.` : ''} } actions={[ { href: '/organizations/organizations-list', label: 'Browse organizations', color: 'info', outline: true, }, ...(linkedOrganization ? canUpdateOrganizations ? [ { href: getOrganizationManageHref(linkedOrganization.id, tenantId, tenantNameForLinks), label: 'Back to organization', color: 'white', outline: true, }, ] : canReadOrganizations ? [ { href: getOrganizationViewHref(linkedOrganization.id, tenantId, tenantNameForLinks), label: 'View organization', color: 'white', outline: true, }, ] : [] : []), ]} />

Linked organizations

{linkedOrganizations.length ? `${linkedOrganizations.length} organization${linkedOrganizations.length === 1 ? '' : 's'} will stay attached to this tenant after you save.` : 'No organizations are currently linked. Add one below when this tenant should own a workspace.'}

{linkedOrganizations.length ? (
{linkedOrganizations.map((organization: any) => ( ))}
) : null}
router.push('/tenants/tenants-list')} /> ) }}
) } EditTenantsPage.getLayout = function getLayout(page: ReactElement) { return {page} } export default EditTenantsPage