feat: Add AGB PDF attachment to booking confirmation emails
- Extend email system to support file attachments - Add sendEmailWithAGB() function that automatically attaches AGB.pdf - Implement AGB PDF caching for better performance - Update booking confirmation email template with AGB notice - Add visual highlight box in HTML email with AGB information - Update email subject to indicate AGB attachment - Include AGB reference in both HTML and text versions - Ensure legal compliance by automatically sending terms with confirmations Changes: - email.ts: Add attachment support and AGB PDF integration - email-templates.ts: Add AGB notice to confirmation emails - bookings.ts: Use sendEmailWithAGB for confirmed bookings - German localization for admin treatments component
This commit is contained in:
@@ -9,8 +9,8 @@ export function AdminTreatments() {
|
||||
name: "",
|
||||
description: "",
|
||||
duration: 60,
|
||||
price: 5000, // $50.00 in cents
|
||||
category: "Manicure",
|
||||
price: 5000, // 50,00 € in Cent
|
||||
category: "Maniküre",
|
||||
});
|
||||
|
||||
const { data: treatments } = useQuery(
|
||||
@@ -29,7 +29,7 @@ export function AdminTreatments() {
|
||||
queryClient.treatments.remove.mutationOptions()
|
||||
);
|
||||
|
||||
const categories = ["Manicure", "Pedicure", "Nail Art", "Extensions", "Other"];
|
||||
const categories = ["Maniküre", "Pediküre", "Nageldesign", "Verlängerungen", "Sonstiges"];
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -61,7 +61,7 @@ export function AdminTreatments() {
|
||||
description: "",
|
||||
duration: 60,
|
||||
price: 5000,
|
||||
category: "Manicure",
|
||||
category: "Maniküre",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -86,26 +86,26 @@ export function AdminTreatments() {
|
||||
return (
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Manage Treatments</h2>
|
||||
<h2 className="text-2xl font-bold text-gray-900">Behandlungen verwalten</h2>
|
||||
<button
|
||||
onClick={() => setShowForm(true)}
|
||||
className="bg-pink-600 text-white px-4 py-2 rounded-md hover:bg-pink-700 font-medium"
|
||||
>
|
||||
Add Treatment
|
||||
Behandlung hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{showForm && (
|
||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{editingTreatment ? "Edit Treatment" : "Add New Treatment"}
|
||||
{editingTreatment ? "Behandlung bearbeiten" : "Neue Behandlung hinzufügen"}
|
||||
</h3>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Treatment Name *
|
||||
Behandlungsname *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -117,7 +117,7 @@ export function AdminTreatments() {
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Category *
|
||||
Kategorie *
|
||||
</label>
|
||||
<select
|
||||
value={formData.category}
|
||||
@@ -134,7 +134,7 @@ export function AdminTreatments() {
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Description *
|
||||
Beschreibung *
|
||||
</label>
|
||||
<textarea
|
||||
value={formData.description}
|
||||
@@ -148,7 +148,7 @@ export function AdminTreatments() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Duration (minutes) *
|
||||
Dauer (Minuten) *
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
@@ -163,7 +163,7 @@ export function AdminTreatments() {
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Price ($) *
|
||||
Preis (€) *
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
@@ -183,14 +183,14 @@ export function AdminTreatments() {
|
||||
disabled={isCreating || isUpdating}
|
||||
className="bg-pink-600 text-white px-4 py-2 rounded-md hover:bg-pink-700 disabled:opacity-50 font-medium"
|
||||
>
|
||||
{isCreating || isUpdating ? "Saving..." : (editingTreatment ? "Update" : "Create")}
|
||||
{isCreating || isUpdating ? "Speichern..." : (editingTreatment ? "Aktualisieren" : "Erstellen")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancel}
|
||||
className="bg-gray-300 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-400 font-medium"
|
||||
>
|
||||
Cancel
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -202,19 +202,19 @@ export function AdminTreatments() {
|
||||
<thead className="bg-gray-50">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Treatment
|
||||
Behandlung
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Category
|
||||
Kategorie
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Duration
|
||||
Dauer
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Price
|
||||
Preis
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Actions
|
||||
Aktionen
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -231,17 +231,17 @@ export function AdminTreatments() {
|
||||
{treatment.category}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
{treatment.duration} min
|
||||
{treatment.duration} Min
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
|
||||
${(treatment.price / 100).toFixed(2)}
|
||||
{(treatment.price / 100).toFixed(2)} €
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<button
|
||||
onClick={() => handleEdit(treatment)}
|
||||
className="text-pink-600 hover:text-pink-900"
|
||||
>
|
||||
Edit
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -251,7 +251,7 @@ export function AdminTreatments() {
|
||||
}}
|
||||
className="text-red-600 hover:text-red-900"
|
||||
>
|
||||
Delete
|
||||
Löschen
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
Reference in New Issue
Block a user