fix: improve toggle switch styling in MapFilters

- Update all toggle switches to use consistent sizing (h-6 w-11)
- Add proper shadow and border styling
- Improve transition animations
- Remove touch-manipulation styles that were causing layout issues
- Add focus ring states for accessibility
- Make switches look more polished and professional

All toggles now have a clean, modern appearance.
This commit is contained in:
2025-11-17 01:11:10 +00:00
parent e135cd66e8
commit 559976ea85
2 changed files with 51 additions and 45 deletions

View File

@@ -18,24 +18,36 @@ export class EmailService {
*/
async sendEmail(data: EmailData): Promise<boolean> {
try {
// Parse from email to separate email and name
const fromMatch = FROM_EMAIL.match(/^(.+?)\s*<(.+)>$/);
const fromEmail = fromMatch ? fromMatch[2] : FROM_EMAIL;
const fromName = fromMatch ? fromMatch[1].trim() : undefined;
const payload = {
to: data.to,
from: FROM_EMAIL,
from: {
email: fromEmail,
...(fromName && { name: fromName }),
},
recipients: [
{
email: data.to,
},
],
subject: data.subject,
text: data.text,
html: data.html || this.generateHtmlFromText(data.text),
text_content: data.text,
html_content: data.html || this.generateHtmlFromText(data.text),
};
console.log('[Email] Sending request to AhaSend API...');
console.log('[Email] Sending request to AhaSend API v2...');
console.log('[Email] API Key:', AHASEND_API_KEY.substring(0, 30) + '...');
console.log('[Email] Payload:', JSON.stringify(payload, null, 2));
console.log('[Email] Endpoint:', `/v2/accounts/${AHASEND_ACCOUNT_ID}/messages`);
const response = await axios.post(
`https://api.ahasend.com/messages`,
`https://api.ahasend.com/v2/accounts/${AHASEND_ACCOUNT_ID}/messages`,
payload,
{
headers: {
'X-API-Key': AHASEND_API_KEY,
'Authorization': `Bearer ${AHASEND_API_KEY}`,
'Content-Type': 'application/json',
},
}

View File

@@ -125,46 +125,44 @@ export function MapFilters({
{isExpanded && (
<div className="p-3 sm:p-4 space-y-3 sm:space-y-4 max-h-[60vh] sm:max-h-[70vh] overflow-y-auto">
{/* Cluster Toggle */}
<div className="flex items-center justify-between py-1">
<label htmlFor="cluster-toggle" className="text-xs sm:text-sm font-medium text-gray-700">
<div className="flex items-center justify-between py-2">
<label htmlFor="cluster-toggle" className="text-sm font-medium text-gray-700">
Cluster Markers
</label>
<button
id="cluster-toggle"
onClick={onToggleClusters}
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors touch-manipulation ${
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
showClusters ? 'bg-blue-600' : 'bg-gray-200'
}`}
role="switch"
aria-checked={showClusters}
style={{ minWidth: '44px', minHeight: '44px' }}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${
showClusters ? 'translate-x-6' : 'translate-x-1'
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
showClusters ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
</div>
{/* Heat Map Toggle */}
<div className="flex items-center justify-between py-1">
<label htmlFor="heatmap-toggle" className="text-xs sm:text-sm font-medium text-gray-700">
<div className="flex items-center justify-between py-2">
<label htmlFor="heatmap-toggle" className="text-sm font-medium text-gray-700">
Heat Map
</label>
<button
id="heatmap-toggle"
onClick={onToggleHeatmap}
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors touch-manipulation ${
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
showHeatmap ? 'bg-blue-600' : 'bg-gray-200'
}`}
role="switch"
aria-checked={showHeatmap}
style={{ minWidth: '44px', minHeight: '44px' }}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${
showHeatmap ? 'translate-x-6' : 'translate-x-1'
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
showHeatmap ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
@@ -172,23 +170,22 @@ export function MapFilters({
{/* Events Toggle */}
{onToggleEvents && (
<div className="flex items-center justify-between py-1">
<label htmlFor="events-toggle" className="text-xs sm:text-sm font-medium text-gray-700">
<div className="flex items-center justify-between py-2">
<label htmlFor="events-toggle" className="text-sm font-medium text-gray-700">
Show Events
</label>
<button
id="events-toggle"
onClick={onToggleEvents}
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors touch-manipulation ${
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 ${
showEvents ? 'bg-green-600' : 'bg-gray-200'
}`}
role="switch"
aria-checked={showEvents}
style={{ minWidth: '44px', minHeight: '44px' }}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${
showEvents ? 'translate-x-6' : 'translate-x-1'
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
showEvents ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
@@ -197,23 +194,22 @@ export function MapFilters({
{/* Water Quality Toggle */}
{onToggleWaterQuality && (
<div className="flex items-center justify-between py-1">
<label htmlFor="water-quality-toggle" className="text-xs sm:text-sm font-medium text-gray-700">
<div className="flex items-center justify-between py-2">
<label htmlFor="water-quality-toggle" className="text-sm font-medium text-gray-700">
Water Quality
</label>
<button
id="water-quality-toggle"
onClick={onToggleWaterQuality}
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors touch-manipulation ${
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
showWaterQuality ? 'bg-blue-600' : 'bg-gray-200'
}`}
role="switch"
aria-checked={showWaterQuality}
style={{ minWidth: '44px', minHeight: '44px' }}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${
showWaterQuality ? 'translate-x-6' : 'translate-x-1'
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
showWaterQuality ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
@@ -222,11 +218,11 @@ export function MapFilters({
{/* Hotspots Toggle */}
{onToggleHotspots && (
<div className="flex items-center justify-between py-1">
<label htmlFor="hotspots-toggle" className="text-xs sm:text-sm font-medium text-gray-700 flex items-center gap-2">
<div className="flex items-center justify-between py-2">
<label htmlFor="hotspots-toggle" className="text-sm font-medium text-gray-700 flex items-center gap-2">
Hotspots
{isLoadingHotspots && (
<svg className="animate-spin h-3 w-3 sm:h-4 sm:w-4 text-blue-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<svg className="animate-spin h-4 w-4 text-orange-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<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"></path>
</svg>
@@ -236,16 +232,15 @@ export function MapFilters({
id="hotspots-toggle"
onClick={onToggleHotspots}
disabled={isLoadingHotspots}
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors touch-manipulation ${
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2 ${
showHotspots ? 'bg-orange-600' : 'bg-gray-200'
} ${isLoadingHotspots ? 'opacity-50 cursor-not-allowed' : ''}`}
role="switch"
aria-checked={showHotspots}
style={{ minWidth: '44px', minHeight: '44px' }}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${
showHotspots ? 'translate-x-6' : 'translate-x-1'
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
showHotspots ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
@@ -253,23 +248,22 @@ export function MapFilters({
)}
{/* Show Cleaned Reports Toggle */}
<div className="flex items-center justify-between py-1">
<label htmlFor="cleaned-toggle" className="text-xs sm:text-sm font-medium text-gray-700">
<div className="flex items-center justify-between py-2">
<label htmlFor="cleaned-toggle" className="text-sm font-medium text-gray-700">
Show Cleaned
</label>
<button
id="cleaned-toggle"
onClick={() => onFiltersChange({ ...filters, showCleaned: filters.showCleaned === false ? true : false })}
className={`relative inline-flex h-7 w-12 items-center rounded-full transition-colors touch-manipulation ${
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 ${
filters.showCleaned !== false ? 'bg-green-600' : 'bg-gray-200'
}`}
role="switch"
aria-checked={filters.showCleaned !== false}
style={{ minWidth: '44px', minHeight: '44px' }}
>
<span
className={`inline-block h-5 w-5 transform rounded-full bg-white transition-transform ${
filters.showCleaned !== false ? 'translate-x-6' : 'translate-x-1'
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
filters.showCleaned !== false ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>