Add platform email templates, staff invitations, and quota tracking
- Add PlatformEmailTemplate model and API for superuser-managed email templates - Add PlatformStaffInvitation model with email sending via Celery tasks - Add platform staff invite page and acceptance flow with auto-login - Add quota tracking models (DailyAppointmentUsage, DailyAPIUsage, StorageUsage) - Add quota status API endpoints and frontend banners - Add storage usage service for tenant media tracking - Fix platform user deletion with raw SQL to handle multi-tenant FK constraints - Update EditPlatformUserModal with archive/delete buttons - Update PlatformSidebar with email templates link for superusers - Configure console email backend and Celery eager mode for local development 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,10 +66,12 @@ const PlatformStaff = React.lazy(() => import('./pages/platform/PlatformStaff'))
|
||||
const PlatformSettings = React.lazy(() => import('./pages/platform/PlatformSettings'));
|
||||
const BillingManagement = React.lazy(() => import('./pages/platform/BillingManagement'));
|
||||
const PlatformStaffEmail = React.lazy(() => import('./pages/platform/PlatformStaffEmail'));
|
||||
const PlatformEmailTemplates = React.lazy(() => import('./pages/platform/PlatformEmailTemplates'));
|
||||
const ProfileSettings = React.lazy(() => import('./pages/ProfileSettings'));
|
||||
const VerifyEmail = React.lazy(() => import('./pages/VerifyEmail'));
|
||||
const EmailVerificationRequired = React.lazy(() => import('./pages/EmailVerificationRequired'));
|
||||
const AcceptInvitePage = React.lazy(() => import('./pages/AcceptInvitePage'));
|
||||
const PlatformStaffInvitePage = React.lazy(() => import('./pages/platform/PlatformStaffInvitePage'));
|
||||
const TenantOnboardPage = React.lazy(() => import('./pages/TenantOnboardPage'));
|
||||
const TenantLandingPage = React.lazy(() => import('./pages/TenantLandingPage'));
|
||||
const Tickets = React.lazy(() => import('./pages/Tickets')); // Import Tickets page
|
||||
@@ -375,6 +377,7 @@ const AppContent: React.FC = () => {
|
||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||
<Route path="/accept-invite" element={<AcceptInvitePage />} />
|
||||
<Route path="/accept-invite/:token" element={<AcceptInvitePage />} />
|
||||
<Route path="/platform-staff-invite" element={<PlatformStaffInvitePage />} />
|
||||
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
|
||||
<Route path="/sign/:token" element={<ContractSigning />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
@@ -411,6 +414,7 @@ const AppContent: React.FC = () => {
|
||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||
<Route path="/accept-invite" element={<AcceptInvitePage />} />
|
||||
<Route path="/accept-invite/:token" element={<AcceptInvitePage />} />
|
||||
<Route path="/platform-staff-invite" element={<PlatformStaffInvitePage />} />
|
||||
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
|
||||
<Route path="/sign/:token" element={<ContractSigning />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
@@ -419,10 +423,10 @@ const AppContent: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
// For platform subdomain, only /platform/login exists - everything else renders nothing
|
||||
// For platform subdomain, only specific paths exist - everything else renders nothing
|
||||
if (isPlatformSubdomain) {
|
||||
const path = window.location.pathname;
|
||||
const allowedPaths = ['/platform/login', '/mfa-verify', '/verify-email'];
|
||||
const allowedPaths = ['/platform/login', '/mfa-verify', '/verify-email', '/platform-staff-invite'];
|
||||
|
||||
// If not an allowed path, render nothing
|
||||
if (!allowedPaths.includes(path)) {
|
||||
@@ -435,6 +439,7 @@ const AppContent: React.FC = () => {
|
||||
<Route path="/platform/login" element={<PlatformLoginPage />} />
|
||||
<Route path="/mfa-verify" element={<MFAVerifyPage />} />
|
||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||
<Route path="/platform-staff-invite" element={<PlatformStaffInvitePage />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
);
|
||||
@@ -460,6 +465,7 @@ const AppContent: React.FC = () => {
|
||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||
<Route path="/accept-invite" element={<AcceptInvitePage />} />
|
||||
<Route path="/accept-invite/:token" element={<AcceptInvitePage />} />
|
||||
<Route path="/platform-staff-invite" element={<PlatformStaffInvitePage />} />
|
||||
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
|
||||
<Route path="/sign/:token" element={<ContractSigning />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
@@ -599,6 +605,7 @@ const AppContent: React.FC = () => {
|
||||
<>
|
||||
<Route path="/platform/settings" element={<PlatformSettings />} />
|
||||
<Route path="/platform/billing" element={<BillingManagement />} />
|
||||
<Route path="/platform/email-templates" element={<PlatformEmailTemplates />} />
|
||||
</>
|
||||
)}
|
||||
<Route path="/platform/profile" element={<ProfileSettings />} />
|
||||
|
||||
Reference in New Issue
Block a user