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:
@@ -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',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user