UNPKG

@dbs-portal/module-tenant-management

Version:

Tenant management and multi-tenancy support module

130 lines 13 kB
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