Add frontend pages imporvements
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTenant } from '../../stores/tenant.store';
|
||||
import { useToast } from '../../hooks/ui/useToast';
|
||||
import { ChevronDown, Building2, Check, AlertCircle } from 'lucide-react';
|
||||
import { ChevronDown, Building2, Check, AlertCircle, Plus } from 'lucide-react';
|
||||
|
||||
interface TenantSwitcherProps {
|
||||
className?: string;
|
||||
@@ -13,6 +14,7 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
className = '',
|
||||
showLabel = true,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [dropdownPosition, setDropdownPosition] = useState<{
|
||||
top: number;
|
||||
@@ -23,7 +25,7 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
}>({ top: 0, left: 0, width: 288, isMobile: false });
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
|
||||
const {
|
||||
currentTenant,
|
||||
availableTenants,
|
||||
@@ -33,7 +35,7 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
loadUserTenants,
|
||||
clearError,
|
||||
} = useTenant();
|
||||
|
||||
|
||||
const { success: showSuccessToast, error: showErrorToast } = useToast();
|
||||
|
||||
// Load tenants on mount
|
||||
@@ -170,6 +172,12 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
loadUserTenants();
|
||||
};
|
||||
|
||||
// Handle creating new tenant
|
||||
const handleCreateNewTenant = () => {
|
||||
setIsOpen(false);
|
||||
navigate('/app/onboarding');
|
||||
};
|
||||
|
||||
// Don't render if no tenants available
|
||||
if (!availableTenants || availableTenants.length === 0) {
|
||||
return null;
|
||||
@@ -229,11 +237,8 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
{/* Header */}
|
||||
<div className={`border-b border-border-primary ${dropdownPosition.isMobile ? 'px-4 py-3' : 'px-3 py-2'}`}>
|
||||
<h3 className={`font-semibold text-text-primary ${dropdownPosition.isMobile ? 'text-base' : 'text-sm'}`}>
|
||||
Switch Organization
|
||||
Organizations
|
||||
</h3>
|
||||
<p className={`text-text-secondary ${dropdownPosition.isMobile ? 'text-sm mt-1' : 'text-xs'}`}>
|
||||
Select the organization you want to work with
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Error State */}
|
||||
@@ -267,21 +272,25 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-8 h-8 bg-color-primary/10 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
<Building2 className="w-4 h-4 text-color-primary" />
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
|
||||
tenant.id === currentTenant?.id
|
||||
? 'bg-color-primary text-white'
|
||||
: 'bg-color-primary/10 text-color-primary'
|
||||
}`}>
|
||||
<Building2 className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium text-text-primary truncate">
|
||||
{tenant.name}
|
||||
</p>
|
||||
<p className="text-xs text-text-secondary truncate">
|
||||
{tenant.business_type} • {tenant.city}
|
||||
{tenant.city}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{tenant.id === currentTenant?.id && (
|
||||
<Check className="w-4 h-4 text-color-success flex-shrink-0 ml-2" />
|
||||
)}
|
||||
@@ -294,14 +303,17 @@ export const TenantSwitcher: React.FC<TenantSwitcherProps> = ({
|
||||
<div className={`border-t border-border-primary bg-bg-secondary rounded-b-lg ${
|
||||
dropdownPosition.isMobile ? 'px-4 py-3' : 'px-3 py-2'
|
||||
}`}>
|
||||
<p className={`text-text-secondary ${dropdownPosition.isMobile ? 'text-sm' : 'text-xs'}`}>
|
||||
Need to add a new organization?{' '}
|
||||
<button className={`text-color-primary hover:text-color-primary-dark underline ${
|
||||
dropdownPosition.isMobile ? 'active:text-color-primary-dark' : ''
|
||||
}`}>
|
||||
Contact Support
|
||||
</button>
|
||||
</p>
|
||||
<button
|
||||
onClick={handleCreateNewTenant}
|
||||
className={`w-full flex items-center justify-center gap-2 ${
|
||||
dropdownPosition.isMobile ? 'px-4 py-3' : 'px-3 py-2'
|
||||
} text-color-primary hover:text-color-primary-dark hover:bg-bg-tertiary rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-color-primary focus:ring-opacity-20`}
|
||||
>
|
||||
<Plus className="w-4 h-4" />
|
||||
<span className={`font-medium ${dropdownPosition.isMobile ? 'text-sm' : 'text-xs'}`}>
|
||||
Add New Organization
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
import { useTheme } from '../../../contexts/ThemeContext';
|
||||
import { Button } from '../Button';
|
||||
import { Sun, Moon, Computer } from 'lucide-react';
|
||||
import { Sun, Moon } from 'lucide-react';
|
||||
|
||||
export interface ThemeToggleProps {
|
||||
className?: string;
|
||||
@@ -29,7 +29,7 @@ export interface ThemeToggleProps {
|
||||
*
|
||||
* Features:
|
||||
* - Multiple display variants (button, dropdown, switch)
|
||||
* - Support for light/dark/system themes
|
||||
* - Support for light/dark themes
|
||||
* - Configurable size and labels
|
||||
* - Accessible keyboard navigation
|
||||
* - Click outside to close dropdown
|
||||
@@ -47,10 +47,11 @@ export const ThemeToggle: React.FC<ThemeToggleProps> = ({
|
||||
const themes = [
|
||||
{ key: 'light' as const, label: 'Claro', icon: Sun },
|
||||
{ key: 'dark' as const, label: 'Oscuro', icon: Moon },
|
||||
{ key: 'auto' as const, label: 'Sistema', icon: Computer },
|
||||
];
|
||||
|
||||
const currentTheme = themes.find(t => t.key === theme) || themes[0];
|
||||
// If theme is 'auto', use the resolved theme for display
|
||||
const displayTheme = theme === 'auto' ? resolvedTheme : theme;
|
||||
const currentTheme = themes.find(t => t.key === displayTheme) || themes[0];
|
||||
const CurrentIcon = currentTheme.icon;
|
||||
|
||||
// Size mappings
|
||||
@@ -93,20 +94,18 @@ export const ThemeToggle: React.FC<ThemeToggleProps> = ({
|
||||
return () => document.removeEventListener('click', handleClickOutside);
|
||||
}, [isDropdownOpen]);
|
||||
|
||||
// Cycle through themes for button variant
|
||||
// Toggle between light and dark for button variant
|
||||
const handleButtonToggle = () => {
|
||||
const currentIndex = themes.findIndex(t => t.key === theme);
|
||||
const nextIndex = (currentIndex + 1) % themes.length;
|
||||
setTheme(themes[nextIndex].key);
|
||||
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
// Handle theme selection
|
||||
const handleThemeSelect = (themeKey: 'light' | 'dark' | 'auto') => {
|
||||
const handleThemeSelect = (themeKey: 'light' | 'dark') => {
|
||||
setTheme(themeKey);
|
||||
setIsDropdownOpen(false);
|
||||
};
|
||||
|
||||
// Button variant - cycles through themes
|
||||
// Button variant - toggles between light and dark
|
||||
if (variant === 'button') {
|
||||
return (
|
||||
<Button
|
||||
@@ -209,14 +208,14 @@ export const ThemeToggle: React.FC<ThemeToggleProps> = ({
|
||||
'w-full px-4 py-2 text-left text-sm flex items-center gap-3',
|
||||
'hover:bg-[var(--bg-secondary)] transition-colors',
|
||||
'focus:bg-[var(--bg-secondary)] focus:outline-none',
|
||||
theme === key && 'bg-[var(--bg-secondary)] text-[var(--color-primary)]'
|
||||
displayTheme === key && 'bg-[var(--bg-secondary)] text-[var(--color-primary)]'
|
||||
)}
|
||||
role="menuitem"
|
||||
aria-label={`Cambiar a tema ${label.toLowerCase()}`}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{label}
|
||||
{theme === key && (
|
||||
{displayTheme === key && (
|
||||
<div className="ml-auto w-2 h-2 bg-[var(--color-primary)] rounded-full" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user