@dbs-portal/module-tenant-management
Version:
Tenant management and multi-tenancy support module
130 lines • 13 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* TenantForm Component - Create and edit tenants
*/
import React, { useEffect } from 'react';
import { Form, Input, Switch, Button, Card, Space, Row, Col, InputNumber, Select, Divider, Alert, Collapse, Tag } from 'antd';
import { SaveOutlined, CloseOutlined, InfoCircleOutlined, SettingOutlined, DatabaseOutlined, MailOutlined } from '@ant-design/icons';
import { useCreateTenant, useUpdateTenant, useAvailableFeatures } from '../hooks';
const { TextArea } = Input;
const { Option } = Select;
const { Panel } = Collapse;
/**
* TenantForm component
*/
export const TenantForm = ({ tenant, onSubmit, onCancel, loading = false, className }) => {
const [form] = Form.useForm();
const isEditing = !!tenant;
// Queries
const { data: availableFeatures = [] } = useAvailableFeatures();
// Mutations
const createTenantMutation = useCreateTenant();
const updateTenantMutation = useUpdateTenant();
// Initialize form with tenant data
useEffect(() => {
if (tenant) {
form.setFieldsValue({
name: tenant.name,
displayName: tenant.displayName,
connectionString: tenant.connectionString,
isActive: tenant.isActive,
// Settings
maxUsers: tenant.settings?.maxUsers,
maxStorage: tenant.settings?.maxStorage,
allowedDomains: tenant.settings?.allowedDomains?.join(', '),
customDomain: tenant.settings?.customDomain,
theme: tenant.settings?.theme,
timezone: tenant.settings?.timezone,
language: tenant.settings?.language,
// Email settings
fromEmail: tenant.settings?.emailSettings?.fromEmail,
fromName: tenant.settings?.emailSettings?.fromName,
smtpHost: tenant.settings?.emailSettings?.smtpHost,
smtpPort: tenant.settings?.emailSettings?.smtpPort,
smtpUsername: tenant.settings?.emailSettings?.smtpUsername,
smtpPassword: tenant.settings?.emailSettings?.smtpPassword,
useSsl: tenant.settings?.emailSettings?.useSsl,
// Features
features: tenant.features?.reduce((acc, feature) => {
acc[feature.name] = feature.isEnabled;
return acc;
}, {})
});
}
}, [tenant, form]);
const handleSubmit = async (values) => {
try {
// Parse allowed domains
const allowedDomains = values.allowedDomains
? values.allowedDomains.split(',').map((d) => d.trim()).filter(Boolean)
: undefined;
// Prepare features array
const features = availableFeatures
.filter(feature => values.features?.[feature.name])
.map(feature => ({
name: feature.name,
isEnabled: true,
value: feature.defaultValue
}));
const tenantData = {
name: values.name,
displayName: values.displayName,
connectionString: values.connectionString,
isActive: values.isActive ?? true,
features,
settings: {
maxUsers: values.maxUsers,
maxStorage: values.maxStorage,
allowedDomains,
customDomain: values.customDomain,
theme: values.theme,
timezone: values.timezone,
language: values.language,
emailSettings: {
fromEmail: values.fromEmail,
fromName: values.fromName,
smtpHost: values.smtpHost,
smtpPort: values.smtpPort,
smtpUsername: values.smtpUsername,
smtpPassword: values.smtpPassword,
useSsl: values.useSsl
}
}
};
if (isEditing) {
const updateData = {
id: tenant.id,
...tenantData
};
const updatedTenant = await updateTenantMutation.mutateAsync(updateData);
onSubmit?.(updateData);
}
else {
const createData = tenantData;
const newTenant = await createTenantMutation.mutateAsync(createData);
onSubmit?.(createData);
}
form.resetFields();
}
catch (error) {
console.error('Failed to save tenant:', error);
}
};
const handleCancel = () => {
form.resetFields();
onCancel?.();
};
const isSubmitting = loading || createTenantMutation.isPending || updateTenantMutation.isPending;
return (_jsx("div", { className: className, children: _jsx(Form, { form: form, layout: "vertical", onFinish: handleSubmit, initialValues: {
isActive: true,
theme: 'default',
timezone: 'UTC',
language: 'en',
useSsl: true
}, children: _jsxs(Space, { direction: "vertical", size: "large", style: { width: '100%' }, children: [_jsxs(Card, { title: _jsxs(Space, { children: [_jsx(InfoCircleOutlined, {}), "Basic Information"] }), children: [_jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "name", label: "Tenant Name", rules: [
{ required: true, message: 'Please enter tenant name' },
{ pattern: /^[a-z0-9-]+$/, message: 'Only lowercase letters, numbers, and hyphens allowed' }
], tooltip: "Unique identifier for the tenant (used in URLs and database)", children: _jsx(Input, { placeholder: "e.g., acme-corp", disabled: isEditing }) }) }), _jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "displayName", label: "Display Name", rules: [{ required: true, message: 'Please enter display name' }], tooltip: "Human-readable name shown in the UI", children: _jsx(Input, { placeholder: "e.g., Acme Corporation" }) }) })] }), _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 18, children: _jsx(Form.Item, { name: "connectionString", label: "Database Connection String", tooltip: "Optional custom database connection for tenant isolation", children: _jsx(Input.Password, { placeholder: "Server=...;Database=...;User Id=...;Password=..." }) }) }), _jsx(Col, { xs: 24, md: 6, children: _jsx(Form.Item, { name: "isActive", label: "Active", valuePropName: "checked", children: _jsx(Switch, {}) }) })] })] }), _jsx(Card, { title: _jsxs(Space, { children: [_jsx(SettingOutlined, {}), "Tenant Settings"] }), children: _jsxs(Collapse, { ghost: true, children: [_jsx(Panel, { header: "Resource Limits", children: _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "maxUsers", label: "Maximum Users", tooltip: "Maximum number of users allowed for this tenant", children: _jsx(InputNumber, { min: 1, max: 10000, placeholder: "e.g., 100", style: { width: '100%' } }) }) }), _jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "maxStorage", label: "Maximum Storage (MB)", tooltip: "Maximum storage allowed for this tenant in megabytes", children: _jsx(InputNumber, { min: 100, max: 100000, placeholder: "e.g., 5000", style: { width: '100%' } }) }) })] }) }, "limits"), _jsxs(Panel, { header: "Domain & Branding", children: [_jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "customDomain", label: "Custom Domain", tooltip: "Custom domain for this tenant (e.g., tenant.yourdomain.com)", children: _jsx(Input, { placeholder: "e.g., acme.yourdomain.com" }) }) }), _jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "theme", label: "Theme", tooltip: "UI theme for this tenant", children: _jsxs(Select, { placeholder: "Select theme", children: [_jsx(Option, { value: "default", children: "Default" }), _jsx(Option, { value: "dark", children: "Dark" }), _jsx(Option, { value: "light", children: "Light" }), _jsx(Option, { value: "custom", children: "Custom" })] }) }) })] }), _jsx(Form.Item, { name: "allowedDomains", label: "Allowed Email Domains", tooltip: "Comma-separated list of allowed email domains for user registration", children: _jsx(Input, { placeholder: "e.g., acme.com, acme.org" }) })] }, "branding"), _jsx(Panel, { header: "Localization", children: _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "timezone", label: "Timezone", children: _jsxs(Select, { placeholder: "Select timezone", children: [_jsx(Option, { value: "UTC", children: "UTC" }), _jsx(Option, { value: "America/New_York", children: "Eastern Time" }), _jsx(Option, { value: "America/Chicago", children: "Central Time" }), _jsx(Option, { value: "America/Denver", children: "Mountain Time" }), _jsx(Option, { value: "America/Los_Angeles", children: "Pacific Time" }), _jsx(Option, { value: "Europe/London", children: "London" }), _jsx(Option, { value: "Europe/Paris", children: "Paris" }), _jsx(Option, { value: "Asia/Tokyo", children: "Tokyo" })] }) }) }), _jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "language", label: "Default Language", children: _jsxs(Select, { placeholder: "Select language", children: [_jsx(Option, { value: "en", children: "English" }), _jsx(Option, { value: "es", children: "Spanish" }), _jsx(Option, { value: "fr", children: "French" }), _jsx(Option, { value: "de", children: "German" }), _jsx(Option, { value: "ja", children: "Japanese" }), _jsx(Option, { value: "zh", children: "Chinese" })] }) }) })] }) }, "localization"), _jsxs(Panel, { header: "Email Settings", children: [_jsx(Alert, { message: "Email Configuration", description: "Configure SMTP settings for tenant-specific email notifications", type: "info", showIcon: true, style: { marginBottom: 16 } }), _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "fromEmail", label: "From Email", children: _jsx(Input, { placeholder: "noreply@acme.com" }) }) }), _jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "fromName", label: "From Name", children: _jsx(Input, { placeholder: "Acme Corporation" }) }) })] }), _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "smtpHost", label: "SMTP Host", children: _jsx(Input, { placeholder: "smtp.gmail.com" }) }) }), _jsx(Col, { xs: 24, md: 6, children: _jsx(Form.Item, { name: "smtpPort", label: "SMTP Port", children: _jsx(InputNumber, { min: 1, max: 65535, placeholder: "587", style: { width: '100%' } }) }) }), _jsx(Col, { xs: 24, md: 6, children: _jsx(Form.Item, { name: "useSsl", label: "Use SSL", valuePropName: "checked", children: _jsx(Switch, {}) }) })] }), _jsxs(Row, { gutter: 16, children: [_jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "smtpUsername", label: "SMTP Username", children: _jsx(Input, { placeholder: "username@gmail.com" }) }) }), _jsx(Col, { xs: 24, md: 12, children: _jsx(Form.Item, { name: "smtpPassword", label: "SMTP Password", children: _jsx(Input.Password, { placeholder: "Enter SMTP password" }) }) })] })] }, "email")] }) }), availableFeatures.length > 0 && (_jsxs(Card, { title: _jsxs(Space, { children: [_jsx(DatabaseOutlined, {}), "Features"] }), children: [_jsx(Alert, { message: "Feature Configuration", description: "Enable or disable features for this tenant", type: "info", showIcon: true, style: { marginBottom: 16 } }), _jsx(Row, { gutter: [16, 16], children: availableFeatures.map(feature => (_jsx(Col, { xs: 24, md: 12, lg: 8, children: _jsxs(Card, { size: "small", children: [_jsx(Form.Item, { name: ['features', feature.name], valuePropName: "checked", style: { marginBottom: 8 }, children: _jsx(Switch, {}) }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 500 }, children: feature.displayName }), _jsx("div", { style: { fontSize: '12px', color: '#666', marginTop: 4 }, children: feature.description }), _jsxs("div", { style: { marginTop: 8 }, children: [_jsx(Tag, { children: feature.category }), feature.isRequired && _jsx(Tag, { color: "red", children: "Required" })] })] })] }) }, feature.name))) })] })), _jsx(Card, { children: _jsx("div", { style: { textAlign: 'right' }, children: _jsxs(Space, { children: [_jsxs(Button, { onClick: handleCancel, disabled: isSubmitting, children: [_jsx(CloseOutlined, {}), " Cancel"] }), _jsx(Button, { type: "primary", htmlType: "submit", loading: isSubmitting, icon: _jsx(SaveOutlined, {}), children: isEditing ? 'Update Tenant' : 'Create Tenant' })] }) }) })] }) }) }));
};
export default TenantForm;
//# sourceMappingURL=TenantForm.js.map