fix: URL kopieren button with toast popup instead of emoji
Build & Push Docker Image / build (push) Successful in 13s

This commit is contained in:
2026-05-11 19:43:48 +02:00
parent 715b61ce58
commit 2c08046fb3
5 changed files with 53 additions and 9 deletions
+6 -1
View File
@@ -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: {
+31
View File
@@ -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>
);
}
+7 -3
View File
@@ -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>
);
}
+8 -4
View File
@@ -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
View File
@@ -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"}