feat: Quota enforcement UI and various improvements

- Add quota limit warnings to Resources, Services, and OwnerScheduler pages
- Add quotaUtils.ts for checking quota limits
- Update BusinessLayout with quota context
- Improve email receiver logging
- Update serializers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-03 15:47:48 -05:00
parent fd751f02f8
commit 4f515c3710
8 changed files with 281 additions and 36 deletions

View File

@@ -5,6 +5,7 @@ import TopBar from '../components/TopBar';
import TrialBanner from '../components/TrialBanner';
import SandboxBanner from '../components/SandboxBanner';
import QuotaWarningBanner from '../components/QuotaWarningBanner';
import QuotaOverageModal, { resetQuotaOverageModalDismissal } from '../components/QuotaOverageModal';
import { Business, User } from '../types';
import MasqueradeBanner from '../components/MasqueradeBanner';
import OnboardingWizard from '../components/OnboardingWizard';
@@ -108,9 +109,21 @@ const BusinessLayoutContent: React.FC<BusinessLayoutProps> = ({ business, user,
}, []);
const handleStopMasquerade = () => {
// Reset quota modal dismissal when returning from masquerade
resetQuotaOverageModalDismissal();
stopMasqueradeMutation.mutate();
};
// Reset quota modal when user changes (masquerade start)
const prevUserIdRef = useRef<string | undefined>(undefined);
useEffect(() => {
if (prevUserIdRef.current !== undefined && prevUserIdRef.current !== user.id) {
// User changed (masquerade started or changed) - reset modal
resetQuotaOverageModalDismissal();
}
prevUserIdRef.current = user.id;
}, [user.id]);
useNotificationWebSocket(); // Activate the notification WebSocket listener
// Get the previous user from the stack (the one we'll return to)
@@ -193,6 +206,10 @@ const BusinessLayoutContent: React.FC<BusinessLayoutProps> = ({ business, user,
{user.quota_overages && user.quota_overages.length > 0 && (
<QuotaWarningBanner overages={user.quota_overages} />
)}
{/* Quota overage modal - shows once per session on login/masquerade */}
{user.quota_overages && user.quota_overages.length > 0 && (
<QuotaOverageModal overages={user.quota_overages} onDismiss={() => {}} />
)}
{/* Sandbox mode banner */}
<SandboxBannerWrapper />
{/* Show trial banner if trial is active and payments not yet enabled */}