fix: URL kopieren button with toast popup instead of emoji
Build & Push Docker Image / build (push) Successful in 13s
Build & Push Docker Image / build (push) Successful in 13s
This commit is contained in:
+6
-1
@@ -7,7 +7,12 @@ export const auth = betterAuth({
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
trustedOrigins: [process.env.APP_URL || 'http://localhost:5173'],
|
||||
trustedOrigins: [
|
||||
process.env.APP_URL || 'http://localhost:5173',
|
||||
'http://localhost:5173',
|
||||
'http://localhost:5174',
|
||||
'http://localhost:*',
|
||||
],
|
||||
user: {
|
||||
additionalFields: {
|
||||
role: {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface ToastProps {
|
||||
message: string | null;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function Toast({ message, onClose }: ToastProps) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (message) {
|
||||
setVisible(true);
|
||||
const timer = setTimeout(() => {
|
||||
setVisible(false);
|
||||
onClose();
|
||||
}, 2500);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [message, onClose]);
|
||||
|
||||
if (!visible || !message) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-6 right-6 z-50 animate-fade-in">
|
||||
<div className="bg-slate-900 dark:bg-slate-800 text-white text-sm px-4 py-2.5 rounded-lg shadow-xl border border-white/10">
|
||||
{message}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { api } from '@/lib/api';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Toast } from '@/components/ui/Toast';
|
||||
import { fmtDE, fmtDE_timestamp } from '@/utils/format';
|
||||
|
||||
const filters = [
|
||||
@@ -15,6 +16,7 @@ const filters = [
|
||||
|
||||
export function BookingsPage() {
|
||||
const [filter, setFilter] = useState('');
|
||||
const [toastMsg, setToastMsg] = useState<string | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -84,13 +86,14 @@ export function BookingsPage() {
|
||||
<span className="text-sm font-medium text-slate-900 dark:text-white/90">{b.customer_name}</span>
|
||||
<Badge status={b.status} />
|
||||
<button
|
||||
className="text-slate-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer text-xs"
|
||||
className="text-xs text-slate-500 dark:text-white/40 hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer whitespace-nowrap"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(`${window.location.origin}/book/${b.token}`);
|
||||
setToastMsg('Buchungs-URL kopiert');
|
||||
}}
|
||||
title="Buchungs-Link kopieren"
|
||||
>🔗</button>
|
||||
title="Buchungs-URL kopieren"
|
||||
>URL kopieren</button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 dark:text-white/40 mt-0.5">{b.customer_email}</p>
|
||||
{(b.customer_company || b.customer_location) && (
|
||||
@@ -131,6 +134,7 @@ export function BookingsPage() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Toast message={toastMsg} onClose={() => setToastMsg(null)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { api } from '@/lib/api';
|
||||
@@ -6,6 +6,7 @@ import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Toast } from '@/components/ui/Toast';
|
||||
import { WeekCalendar } from '@/components/ui/WeekCalendar';
|
||||
import { SlotForm } from './SlotForm';
|
||||
import { CustomerHistoryModal } from './CustomerHistoryModal';
|
||||
@@ -41,6 +42,7 @@ export function SlotsPage() {
|
||||
const [blockDayDate, setBlockDayDate] = useState('');
|
||||
const [blockDayReason, setBlockDayReason] = useState('');
|
||||
const [highlightBookingId, setHighlightBookingId] = useState<number | null>(null);
|
||||
const [toastMsg, setToastMsg] = useState<string | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
@@ -225,13 +227,14 @@ export function SlotsPage() {
|
||||
<div className="flex items-center gap-1">
|
||||
<Badge status={b.status} />
|
||||
<button
|
||||
className="text-slate-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer text-[11px]"
|
||||
className="text-xs text-slate-500 dark:text-white/40 hover:text-blue-600 dark:hover:text-blue-400 transition-colors cursor-pointer whitespace-nowrap"
|
||||
onClick={() => {
|
||||
const link = `${window.location.origin}/book/${b.token}`;
|
||||
navigator.clipboard.writeText(link);
|
||||
setToastMsg('Buchungs-URL kopiert');
|
||||
}}
|
||||
title="Buchungs-Link kopieren"
|
||||
>🔗</button>
|
||||
title="Buchungs-URL kopieren"
|
||||
>URL kopieren</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-500 dark:text-white/40">{b.customer_company}</p>
|
||||
@@ -327,6 +330,7 @@ export function SlotsPage() {
|
||||
</Modal>
|
||||
|
||||
<CustomerHistoryModal email={historyEmail} onClose={() => setHistoryEmail(null)} />
|
||||
<Toast message={toastMsg} onClose={() => setToastMsg(null)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/ui/Badge.tsx","./src/components/ui/Button.tsx","./src/components/ui/Card.tsx","./src/components/ui/Input.tsx","./src/components/ui/Modal.tsx","./src/components/ui/WeekCalendar.tsx","./src/features/auth/LoginPage.tsx","./src/features/auth/ProtectedRoute.tsx","./src/features/bookings/BookingsPage.tsx","./src/features/customer/BookingConfirmation.tsx","./src/features/customer/BookingPage.tsx","./src/features/dashboard/DashboardLayout.tsx","./src/features/settings/SettingsPage.tsx","./src/features/slots/CustomerHistoryModal.tsx","./src/features/slots/SlotForm.tsx","./src/features/slots/SlotsPage.tsx","./src/lib/api.ts","./src/lib/auth-client.ts","./src/store/theme.ts","./src/types/index.ts","./src/utils/format.ts"],"version":"6.0.3"}
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/ui/Badge.tsx","./src/components/ui/Button.tsx","./src/components/ui/Card.tsx","./src/components/ui/Input.tsx","./src/components/ui/Modal.tsx","./src/components/ui/Toast.tsx","./src/components/ui/WeekCalendar.tsx","./src/features/auth/LoginPage.tsx","./src/features/auth/ProtectedRoute.tsx","./src/features/bookings/BookingsPage.tsx","./src/features/customer/BookingConfirmation.tsx","./src/features/customer/BookingPage.tsx","./src/features/dashboard/DashboardLayout.tsx","./src/features/settings/SettingsPage.tsx","./src/features/slots/CustomerHistoryModal.tsx","./src/features/slots/SlotForm.tsx","./src/features/slots/SlotsPage.tsx","./src/lib/api.ts","./src/lib/auth-client.ts","./src/store/theme.ts","./src/types/index.ts","./src/utils/format.ts"],"version":"6.0.3"}
|
||||
Reference in New Issue
Block a user