Files
smoothschedule/frontend/src/components/DevQuickLogin.tsx
poduck 89fa8f81af Fix: Display correct SmoothSchedule logo in email preview
Replaced the blank base64 encoded logo with the actual SmoothSchedule logo in the email rendering pipeline.

A Playwright E2E test was run to verify that the logo is correctly displayed in the email preview modal, ensuring it loads with natural dimensions and is visible.
2025-12-14 19:10:56 -05:00

240 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import apiClient from '../api/client';
import { setCookie } from '../utils/cookies';
import { useQueryClient } from '@tanstack/react-query';
import { getBaseDomain, buildSubdomainUrl } from '../utils/domain';
export interface TestUser {
email: string;
password: string;
role: string;
label: string;
color: string;
category: 'platform' | 'business' | 'customer';
}
const testUsers: TestUser[] = [
{
email: 'superuser@platform.com',
password: 'test123',
role: 'SUPERUSER',
label: 'Platform Superuser',
color: 'bg-purple-600 hover:bg-purple-700',
category: 'platform',
},
{
email: 'manager@platform.com',
password: 'test123',
role: 'PLATFORM_MANAGER',
label: 'Platform Manager',
color: 'bg-blue-600 hover:bg-blue-700',
category: 'platform',
},
{
email: 'sales@platform.com',
password: 'test123',
role: 'PLATFORM_SALES',
label: 'Platform Sales',
color: 'bg-green-600 hover:bg-green-700',
category: 'platform',
},
{
email: 'support@platform.com',
password: 'test123',
role: 'PLATFORM_SUPPORT',
label: 'Platform Support',
color: 'bg-yellow-600 hover:bg-yellow-700',
category: 'platform',
},
{
email: 'owner@demo.com',
password: 'test123',
role: 'TENANT_OWNER',
label: 'Business Owner',
color: 'bg-indigo-600 hover:bg-indigo-700',
category: 'business',
},
{
email: 'manager@demo.com',
password: 'test123',
role: 'TENANT_MANAGER',
label: 'Business Manager',
color: 'bg-pink-600 hover:bg-pink-700',
category: 'business',
},
{
email: 'staff@demo.com',
password: 'test123',
role: 'TENANT_STAFF',
label: 'Staff Member',
color: 'bg-teal-600 hover:bg-teal-700',
category: 'business',
},
{
email: 'customer@demo.com',
password: 'test123',
role: 'CUSTOMER',
label: 'Customer',
color: 'bg-orange-600 hover:bg-orange-700',
category: 'customer',
},
];
type UserFilter = 'all' | 'platform' | 'business';
interface DevQuickLoginProps {
embedded?: boolean;
filter?: UserFilter;
}
export function DevQuickLogin({ embedded = false, filter = 'all' }: DevQuickLoginProps) {
const queryClient = useQueryClient();
const [loading, setLoading] = useState<string | null>(null);
const [isMinimized, setIsMinimized] = useState(false);
// Only show in development
if (import.meta.env.PROD) {
return null;
}
// Filter users based on the filter prop
const filteredUsers = testUsers.filter((user) => {
if (filter === 'all') return true;
if (filter === 'platform') return user.category === 'platform';
if (filter === 'business') return user.category === 'business';
return true;
});
const handleQuickLogin = async (user: TestUser) => {
setLoading(user.email);
try {
// Call custom login API that supports email login
const response = await apiClient.post('/auth/login/', {
email: user.email,
password: user.password,
});
// Store token in cookie (use 'access_token' to match what client.ts expects)
setCookie('access_token', response.data.access, 7);
// Clear any existing masquerade stack - this is a fresh login
localStorage.removeItem('masquerade_stack');
// Fetch user data to determine redirect
const userResponse = await apiClient.get('/auth/me/');
const userData = userResponse.data;
// Determine the correct subdomain based on user role
const currentHostname = window.location.hostname;
const currentPort = window.location.port;
let targetSubdomain: string | null = null;
// Platform users (superuser, platform_manager, platform_support)
if (['superuser', 'platform_manager', 'platform_support'].includes(userData.role)) {
targetSubdomain = 'platform';
}
// Business users - redirect to their business subdomain
else if (userData.business_subdomain) {
targetSubdomain = userData.business_subdomain;
}
// Check if we need to redirect to a different subdomain
const baseDomain = getBaseDomain();
const isOnTargetSubdomain = currentHostname === (targetSubdomain ? `${targetSubdomain}.${baseDomain}` : baseDomain);
const needsRedirect = targetSubdomain && !isOnTargetSubdomain;
if (needsRedirect) {
// Redirect to the correct subdomain
window.location.href = buildSubdomainUrl(targetSubdomain, '/dashboard');
return;
}
// Already on correct subdomain - navigate to dashboard
window.location.href = '/dashboard';
} catch (error: any) {
console.error('Quick login failed:', error);
alert(`Failed to login as ${user.label}: ${error.message || 'Unknown error'}`);
} finally {
setLoading(null);
}
};
if (!embedded && isMinimized) {
return (
<div className="fixed bottom-4 right-4 z-50">
<button
onClick={() => setIsMinimized(false)}
className="bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg hover:bg-gray-700 transition-colors"
>
🔓 Quick Login
</button>
</div>
);
}
const containerClasses = embedded
? "w-full bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4 mt-6"
: "fixed bottom-4 right-4 z-50 bg-white rounded-lg shadow-2xl border-2 border-gray-300 p-4 max-w-md";
return (
<div className={containerClasses}>
<div className="flex items-center justify-between mb-3">
<h3 className="font-bold text-gray-800 dark:text-white flex items-center gap-2">
<span>🔓</span>
<span>Quick Login (Dev Only)</span>
</h3>
{!embedded && (
<button
onClick={() => setIsMinimized(true)}
className="text-gray-500 hover:text-gray-700 text-xl leading-none"
>
×
</button>
)}
</div>
<div className="grid grid-cols-2 gap-2">
{filteredUsers.map((user) => (
<button
key={user.email}
onClick={() => handleQuickLogin(user)}
disabled={loading !== null}
className={`${user.color} text-white px-3 py-2 rounded text-sm font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed`}
>
{loading === user.email ? (
<span className="flex items-center justify-center">
<svg className="animate-spin h-4 w-4 mr-2" viewBox="0 0 24 24">
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
fill="none"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
Logging in...
</span>
) : (
<div className="text-left">
<div className="font-semibold">{user.label}</div>
<div className="text-xs opacity-90">{user.role}</div>
</div>
)}
</button>
))}
</div>
<div className="mt-3 text-xs text-gray-500 dark:text-gray-400 text-center">
Password for all: <code className="bg-gray-100 dark:bg-gray-700 px-1 rounded">test123</code>
</div>
</div>
);
}