feat: Add photo galleries to services, resource types management, and UI improvements

Major features:
- Add drag-and-drop photo gallery to Service create/edit modals
- Add Resource Types management section to Settings (CRUD for custom types)
- Add edit icon consistency to Resources table (pencil icon in actions)
- Improve Services page with drag-to-reorder and customer preview mockup

Backend changes:
- Add photos JSONField to Service model with migration
- Add ResourceType model with category (STAFF/OTHER), description fields
- Add ResourceTypeViewSet with CRUD operations
- Add service reorder endpoint for display order

Frontend changes:
- Services page: two-column layout, drag-reorder, photo upload
- Settings page: Resource Types tab with full CRUD modal
- Resources page: Edit icon in actions column instead of row click
- Sidebar: Payments link visibility based on role and paymentsEnabled
- Update types.ts with Service.photos and ResourceTypeDefinition

Note: Removed photos from ResourceType (kept only for Service)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-11-28 01:11:53 -05:00
parent a7c756a8ec
commit b10426fbdb
52 changed files with 4259 additions and 356 deletions

View File

@@ -4,13 +4,15 @@ import { useTranslation } from 'react-i18next';
import { Menu, X, Sun, Moon } from 'lucide-react';
import SmoothScheduleLogo from '../SmoothScheduleLogo';
import LanguageSelector from '../LanguageSelector';
import { User } from '../../api/auth';
interface NavbarProps {
darkMode: boolean;
toggleTheme: () => void;
user?: User | null;
}
const Navbar: React.FC<NavbarProps> = ({ darkMode, toggleTheme }) => {
const Navbar: React.FC<NavbarProps> = ({ darkMode, toggleTheme, user }) => {
const { t } = useTranslation();
const location = useLocation();
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -38,6 +40,21 @@ const Navbar: React.FC<NavbarProps> = ({ darkMode, toggleTheme }) => {
const isActive = (path: string) => location.pathname === path;
// Get the dashboard URL based on user role
const getDashboardUrl = (): string => {
if (!user) return '/login';
const port = window.location.port ? `:${window.location.port}` : '';
const protocol = window.location.protocol;
if (['superuser', 'platform_manager', 'platform_support'].includes(user.role)) {
return `${protocol}//platform.lvh.me${port}/`;
}
if (user.business_subdomain) {
return `${protocol}//${user.business_subdomain}.lvh.me${port}/`;
}
return '/login';
};
return (
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
@@ -90,12 +107,21 @@ const Navbar: React.FC<NavbarProps> = ({ darkMode, toggleTheme }) => {
</button>
{/* Login Button - Hidden on mobile */}
<Link
to="/login"
className="hidden md:inline-flex px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-brand-600 dark:hover:text-brand-400 transition-colors"
>
{t('marketing.nav.login')}
</Link>
{user ? (
<a
href={getDashboardUrl()}
className="hidden md:inline-flex px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-brand-600 dark:hover:text-brand-400 transition-colors"
>
{t('marketing.nav.login')}
</a>
) : (
<Link
to="/login"
className="hidden md:inline-flex px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-brand-600 dark:hover:text-brand-400 transition-colors"
>
{t('marketing.nav.login')}
</Link>
)}
{/* Get Started CTA */}
<Link
@@ -139,12 +165,21 @@ const Navbar: React.FC<NavbarProps> = ({ darkMode, toggleTheme }) => {
</Link>
))}
<hr className="my-2 border-gray-200 dark:border-gray-800" />
<Link
to="/login"
className="px-4 py-3 rounded-lg text-sm font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
{t('marketing.nav.login')}
</Link>
{user ? (
<a
href={getDashboardUrl()}
className="px-4 py-3 rounded-lg text-sm font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
{t('marketing.nav.login')}
</a>
) : (
<Link
to="/login"
className="px-4 py-3 rounded-lg text-sm font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
{t('marketing.nav.login')}
</Link>
)}
<Link
to="/signup"
className="px-4 py-3 rounded-lg text-sm font-medium text-center text-white bg-brand-600 hover:bg-brand-700 transition-colors"