@titas_mallick/wedding-site-gen 1.0.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -184
- package/app/api/email-reminders/route.ts +240 -0
- package/app/couple/page.tsx +4 -4
- package/app/game/page.tsx +298 -0
- package/app/guestbook/page.tsx +270 -152
- package/app/invitation/[slug]/layout.tsx +4 -2
- package/app/invitation/[slug]/page.tsx +303 -84
- package/app/invitation/actions.ts +49 -0
- package/app/invitation/maker/auth.js +1 -1
- package/app/invitation/maker/guestAdder.js +4 -0
- package/app/invitation/maker/guestShower.js +39 -8
- package/app/invitation/maker/layout.tsx +1 -1
- package/app/invitation/maker/page.js +9 -7
- package/app/invitation/maker/rsvpViewer.js +90 -8
- package/app/layout.tsx +40 -14
- package/app/mark-the-dates/page.tsx +8 -2
- package/app/page.tsx +7 -1
- package/app/providers.tsx +1 -1
- package/app/sagun/page.tsx +224 -76
- package/app/song-requests/page.tsx +242 -105
- package/app/sukanya/page.tsx +9 -13
- package/app/titas/page.tsx +8 -24
- package/app/travel-guide/page.tsx +361 -120
- package/app/updates/maker/page.js +2 -2
- package/app/updates/overlay/page.tsx +65 -30
- package/app/updates/page.js +3 -3
- package/cli.mjs +49 -21
- package/components/AdminAuth.tsx +145 -0
- package/components/AdminLinks.tsx +120 -0
- package/components/ConciergeBot.tsx +104 -44
- package/components/CountdownTimer.tsx +37 -15
- package/components/Gallery.tsx +1 -1
- package/components/LiveVideos.tsx +27 -15
- package/components/OurStory.tsx +1 -1
- package/components/SchemaMarkup.tsx +74 -0
- package/components/certificate.jsx +287 -300
- package/components/footer.tsx +2 -0
- package/components/hero.tsx +47 -4
- package/components/icons.tsx +45 -0
- package/components/importantNews.js +168 -168
- package/components/navbar.tsx +113 -18
- package/components/updates.tsx +36 -26
- package/config/firebase-admin.js +14 -17
- package/config/firebase.ts +4 -2
- package/config/site.ts +10 -2
- package/firestore.rules +6 -1
- package/next-sitemap.config.js +21 -0
- package/package.json +4 -3
- package/public/corner1-01.svg +0 -0
- package/public/love-birds.png +0 -0
- package/public/next.svg +0 -0
- package/public/pubqr.png +0 -0
- package/public/pw/sample.jpg +0 -0
- package/public/qr.png +0 -0
- package/public/sample.jpg +0 -0
- package/public/vercel.svg +0 -0
- package/vercel.json +1 -0
- package/.recover +0 -9
- package/next-env.d.ts +0 -6
- package/public/DCV.gif +0 -0
- package/public/DCV2.gif +0 -0
- package/public/DCV3.gif +0 -0
- package/public/Images/1.jpg +0 -0
- package/public/Images/11.jpg +0 -0
- package/public/Images/12.jpg +0 -0
- package/public/Images/13.jpg +0 -0
- package/public/Images/14.jpg +0 -0
- package/public/Images/15.jpg +0 -0
- package/public/Images/16.jpg +0 -0
- package/public/Images/17.jpg +0 -0
- package/public/Images/18.jpg +0 -0
- package/public/Images/19.jpg +0 -0
- package/public/Images/2.jpg +0 -0
- package/public/Images/21.jpg +0 -0
- package/public/Images/22.jpg +0 -0
- package/public/Images/3.jpg +0 -0
- package/public/Images/4.jpg +0 -0
- package/public/Images/5.jpg +0 -0
- package/public/Images/6.jpg +0 -0
- package/public/Images/7.jpg +0 -0
- package/public/Images/8.jpg +0 -0
- package/public/Images/9.jpg +0 -0
- package/public/Images/9b.jpg +0 -0
- package/public/Images/Patipatra.jpeg +0 -0
- package/public/audio (1).mp3 +0 -0
- package/public/audio (2).mp3 +0 -0
- package/public/bride.jpg +0 -0
- package/public/groom.jpg +0 -0
- package/public/invite.png +0 -0
- package/public/pw/001.jpg +0 -0
- package/public/pw/002.jpg +0 -0
- package/public/pw/003.jpg +0 -0
- package/public/pw/004.jpg +0 -0
- package/public/pw/005.jpg +0 -0
- package/public/pw/006.jpg +0 -0
- package/public/pw/007.jpg +0 -0
- package/public/pw/008.jpg +0 -0
- package/public/pw/009.jpg +0 -0
- package/public/pw/010.jpg +0 -0
- package/public/pw/011.jpg +0 -0
- package/public/pw/012.jpg +0 -0
- package/public/pw/013.jpg +0 -0
- package/public/pw/014.jpg +0 -0
- package/public/pw/015.jpg +0 -0
- package/public/pw/016.jpg +0 -0
- package/public/pw/017.jpg +0 -0
- package/public/pw/018.jpg +0 -0
- package/public/pw/019.jpg +0 -0
- package/public/pw/020.jpg +0 -0
- package/public/pw/021.jpg +0 -0
- package/public/pw/022.jpg +0 -0
- package/public/pw/023.jpg +0 -0
- package/public/pw/024.jpg +0 -0
- package/public/pw/025.jpg +0 -0
- package/public/pw/026.jpg +0 -0
- package/public/pw/027.jpg +0 -0
- package/public/pw/028.jpg +0 -0
- package/public/pw/029.jpg +0 -0
- package/public/pw/030.jpg +0 -0
- package/public/pw/031.jpg +0 -0
- package/public/pw/032.jpg +0 -0
- package/tsconfig.tsbuildinfo +0 -1
- /package/public/Images/{20.jpg → sample.jpg} +0 -0
package/app/sagun/page.tsx
CHANGED
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import Image from "next/image";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardBody,
|
|
7
|
+
Button,
|
|
8
|
+
Snippet,
|
|
9
|
+
Input,
|
|
10
|
+
Textarea,
|
|
11
|
+
Divider,
|
|
12
|
+
Spinner,
|
|
13
|
+
Avatar,
|
|
14
|
+
addToast,
|
|
15
|
+
Modal,
|
|
16
|
+
ModalContent,
|
|
17
|
+
ModalHeader,
|
|
18
|
+
ModalBody,
|
|
19
|
+
ModalFooter,
|
|
20
|
+
useDisclosure,
|
|
21
|
+
} from "@heroui/react";
|
|
5
22
|
import { useEffect, useState } from "react";
|
|
6
|
-
import {
|
|
7
|
-
getFirestore,
|
|
8
|
-
collection,
|
|
9
|
-
addDoc,
|
|
10
|
-
onSnapshot,
|
|
11
|
-
query,
|
|
12
|
-
orderBy,
|
|
23
|
+
import {
|
|
24
|
+
getFirestore,
|
|
25
|
+
collection,
|
|
26
|
+
addDoc,
|
|
27
|
+
onSnapshot,
|
|
28
|
+
query,
|
|
29
|
+
orderBy,
|
|
13
30
|
serverTimestamp,
|
|
14
|
-
limit
|
|
31
|
+
limit,
|
|
32
|
+
deleteDoc,
|
|
33
|
+
doc,
|
|
15
34
|
} from "firebase/firestore";
|
|
35
|
+
import { getAuth, onAuthStateChanged, User } from "firebase/auth";
|
|
16
36
|
import { motion, AnimatePresence } from "framer-motion";
|
|
17
37
|
|
|
18
38
|
import firebaseApp from "@/config/firebase";
|
|
19
39
|
import { fontCursive, fontSans, fontMono } from "@/config/fonts";
|
|
20
|
-
import { HeartFilledIcon } from "@/components/icons";
|
|
40
|
+
import { HeartFilledIcon, TrashIcon } from "@/components/icons";
|
|
21
41
|
|
|
22
42
|
const db = getFirestore(firebaseApp());
|
|
23
43
|
|
|
@@ -25,26 +45,68 @@ export default function SagunPage() {
|
|
|
25
45
|
const [wishes, setWishes] = useState<any[]>([]);
|
|
26
46
|
const [loading, setLoading] = useState(true);
|
|
27
47
|
const [submitting, setSubmitting] = useState(false);
|
|
28
|
-
const [
|
|
48
|
+
const [user, setUser] = useState<User | null>(null);
|
|
49
|
+
const [formData, setFormData] = useState({
|
|
50
|
+
name: "",
|
|
51
|
+
message: "",
|
|
52
|
+
captcha: "",
|
|
53
|
+
});
|
|
29
54
|
const [sentiment, setSentiment] = useState<string>("");
|
|
55
|
+
const { isOpen, onOpen, onOpenChange } = useDisclosure();
|
|
56
|
+
const [wishToDelete, setWishToDelete] = useState<string | null>(null);
|
|
30
57
|
|
|
31
58
|
useEffect(() => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
59
|
+
const auth = getAuth(firebaseApp());
|
|
60
|
+
const unsubscribeAuth = onAuthStateChanged(auth, (currentUser) => {
|
|
61
|
+
setUser(currentUser);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const q = query(
|
|
65
|
+
collection(db, "wishes"),
|
|
66
|
+
orderBy("createdAt", "desc"),
|
|
67
|
+
limit(20),
|
|
68
|
+
);
|
|
69
|
+
const unsubscribeFirestore = onSnapshot(q, (snapshot) => {
|
|
70
|
+
const data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
|
|
71
|
+
|
|
35
72
|
setWishes(data);
|
|
36
73
|
setLoading(false);
|
|
37
|
-
|
|
74
|
+
|
|
38
75
|
if (data.length >= 3) {
|
|
39
76
|
generateSentimentSummary(data.slice(0, 10));
|
|
40
77
|
}
|
|
41
78
|
});
|
|
42
79
|
|
|
43
|
-
return () =>
|
|
80
|
+
return () => {
|
|
81
|
+
unsubscribeAuth();
|
|
82
|
+
unsubscribeFirestore();
|
|
83
|
+
};
|
|
44
84
|
}, []);
|
|
45
85
|
|
|
86
|
+
const handleDeleteWish = async () => {
|
|
87
|
+
if (!wishToDelete) return;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
await deleteDoc(doc(db, "wishes", wishToDelete));
|
|
91
|
+
addToast({
|
|
92
|
+
title: "Wish Deleted",
|
|
93
|
+
description: "The blessing has been removed.",
|
|
94
|
+
color: "warning",
|
|
95
|
+
});
|
|
96
|
+
setWishToDelete(null);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("Error deleting wish", err);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const confirmDelete = (id: string) => {
|
|
103
|
+
setWishToDelete(id);
|
|
104
|
+
onOpen();
|
|
105
|
+
};
|
|
106
|
+
|
|
46
107
|
const generateSentimentSummary = async (recentWishes: any[]) => {
|
|
47
108
|
const apiKey = process.env.NEXT_PUBLIC_GEMINI_API_KEY;
|
|
109
|
+
|
|
48
110
|
if (!apiKey) return;
|
|
49
111
|
|
|
50
112
|
try {
|
|
@@ -54,19 +116,25 @@ export default function SagunPage() {
|
|
|
54
116
|
method: "POST",
|
|
55
117
|
headers: { "Content-Type": "application/json" },
|
|
56
118
|
body: JSON.stringify({
|
|
57
|
-
contents: [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
119
|
+
contents: [
|
|
120
|
+
{
|
|
121
|
+
parts: [
|
|
122
|
+
{
|
|
123
|
+
text: `You are a wedding storyteller. Summarize the following guest wishes into a single, beautiful, and poetic paragraph (max 250 characters) that captures the collective love and blessings for the couple. Output ONLY the paragraph in PLAIN TEXT.`,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
text: `Wishes: ${recentWishes.map((w) => w.message).join(" | ")}`,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
generationConfig: { temperature: 0.7, maxOutputTokens: 150 },
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
67
134
|
);
|
|
68
135
|
const result = await response.json();
|
|
69
136
|
const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
|
|
137
|
+
|
|
70
138
|
if (text) setSentiment(text);
|
|
71
139
|
} catch (err) {
|
|
72
140
|
console.error("Sentiment error", err);
|
|
@@ -75,14 +143,16 @@ export default function SagunPage() {
|
|
|
75
143
|
|
|
76
144
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
77
145
|
e.preventDefault();
|
|
78
|
-
|
|
146
|
+
|
|
79
147
|
// Simple Wedding-Themed Bot Protection
|
|
80
148
|
if (formData.captcha.trim() !== "10") {
|
|
81
149
|
addToast({
|
|
82
150
|
title: "Security Check",
|
|
83
|
-
description:
|
|
151
|
+
description:
|
|
152
|
+
"Please answer the question correctly (Hint: It is a number!).",
|
|
84
153
|
color: "warning",
|
|
85
154
|
});
|
|
155
|
+
|
|
86
156
|
return;
|
|
87
157
|
}
|
|
88
158
|
|
|
@@ -129,7 +199,9 @@ export default function SagunPage() {
|
|
|
129
199
|
<p
|
|
130
200
|
className={`${fontSans.className} text-lg md:text-xl text-default-600 dark:text-gray-100 leading-relaxed`}
|
|
131
201
|
>
|
|
132
|
-
Your love and presence are the greatest gifts we could ask for.
|
|
202
|
+
Your love and presence are the greatest gifts we could ask for.
|
|
203
|
+
Share your blessings and wishes with us as we begin our new journey
|
|
204
|
+
together.
|
|
133
205
|
</p>
|
|
134
206
|
</div>
|
|
135
207
|
</section>
|
|
@@ -150,7 +222,9 @@ export default function SagunPage() {
|
|
|
150
222
|
/>
|
|
151
223
|
</div>
|
|
152
224
|
<div className="mt-4 md:mt-6 text-center">
|
|
153
|
-
<p
|
|
225
|
+
<p
|
|
226
|
+
className={`${fontMono.className} text-xs md:text-sm text-default-400 uppercase tracking-[0.3em]`}
|
|
227
|
+
>
|
|
154
228
|
Scan to Bless
|
|
155
229
|
</p>
|
|
156
230
|
</div>
|
|
@@ -175,14 +249,16 @@ export default function SagunPage() {
|
|
|
175
249
|
className="bg-wedding-gold-50 dark:bg-wedding-gold-900/10 text-wedding-gold-700 dark:text-wedding-gold-400 font-mono text-base md:text-xl py-2 px-4 md:py-3 md:px-6 rounded-2xl border border-wedding-gold-100/50 max-w-full"
|
|
176
250
|
symbol=""
|
|
177
251
|
>
|
|
178
|
-
|
|
252
|
+
your-upi-id@upi
|
|
179
253
|
</Snippet>
|
|
180
254
|
|
|
181
255
|
<Button
|
|
182
256
|
as="a"
|
|
183
257
|
className="bg-wedding-pink-500 text-white font-bold shadow-lg shadow-wedding-pink-500/30 hover:bg-wedding-pink-600 h-12 md:h-14 px-8 md:px-10 text-base md:text-lg rounded-full w-full md:w-auto"
|
|
184
|
-
href="upi://pay?pa=
|
|
185
|
-
startContent={
|
|
258
|
+
href="upi://pay?pa=your-upi-id@upi&pn=Groom"
|
|
259
|
+
startContent={
|
|
260
|
+
<HeartFilledIcon className="w-5 h-5 md:w-6 md:h-6" />
|
|
261
|
+
}
|
|
186
262
|
>
|
|
187
263
|
Open UPI App
|
|
188
264
|
</Button>
|
|
@@ -198,67 +274,80 @@ export default function SagunPage() {
|
|
|
198
274
|
{/* Wish Form */}
|
|
199
275
|
<div className="lg:col-span-2 space-y-8">
|
|
200
276
|
<div className="space-y-4">
|
|
201
|
-
<h2
|
|
277
|
+
<h2
|
|
278
|
+
className={`${fontCursive.className} text-5xl text-wedding-pink-600 dark:text-wedding-pink-400`}
|
|
279
|
+
>
|
|
202
280
|
Leave a Wish
|
|
203
281
|
</h2>
|
|
204
282
|
<p className="text-default-500 dark:text-default-400">
|
|
205
|
-
Your words will be cherished forever. Write a small message for
|
|
283
|
+
Your words will be cherished forever. Write a small message for
|
|
284
|
+
the couple.
|
|
206
285
|
</p>
|
|
207
286
|
</div>
|
|
208
287
|
|
|
209
288
|
<Card className="p-6 bg-white dark:bg-zinc-900 border border-wedding-pink-100 dark:border-wedding-pink-900/30 shadow-xl">
|
|
210
|
-
<form
|
|
289
|
+
<form className="space-y-6" onSubmit={handleSubmit}>
|
|
211
290
|
<Input
|
|
212
291
|
isRequired
|
|
292
|
+
classNames={{
|
|
293
|
+
label: "text-wedding-pink-600 font-semibold",
|
|
294
|
+
inputWrapper:
|
|
295
|
+
"border-wedding-pink-100 focus-within:!border-wedding-pink-500",
|
|
296
|
+
input: "outline-none",
|
|
297
|
+
}}
|
|
213
298
|
label="Your Name"
|
|
214
299
|
labelPlacement="outside-top"
|
|
215
300
|
placeholder="E.g. Rahul & Priya"
|
|
216
|
-
variant="bordered"
|
|
217
301
|
value={formData.name}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
input: "outline-none"
|
|
223
|
-
}}
|
|
302
|
+
variant="bordered"
|
|
303
|
+
onChange={(e) =>
|
|
304
|
+
setFormData({ ...formData, name: e.target.value })
|
|
305
|
+
}
|
|
224
306
|
/>
|
|
225
307
|
<Textarea
|
|
226
308
|
isRequired
|
|
227
|
-
label="Message"
|
|
228
|
-
labelPlacement="outside-top"
|
|
229
|
-
placeholder="Share your love and blessings..."
|
|
230
|
-
variant="bordered"
|
|
231
|
-
value={formData.message}
|
|
232
|
-
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
|
|
233
309
|
classNames={{
|
|
234
310
|
label: "text-wedding-pink-600 font-semibold",
|
|
235
311
|
input: "outline-none p-2",
|
|
236
|
-
inputWrapper:
|
|
312
|
+
inputWrapper:
|
|
313
|
+
"border-wedding-pink-100 focus-within:!border-wedding-pink-500 h-32",
|
|
237
314
|
}}
|
|
315
|
+
label="Message"
|
|
316
|
+
labelPlacement="outside-top"
|
|
317
|
+
placeholder="Share your love and blessings..."
|
|
318
|
+
value={formData.message}
|
|
319
|
+
variant="bordered"
|
|
320
|
+
onChange={(e) =>
|
|
321
|
+
setFormData({ ...formData, message: e.target.value })
|
|
322
|
+
}
|
|
238
323
|
/>
|
|
239
|
-
|
|
324
|
+
|
|
240
325
|
{/* Bot Protection */}
|
|
241
326
|
<div className="p-4 bg-wedding-pink-50 dark:bg-wedding-pink-900/10 rounded-xl border border-wedding-pink-100 dark:border-wedding-pink-800/30">
|
|
242
|
-
|
|
243
|
-
|
|
327
|
+
<p className="text-xs font-bold text-wedding-pink-600 dark:text-wedding-pink-400 uppercase tracking-widest mb-2">
|
|
328
|
+
Security Question
|
|
329
|
+
</p>
|
|
330
|
+
<Input
|
|
244
331
|
isRequired
|
|
245
|
-
label="How many years have we been together?"
|
|
246
|
-
labelPlacement="outside-top"
|
|
247
|
-
placeholder="Enter the number"
|
|
248
|
-
variant="underlined"
|
|
249
|
-
value={formData.captcha}
|
|
250
|
-
onChange={(e) => setFormData({ ...formData, captcha: e.target.value })}
|
|
251
332
|
classNames={{
|
|
252
333
|
label: "text-default-600 text-xs",
|
|
253
334
|
input: "text-center font-bold outline-none",
|
|
254
335
|
}}
|
|
336
|
+
label="How many years have we been together?"
|
|
337
|
+
labelPlacement="outside-top"
|
|
338
|
+
placeholder="Enter the number"
|
|
339
|
+
value={formData.captcha}
|
|
340
|
+
variant="underlined"
|
|
341
|
+
onChange={(e) =>
|
|
342
|
+
setFormData({ ...formData, captcha: e.target.value })
|
|
343
|
+
}
|
|
255
344
|
/>
|
|
256
345
|
</div>
|
|
257
346
|
|
|
258
|
-
<Button
|
|
259
|
-
type="submit"
|
|
260
|
-
isLoading={submitting}
|
|
347
|
+
<Button
|
|
261
348
|
className="w-full bg-gradient-to-r from-wedding-pink-500 to-wedding-gold-500 text-white font-bold h-12"
|
|
349
|
+
isLoading={submitting}
|
|
350
|
+
type="submit"
|
|
262
351
|
>
|
|
263
352
|
Send Blessing
|
|
264
353
|
</Button>
|
|
@@ -272,20 +361,26 @@ export default function SagunPage() {
|
|
|
272
361
|
<AnimatePresence>
|
|
273
362
|
{sentiment && (
|
|
274
363
|
<motion.div
|
|
275
|
-
initial={{ opacity: 0, scale: 0.9 }}
|
|
276
364
|
animate={{ opacity: 1, scale: 1 }}
|
|
277
365
|
className="relative p-8 rounded-[32px] overflow-hidden group"
|
|
366
|
+
initial={{ opacity: 0, scale: 0.9 }}
|
|
278
367
|
>
|
|
279
368
|
<div className="absolute inset-0 bg-gradient-to-br from-wedding-pink-50 to-wedding-gold-50 dark:from-wedding-pink-900/20 dark:to-wedding-gold-900/20" />
|
|
280
|
-
|
|
369
|
+
|
|
281
370
|
<div className="relative z-10 space-y-4 text-center md:text-left">
|
|
282
371
|
<div className="flex items-center justify-center md:justify-start gap-3">
|
|
283
|
-
<span className="p-2 bg-white dark:bg-zinc-800 rounded-full shadow-sm"
|
|
284
|
-
|
|
372
|
+
<span className="p-2 bg-white dark:bg-zinc-800 rounded-full shadow-sm">
|
|
373
|
+
✨
|
|
374
|
+
</span>
|
|
375
|
+
<h3
|
|
376
|
+
className={`${fontMono.className} text-xs uppercase tracking-[0.4em] text-wedding-gold-600 font-black`}
|
|
377
|
+
>
|
|
285
378
|
A Collective Blessing
|
|
286
379
|
</h3>
|
|
287
380
|
</div>
|
|
288
|
-
<p
|
|
381
|
+
<p
|
|
382
|
+
className={`${fontCursive.className} text-3xl md:text-4xl text-default-800 dark:text-white leading-snug`}
|
|
383
|
+
>
|
|
289
384
|
{sentiment}
|
|
290
385
|
</p>
|
|
291
386
|
</div>
|
|
@@ -296,7 +391,9 @@ export default function SagunPage() {
|
|
|
296
391
|
{/* Wishes Feed */}
|
|
297
392
|
<div className="space-y-6">
|
|
298
393
|
<div className="flex items-center justify-between px-2">
|
|
299
|
-
<h3
|
|
394
|
+
<h3
|
|
395
|
+
className={`${fontSans.className} text-xl font-bold text-default-800 dark:text-white`}
|
|
396
|
+
>
|
|
300
397
|
Recent Wishes
|
|
301
398
|
</h3>
|
|
302
399
|
<span className="text-xs font-mono text-default-400 bg-default-100 dark:bg-zinc-800 px-3 py-1 rounded-full">
|
|
@@ -312,26 +409,45 @@ export default function SagunPage() {
|
|
|
312
409
|
<Spinner color="danger" />
|
|
313
410
|
</div>
|
|
314
411
|
) : wishes.length === 0 ? (
|
|
315
|
-
<p className="text-center py-10 text-default-400 italic">
|
|
412
|
+
<p className="text-center py-10 text-default-400 italic">
|
|
413
|
+
No wishes yet. Be the first to bless the couple!
|
|
414
|
+
</p>
|
|
316
415
|
) : (
|
|
317
416
|
wishes.map((wish, i) => (
|
|
318
417
|
<motion.div
|
|
319
418
|
key={wish.id}
|
|
320
|
-
initial={{ opacity: 0, x: 20 }}
|
|
321
419
|
animate={{ opacity: 1, x: 0 }}
|
|
420
|
+
initial={{ opacity: 0, x: 20 }}
|
|
322
421
|
transition={{ delay: i * 0.1 }}
|
|
323
422
|
>
|
|
324
|
-
<Card className="bg-white/40 dark:bg-zinc-900/40 backdrop-blur-sm border border-default-100 dark:border-zinc-800/50 hover:border-wedding-pink-200 transition-colors">
|
|
423
|
+
<Card className="bg-white/40 dark:bg-zinc-900/40 backdrop-blur-sm border border-default-100 dark:border-zinc-800/50 hover:border-wedding-pink-200 transition-colors group relative">
|
|
325
424
|
<CardBody className="p-6">
|
|
326
425
|
<div className="flex items-start gap-4">
|
|
327
|
-
<Avatar
|
|
328
|
-
name={wish.name}
|
|
426
|
+
<Avatar
|
|
329
427
|
className="bg-wedding-pink-100 text-wedding-pink-600 font-bold flex-shrink-0"
|
|
428
|
+
name={wish.name}
|
|
330
429
|
/>
|
|
331
|
-
<div className="space-y-1">
|
|
332
|
-
<p className="font-bold text-default-900 dark:text-white">
|
|
333
|
-
|
|
430
|
+
<div className="space-y-1 flex-grow">
|
|
431
|
+
<p className="font-bold text-default-900 dark:text-white">
|
|
432
|
+
{wish.name}
|
|
433
|
+
</p>
|
|
434
|
+
<p className="text-default-600 dark:text-gray-100 italic">
|
|
435
|
+
"{wish.message}"
|
|
436
|
+
</p>
|
|
334
437
|
</div>
|
|
438
|
+
|
|
439
|
+
{user?.email === process.env.NEXT_PUBLIC_ADMIN_EMAIL && (
|
|
440
|
+
<Button
|
|
441
|
+
isIconOnly
|
|
442
|
+
size="sm"
|
|
443
|
+
variant="light"
|
|
444
|
+
color="danger"
|
|
445
|
+
className="opacity-0 group-hover:opacity-100 transition-opacity"
|
|
446
|
+
onClick={() => confirmDelete(wish.id)}
|
|
447
|
+
>
|
|
448
|
+
<TrashIcon size={18} />
|
|
449
|
+
</Button>
|
|
450
|
+
)}
|
|
335
451
|
</div>
|
|
336
452
|
</CardBody>
|
|
337
453
|
</Card>
|
|
@@ -343,6 +459,38 @@ export default function SagunPage() {
|
|
|
343
459
|
</div>
|
|
344
460
|
</div>
|
|
345
461
|
</section>
|
|
462
|
+
|
|
463
|
+
{/* Delete Confirmation Modal */}
|
|
464
|
+
<Modal isOpen={isOpen} onOpenChange={onOpenChange} backdrop="blur">
|
|
465
|
+
<ModalContent>
|
|
466
|
+
{(onClose) => (
|
|
467
|
+
<>
|
|
468
|
+
<ModalHeader className="flex flex-col gap-1 text-danger">
|
|
469
|
+
Delete Blessing?
|
|
470
|
+
</ModalHeader>
|
|
471
|
+
<ModalBody>
|
|
472
|
+
<p className="text-default-600">
|
|
473
|
+
Are you sure you want to remove this beautiful blessing? This action cannot be undone.
|
|
474
|
+
</p>
|
|
475
|
+
</ModalBody>
|
|
476
|
+
<ModalFooter>
|
|
477
|
+
<Button variant="light" onPress={onClose}>
|
|
478
|
+
Keep it
|
|
479
|
+
</Button>
|
|
480
|
+
<Button
|
|
481
|
+
color="danger"
|
|
482
|
+
onPress={() => {
|
|
483
|
+
handleDeleteWish();
|
|
484
|
+
onClose();
|
|
485
|
+
}}
|
|
486
|
+
>
|
|
487
|
+
Delete Forever
|
|
488
|
+
</Button>
|
|
489
|
+
</ModalFooter>
|
|
490
|
+
</>
|
|
491
|
+
)}
|
|
492
|
+
</ModalContent>
|
|
493
|
+
</Modal>
|
|
346
494
|
</div>
|
|
347
495
|
);
|
|
348
496
|
}
|