feat: Dashboard redesign, plan permissions, and help docs improvements

Major updates including:
- Customizable dashboard with drag-and-drop widget grid layout
- Plan-based feature locking for plugins and tasks
- Comprehensive help documentation updates across all pages
- Plugin seeding in deployment process for all tenants
- Permission synchronization system for subscription plans
- QuotaOverageModal component and enhanced UX flows

🤖 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 13:02:44 -05:00
parent 9444e26924
commit dcb14503a2
66 changed files with 7099 additions and 1467 deletions

View File

@@ -52,7 +52,7 @@ const BusinessLayoutContent: React.FC<BusinessLayoutProps> = ({ business, user,
const [searchParams] = useSearchParams();
const navigate = useNavigate();
useScrollToTop();
useScrollToTop(mainContentRef);
// Fetch ticket data when modal is opened from notification
const { data: ticketFromNotification } = useTicket(ticketModalId || undefined);

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Outlet, Link, useNavigate } from 'react-router-dom';
import { User, Business } from '../types';
import { LayoutDashboard, CalendarPlus, CreditCard, HelpCircle, Sun, Moon } from 'lucide-react';
@@ -18,7 +18,8 @@ interface CustomerLayoutProps {
const CustomerLayout: React.FC<CustomerLayoutProps> = ({ business, user, darkMode, toggleTheme }) => {
const navigate = useNavigate();
useScrollToTop();
const mainContentRef = useRef<HTMLElement>(null);
useScrollToTop(mainContentRef);
// Masquerade logic
const [masqueradeStack, setMasqueradeStack] = useState<MasqueradeStackEntry[]>([]);
@@ -116,7 +117,7 @@ const CustomerLayout: React.FC<CustomerLayoutProps> = ({ business, user, darkMod
</div>
</div>
</header>
<main className="flex-1 overflow-y-auto">
<main ref={mainContentRef} className="flex-1 overflow-y-auto">
<div className="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
<Outlet context={{ business, user }} />
</div>

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { Outlet } from 'react-router-dom';
import { Moon, Sun, Bell, Globe, Menu } from 'lucide-react';
import { User } from '../types';
@@ -16,8 +16,9 @@ interface ManagerLayoutProps {
const ManagerLayout: React.FC<ManagerLayoutProps> = ({ user, darkMode, toggleTheme, onSignOut }) => {
const [isCollapsed, setIsCollapsed] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const mainContentRef = useRef<HTMLElement>(null);
useScrollToTop();
useScrollToTop(mainContentRef);
return (
<div className="flex h-full bg-gray-100 dark:bg-gray-900">
@@ -63,7 +64,7 @@ const ManagerLayout: React.FC<ManagerLayoutProps> = ({ user, darkMode, toggleThe
</div>
</header>
<main className="flex-1 overflow-auto bg-gray-50 dark:bg-gray-900 p-8">
<main ref={mainContentRef} className="flex-1 overflow-auto bg-gray-50 dark:bg-gray-900 p-8">
<Outlet />
</main>
</div>

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { Outlet } from 'react-router-dom';
import { Moon, Sun, Globe, Menu } from 'lucide-react';
import { User } from '../types';
@@ -21,8 +21,9 @@ const PlatformLayout: React.FC<PlatformLayoutProps> = ({ user, darkMode, toggleT
const [isCollapsed, setIsCollapsed] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [ticketModalId, setTicketModalId] = useState<string | null>(null);
const mainContentRef = useRef<HTMLElement>(null);
useScrollToTop();
useScrollToTop(mainContentRef);
// Fetch ticket data when modal is opened from notification
const { data: ticketFromNotification } = useTicket(ticketModalId && ticketModalId !== 'undefined' ? ticketModalId : undefined);
@@ -83,7 +84,7 @@ const PlatformLayout: React.FC<PlatformLayoutProps> = ({ user, darkMode, toggleT
</div>
</header>
<main className="flex-1 overflow-auto bg-gray-50 dark:bg-gray-900 p-8">
<main ref={mainContentRef} className="flex-1 overflow-auto bg-gray-50 dark:bg-gray-900 p-8">
<Outlet />
</main>
</div>