import type { OrganizationSubtype } from '@/schemas/organizationSubtype.ts'; import { Banknote, Briefcase, Building, Building2, Car, Church, Circle, Construction, Cpu, Factory, GraduationCap, Heart, Hotel, Scissors, ShoppingBag, Theater, Truck, UtensilsCrossed, Zap, } from 'lucide-react'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; /** * Database subtypes (actual values from database) * These are the granular subtypes stored in the database */ export type DatabaseSubtype = | 'retail' | 'healthcare' | 'educational' | 'personal_services' | 'food_beverage' | 'automotive' | 'religious' | 'professional_services' | 'energy' | 'financial' | 'government' | 'manufacturing' | 'cultural' | 'hospitality' | 'transportation' | 'infrastructure' | 'technology' | 'other'; /** * Get the appropriate Lucide icon component for an organization subtype */ function getLucideIconForSubtype( subtype: string ): React.ComponentType<{ size?: number; color?: string }> { const subtypeLower = subtype.toLowerCase().trim(); // Map database subtypes to Lucide icons switch (subtypeLower) { case 'retail': return ShoppingBag; case 'food_beverage': return UtensilsCrossed; case 'automotive': return Car; case 'personal_services': return Scissors; case 'professional_services': return Briefcase; case 'financial': return Banknote; case 'manufacturing': return Factory; case 'hospitality': return Hotel; case 'transportation': return Truck; case 'energy': return Zap; case 'technology': return Cpu; case 'commercial': return Building2; case 'cultural': return Theater; case 'government': return Building; case 'religious': return Church; case 'educational': return GraduationCap; case 'infrastructure': return Construction; case 'healthcare': return Heart; case 'other': default: return Circle; } } /** * Get organization icon SVG as string (for use in Leaflet DivIcon) * Uses Lucide icons rendered to HTML strings */ export function getOrganizationIconSvg( subtype: OrganizationSubtype | DatabaseSubtype | string | undefined | null, size: number, iconColor: string, backgroundColor: string ): string { // Debug logging in development if (process.env.NODE_ENV === 'development') { console.log('[organizationIcons] getOrganizationIconSvg called with:', { subtype, size, iconColor, backgroundColor, sizeType: typeof size, iconColorType: typeof iconColor, backgroundColorType: typeof backgroundColor, }); } // Ensure all parameters are valid with proper type checking const safeSize = Math.max(1, Number.isFinite(size) && size > 0 ? size : 24); // Increase icon size to 65% of marker size for better visibility (was 50%) const safeIconSize = Math.round(safeSize * 0.65); const subtypeValue = (subtype || '').toLowerCase().trim(); // Ensure colors are never undefined and are valid strings const safeBackgroundColor = typeof backgroundColor === 'string' && backgroundColor.trim() ? backgroundColor : '#6b7280'; /** * Calculate a contrasting color for the icon based on background brightness * Returns a color that will stand out against the background */ function getContrastingIconColor(bgColor: string): string { // Parse hex color const hex = bgColor.replace('#', ''); if (hex.length !== 6) { // If invalid color, return white as fallback return '#ffffff'; } // Convert to RGB const r = parseInt(hex.substr(0, 2), 16); const g = parseInt(hex.substr(2, 2), 16); const b = parseInt(hex.substr(4, 2), 16); // Calculate brightness (luminance) const brightness = (r * 299 + g * 587 + b * 114) / 1000; // If background is light, use dark contrasting color // If background is dark, use light contrasting color if (brightness > 128) { // Light background - use darker, more saturated color for contrast // Darken and saturate the background color const darkerR = Math.max(0, Math.min(255, r * 0.4)); const darkerG = Math.max(0, Math.min(255, g * 0.4)); const darkerB = Math.max(0, Math.min(255, b * 0.4)); return `rgb(${Math.round(darkerR)}, ${Math.round(darkerG)}, ${Math.round(darkerB)})`; } else { // Dark background - use lighter, contrasting color // Lighten the background color or use a complementary light color const lighterR = Math.min(255, r + (255 - r) * 0.7); const lighterG = Math.min(255, g + (255 - g) * 0.7); const lighterB = Math.min(255, b + (255 - b) * 0.7); return `rgb(${Math.round(lighterR)}, ${Math.round(lighterG)}, ${Math.round(lighterB)})`; } } // Calculate contrasting icon color based on background const contrastingIconColor = getContrastingIconColor(safeBackgroundColor); // Get the appropriate Lucide icon component const IconComponent = getLucideIconForSubtype(subtypeValue); // Render the icon to HTML string using renderToStaticMarkup (appropriate for Leaflet markers) // Use contrasting color for better visibility against the background const iconHtml = renderToStaticMarkup( React.createElement(IconComponent, { size: safeIconSize, color: contrastingIconColor, // Use contrasting color instead of plain white strokeWidth: 2.5, // Slightly thicker for better visibility }) ); // Return the icon SVG directly (the wrapper div in iconCache.ts will handle centering and background) return iconHtml; }