MVP
This commit is contained in:
@@ -9,6 +9,7 @@ import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useCsrf } from '@/hooks/useCsrf';
|
||||
import { showToast } from '@/components/ui/Toast';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/Dialog';
|
||||
|
||||
@@ -22,17 +23,20 @@ interface QRCodeData {
|
||||
status: 'ACTIVE' | 'PAUSED';
|
||||
createdAt: string;
|
||||
scans: number;
|
||||
style?: any;
|
||||
}
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const { fetchWithCsrf } = useCsrf();
|
||||
const [qrCodes, setQrCodes] = useState<QRCodeData[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [userPlan, setUserPlan] = useState<string>('FREE');
|
||||
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
|
||||
const [upgradedPlan, setUpgradedPlan] = useState<string>('');
|
||||
const [deletingAll, setDeletingAll] = useState(false);
|
||||
const [stats, setStats] = useState({
|
||||
totalScans: 0,
|
||||
activeQRCodes: 0,
|
||||
@@ -116,7 +120,7 @@ export default function DashboardPage() {
|
||||
slug: 'dynamische-vs-statische-qr-codes',
|
||||
},
|
||||
{
|
||||
title: 'QR-Code Marketing-Strategien für 2024',
|
||||
title: 'QR-Code Marketing-Strategien für 2025',
|
||||
excerpt: 'Die besten Marketing-Strategien mit QR-Codes für Ihr Unternehmen...',
|
||||
readTime: '7 Min',
|
||||
slug: 'qr-code-marketing-strategien',
|
||||
@@ -205,19 +209,98 @@ export default function DashboardPage() {
|
||||
}, []);
|
||||
|
||||
const handleEdit = (id: string) => {
|
||||
console.log('Edit QR:', id);
|
||||
// Redirect to edit page
|
||||
router.push(`/qr/${id}/edit`);
|
||||
};
|
||||
|
||||
const handleDuplicate = (id: string) => {
|
||||
console.log('Duplicate QR:', id);
|
||||
const handlePause = async (id: string) => {
|
||||
try {
|
||||
const qr = qrCodes.find(q => q.id === id);
|
||||
if (!qr) return;
|
||||
|
||||
const newStatus = qr.status === 'ACTIVE' ? 'PAUSED' : 'ACTIVE';
|
||||
|
||||
const response = await fetch(`/api/qrs/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ status: newStatus }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Update local state
|
||||
setQrCodes(qrCodes.map(q =>
|
||||
q.id === id ? { ...q, status: newStatus } : q
|
||||
));
|
||||
showToast(`QR code ${newStatus === 'ACTIVE' ? 'resumed' : 'paused'}!`, 'success');
|
||||
} else {
|
||||
throw new Error('Failed to update status');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating QR status:', error);
|
||||
showToast('Failed to update QR code status', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const handlePause = (id: string) => {
|
||||
console.log('Pause QR:', id);
|
||||
const handleDelete = async (id: string) => {
|
||||
if (!confirm('Are you sure you want to delete this QR code? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/qrs/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Remove from local state
|
||||
setQrCodes(qrCodes.filter(q => q.id !== id));
|
||||
showToast('QR code deleted successfully!', 'success');
|
||||
} else {
|
||||
throw new Error('Failed to delete');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting QR:', error);
|
||||
showToast('Failed to delete QR code', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
console.log('Delete QR:', id);
|
||||
const handleDeleteAll = async () => {
|
||||
if (!confirm('Are you sure you want to delete ALL QR codes? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Double confirmation
|
||||
if (!confirm('This will permanently delete ALL your QR codes. Are you absolutely sure?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDeletingAll(true);
|
||||
|
||||
try {
|
||||
const response = await fetchWithCsrf('/api/qrs/delete-all', {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setQrCodes([]);
|
||||
setStats({
|
||||
totalScans: 0,
|
||||
activeQRCodes: 0,
|
||||
conversionRate: 0,
|
||||
});
|
||||
showToast(`Successfully deleted ${data.deletedCount} QR code${data.deletedCount !== 1 ? 's' : ''}`, 'success');
|
||||
} else {
|
||||
throw new Error('Failed to delete all QR codes');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting all QR codes:', error);
|
||||
showToast('Failed to delete QR codes', 'error');
|
||||
} finally {
|
||||
setDeletingAll(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getPlanBadgeColor = (plan: string) => {
|
||||
@@ -263,9 +346,21 @@ export default function DashboardPage() {
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-semibold text-gray-900">{t('dashboard.recent_codes')}</h2>
|
||||
<Link href="/create">
|
||||
<Button>Create New QR Code</Button>
|
||||
</Link>
|
||||
<div className="flex gap-3">
|
||||
{qrCodes.length > 0 && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleDeleteAll}
|
||||
disabled={deletingAll}
|
||||
className="border-red-600 text-red-600 hover:bg-red-50"
|
||||
>
|
||||
{deletingAll ? 'Deleting...' : 'Delete All'}
|
||||
</Button>
|
||||
)}
|
||||
<Link href="/create">
|
||||
<Button>Create New QR Code</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
@@ -292,7 +387,6 @@ export default function DashboardPage() {
|
||||
key={qr.id}
|
||||
qr={qr}
|
||||
onEdit={handleEdit}
|
||||
onDuplicate={handleDuplicate}
|
||||
onPause={handlePause}
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
@@ -351,11 +445,11 @@ export default function DashboardPage() {
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
<span>Branding (Logo, Farben anpassen)</span>
|
||||
<span>Branding (Farben anpassen)</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
<span>Detaillierte Analytics</span>
|
||||
<span>Detaillierte Analytics (Devices, Locations, Time-Series)</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
@@ -363,7 +457,7 @@ export default function DashboardPage() {
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
<span>Passwortschutz für QR-Codes</span>
|
||||
<span>SVG/PNG Download</span>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
@@ -375,15 +469,11 @@ export default function DashboardPage() {
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
<span>Team-Zugänge (bis zu 3 User)</span>
|
||||
<span>Alles von Pro</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
<span>Benutzerdefinierte Domains</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
<span>White-Label</span>
|
||||
<span>Bulk QR-Generierung (bis 1,000)</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-green-600 mr-2">✓</span>
|
||||
|
||||
Reference in New Issue
Block a user