Initial commit - SaveTheMoment Atlas Basis-Setup
This commit is contained in:
366
components/LocationsManager.tsx
Normal file
366
components/LocationsManager.tsx
Normal file
@@ -0,0 +1,366 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { FiMapPin, FiMail, FiSettings, FiCheck, FiX, FiRefreshCw } from 'react-icons/fi';
|
||||
import { formatDateTime } from '@/lib/date-utils';
|
||||
|
||||
interface LocationsManagerProps {
|
||||
locations: any[];
|
||||
user: any;
|
||||
}
|
||||
|
||||
export default function LocationsManager({ locations, user }: LocationsManagerProps) {
|
||||
const [selectedLocation, setSelectedLocation] = useState<any>(null);
|
||||
const [showEmailSettings, setShowEmailSettings] = useState(false);
|
||||
const [syncingLocation, setSyncingLocation] = useState<string | null>(null);
|
||||
|
||||
const handleManualSync = async (locationId: string) => {
|
||||
setSyncingLocation(locationId);
|
||||
try {
|
||||
const res = await fetch('/api/email-sync', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ locationId }),
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
alert(`Sync erfolgreich! ${data.newEmails} neue E-Mails, ${data.newBookings} neue Buchungen`);
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(`Fehler beim Sync: ${data.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Sync');
|
||||
} finally {
|
||||
setSyncingLocation(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-3xl font-bold text-white">Standortverwaltung</h2>
|
||||
<p className="text-gray-400 mt-1">E-Mail-Konfiguration und Standort-Einstellungen</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{locations.map(location => (
|
||||
<div key={location.id} className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-xl shadow-sm p-6">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white">{location.name}</h3>
|
||||
<p className="text-sm text-gray-400">{location.city}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{location.emailSyncEnabled ? (
|
||||
<span className="flex items-center gap-1 px-3 py-1 bg-green-500/20 text-green-400 border border-green-500/50 text-xs font-semibold rounded-full">
|
||||
<FiCheck /> E-Mail aktiv
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-1 px-3 py-1 bg-gray-500/20 text-gray-400 border border-gray-500/50 text-xs font-semibold rounded-full">
|
||||
<FiX /> E-Mail inaktiv
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 mb-4">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<FiMapPin className="text-gray-400" />
|
||||
<span className="text-gray-400">Website:</span>
|
||||
<a href={location.websiteUrl} target="_blank" rel="noopener noreferrer" className="text-red-400 hover:text-red-300 hover:underline transition-colors">
|
||||
{location.websiteUrl}
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<FiMail className="text-gray-400" />
|
||||
<span className="text-gray-400">E-Mail:</span>
|
||||
<span className="text-white">{location.contactEmail}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 mb-4 pt-4 border-t border-gray-700">
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">Fotoboxen</p>
|
||||
<p className="text-2xl font-bold text-white">{location._count.photoboxes}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">Buchungen</p>
|
||||
<p className="text-2xl font-bold text-white">{location._count.bookings}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{location.lastEmailSync && (
|
||||
<div className="text-sm text-gray-400 mb-4">
|
||||
Letzter Sync: {formatDateTime(location.lastEmailSync)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3">
|
||||
{location.emailSyncEnabled && (
|
||||
<button
|
||||
onClick={() => handleManualSync(location.id)}
|
||||
disabled={syncingLocation === location.id}
|
||||
className="flex-1 flex items-center justify-center gap-2 px-4 py-2 bg-gradient-to-r from-red-600 to-pink-600 text-white rounded-lg hover:from-red-700 hover:to-pink-700 transition-all shadow-lg disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<FiRefreshCw className={syncingLocation === location.id ? 'animate-spin' : ''} />
|
||||
{syncingLocation === location.id ? 'Sync läuft...' : 'E-Mails abrufen'}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setSelectedLocation(location);
|
||||
setShowEmailSettings(true);
|
||||
}}
|
||||
className={`${location.emailSyncEnabled ? 'flex-1' : 'w-full'} flex items-center justify-center gap-2 px-4 py-2 bg-gray-700 text-gray-300 rounded-lg hover:bg-gray-600 transition-colors`}
|
||||
>
|
||||
<FiSettings /> E-Mail-Einstellungen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showEmailSettings && selectedLocation && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center p-4 z-50">
|
||||
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border border-gray-700 rounded-2xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="p-6 border-b border-gray-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-2xl font-bold text-white">
|
||||
E-Mail-Einstellungen: {selectedLocation.name}
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowEmailSettings(false);
|
||||
setSelectedLocation(null);
|
||||
}}
|
||||
className="p-2 hover:bg-gray-700 rounded-lg transition-colors"
|
||||
>
|
||||
<FiX className="text-xl text-gray-300" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/locations/${selectedLocation.id}/email-settings`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
imapHost: formData.get('imapHost'),
|
||||
imapPort: parseInt(formData.get('imapPort') as string),
|
||||
imapUser: formData.get('imapUser'),
|
||||
imapPassword: formData.get('imapPassword'),
|
||||
imapSecure: formData.get('imapSecure') === 'on',
|
||||
smtpHost: formData.get('smtpHost'),
|
||||
smtpPort: parseInt(formData.get('smtpPort') as string),
|
||||
smtpUser: formData.get('smtpUser'),
|
||||
smtpPassword: formData.get('smtpPassword'),
|
||||
smtpSecure: formData.get('smtpSecure') === 'on',
|
||||
emailSyncEnabled: formData.get('emailSyncEnabled') === 'on',
|
||||
}),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
alert('Einstellungen gespeichert!');
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Fehler beim Speichern');
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Fehler beim Speichern');
|
||||
}
|
||||
}}
|
||||
className="space-y-6"
|
||||
>
|
||||
<div className="bg-blue-500/20 border border-blue-500/50 rounded-lg p-4">
|
||||
<p className="text-sm text-blue-300">
|
||||
<strong>Wichtig:</strong> Die E-Mail-Zugangsdaten werden verschlüsselt gespeichert.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="emailSyncEnabled"
|
||||
defaultChecked={selectedLocation.emailSyncEnabled}
|
||||
className="w-4 h-4 text-red-600 rounded focus:ring-red-500"
|
||||
/>
|
||||
<span className="font-semibold text-white">E-Mail-Sync aktivieren</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-700 pt-6">
|
||||
<h4 className="font-semibold text-white mb-4">IMAP-Einstellungen (Empfang)</h4>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
IMAP-Server
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="imapHost"
|
||||
defaultValue={selectedLocation.imapHost || ''}
|
||||
placeholder="imap.example.com"
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
Port
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="imapPort"
|
||||
defaultValue={selectedLocation.imapPort || 993}
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end">
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="imapSecure"
|
||||
defaultChecked={selectedLocation.imapSecure ?? true}
|
||||
className="w-4 h-4 text-red-600 rounded focus:ring-red-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-300">SSL/TLS</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
Benutzername
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="imapUser"
|
||||
defaultValue={selectedLocation.imapUser || ''}
|
||||
placeholder={selectedLocation.contactEmail}
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
Passwort
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="imapPassword"
|
||||
defaultValue={selectedLocation.imapPassword || ''}
|
||||
placeholder="••••••••"
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-700 pt-6">
|
||||
<h4 className="font-semibold text-white mb-4">SMTP-Einstellungen (Versand)</h4>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
SMTP-Server
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="smtpHost"
|
||||
defaultValue={selectedLocation.smtpHost || ''}
|
||||
placeholder="smtp.example.com"
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
Port
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="smtpPort"
|
||||
defaultValue={selectedLocation.smtpPort || 465}
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end">
|
||||
<label className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="smtpSecure"
|
||||
defaultChecked={selectedLocation.smtpSecure ?? true}
|
||||
className="w-4 h-4 text-red-600 rounded focus:ring-red-500"
|
||||
/>
|
||||
<span className="text-sm text-gray-300">SSL/TLS</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
Benutzername
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="smtpUser"
|
||||
defaultValue={selectedLocation.smtpUser || ''}
|
||||
placeholder={selectedLocation.contactEmail}
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2">
|
||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
||||
Passwort
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
name="smtpPassword"
|
||||
defaultValue={selectedLocation.smtpPassword || ''}
|
||||
placeholder="••••••••"
|
||||
className="w-full px-4 py-2 bg-gray-700 border border-gray-600 text-white rounded-lg focus:ring-2 focus:ring-red-500 focus:border-transparent placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 pt-6 border-t border-gray-700">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShowEmailSettings(false);
|
||||
setSelectedLocation(null);
|
||||
}}
|
||||
className="flex-1 px-4 py-2 bg-gray-700 text-gray-300 rounded-lg hover:bg-gray-600 transition-colors"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex-1 px-4 py-2 bg-gradient-to-r from-red-600 to-pink-600 text-white rounded-lg hover:from-red-700 hover:to-pink-700 transition-all shadow-lg"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user