@titas_mallick/wedding-site-gen 1.1.0 → 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.
Files changed (123) hide show
  1. package/README.md +70 -184
  2. package/app/api/email-reminders/route.ts +240 -0
  3. package/app/couple/page.tsx +4 -4
  4. package/app/game/page.tsx +298 -0
  5. package/app/guestbook/page.tsx +270 -152
  6. package/app/invitation/[slug]/layout.tsx +4 -2
  7. package/app/invitation/[slug]/page.tsx +303 -84
  8. package/app/invitation/actions.ts +49 -0
  9. package/app/invitation/maker/auth.js +1 -1
  10. package/app/invitation/maker/guestAdder.js +4 -0
  11. package/app/invitation/maker/guestShower.js +39 -8
  12. package/app/invitation/maker/layout.tsx +1 -1
  13. package/app/invitation/maker/page.js +9 -7
  14. package/app/invitation/maker/rsvpViewer.js +90 -8
  15. package/app/layout.tsx +40 -14
  16. package/app/mark-the-dates/page.tsx +8 -2
  17. package/app/page.tsx +7 -1
  18. package/app/providers.tsx +1 -1
  19. package/app/sagun/page.tsx +224 -76
  20. package/app/song-requests/page.tsx +242 -105
  21. package/app/sukanya/page.tsx +9 -13
  22. package/app/titas/page.tsx +8 -24
  23. package/app/travel-guide/page.tsx +361 -120
  24. package/app/updates/maker/page.js +2 -2
  25. package/app/updates/overlay/page.tsx +65 -30
  26. package/app/updates/page.js +3 -3
  27. package/cli.mjs +26 -15
  28. package/components/AdminAuth.tsx +145 -0
  29. package/components/AdminLinks.tsx +120 -0
  30. package/components/ConciergeBot.tsx +104 -44
  31. package/components/CountdownTimer.tsx +37 -15
  32. package/components/Gallery.tsx +1 -1
  33. package/components/LiveVideos.tsx +27 -15
  34. package/components/OurStory.tsx +1 -1
  35. package/components/SchemaMarkup.tsx +74 -0
  36. package/components/certificate.jsx +287 -300
  37. package/components/footer.tsx +2 -0
  38. package/components/hero.tsx +47 -4
  39. package/components/icons.tsx +45 -0
  40. package/components/importantNews.js +168 -168
  41. package/components/navbar.tsx +113 -18
  42. package/components/updates.tsx +36 -26
  43. package/config/firebase-admin.js +14 -17
  44. package/config/firebase.ts +4 -2
  45. package/config/site.ts +10 -2
  46. package/firestore.rules +6 -1
  47. package/next-sitemap.config.js +21 -0
  48. package/package.json +4 -3
  49. package/public/corner1-01.svg +0 -0
  50. package/public/love-birds.png +0 -0
  51. package/public/next.svg +0 -0
  52. package/public/pubqr.png +0 -0
  53. package/public/pw/sample.jpg +0 -0
  54. package/public/qr.png +0 -0
  55. package/public/sample.jpg +0 -0
  56. package/public/vercel.svg +0 -0
  57. package/vercel.json +1 -0
  58. package/.recover +0 -9
  59. package/next-env.d.ts +0 -6
  60. package/public/DCV.gif +0 -0
  61. package/public/DCV2.gif +0 -0
  62. package/public/DCV3.gif +0 -0
  63. package/public/Images/1.jpg +0 -0
  64. package/public/Images/11.jpg +0 -0
  65. package/public/Images/12.jpg +0 -0
  66. package/public/Images/13.jpg +0 -0
  67. package/public/Images/14.jpg +0 -0
  68. package/public/Images/15.jpg +0 -0
  69. package/public/Images/16.jpg +0 -0
  70. package/public/Images/17.jpg +0 -0
  71. package/public/Images/18.jpg +0 -0
  72. package/public/Images/19.jpg +0 -0
  73. package/public/Images/2.jpg +0 -0
  74. package/public/Images/21.jpg +0 -0
  75. package/public/Images/22.jpg +0 -0
  76. package/public/Images/3.jpg +0 -0
  77. package/public/Images/4.jpg +0 -0
  78. package/public/Images/5.jpg +0 -0
  79. package/public/Images/6.jpg +0 -0
  80. package/public/Images/7.jpg +0 -0
  81. package/public/Images/8.jpg +0 -0
  82. package/public/Images/9.jpg +0 -0
  83. package/public/Images/9b.jpg +0 -0
  84. package/public/Images/Patipatra.jpeg +0 -0
  85. package/public/audio (1).mp3 +0 -0
  86. package/public/audio (2).mp3 +0 -0
  87. package/public/bride.jpg +0 -0
  88. package/public/groom.jpg +0 -0
  89. package/public/invite.png +0 -0
  90. package/public/pw/001.jpg +0 -0
  91. package/public/pw/002.jpg +0 -0
  92. package/public/pw/003.jpg +0 -0
  93. package/public/pw/004.jpg +0 -0
  94. package/public/pw/005.jpg +0 -0
  95. package/public/pw/006.jpg +0 -0
  96. package/public/pw/007.jpg +0 -0
  97. package/public/pw/008.jpg +0 -0
  98. package/public/pw/009.jpg +0 -0
  99. package/public/pw/010.jpg +0 -0
  100. package/public/pw/011.jpg +0 -0
  101. package/public/pw/012.jpg +0 -0
  102. package/public/pw/013.jpg +0 -0
  103. package/public/pw/014.jpg +0 -0
  104. package/public/pw/015.jpg +0 -0
  105. package/public/pw/016.jpg +0 -0
  106. package/public/pw/017.jpg +0 -0
  107. package/public/pw/018.jpg +0 -0
  108. package/public/pw/019.jpg +0 -0
  109. package/public/pw/020.jpg +0 -0
  110. package/public/pw/021.jpg +0 -0
  111. package/public/pw/022.jpg +0 -0
  112. package/public/pw/023.jpg +0 -0
  113. package/public/pw/024.jpg +0 -0
  114. package/public/pw/025.jpg +0 -0
  115. package/public/pw/026.jpg +0 -0
  116. package/public/pw/027.jpg +0 -0
  117. package/public/pw/028.jpg +0 -0
  118. package/public/pw/029.jpg +0 -0
  119. package/public/pw/030.jpg +0 -0
  120. package/public/pw/031.jpg +0 -0
  121. package/public/pw/032.jpg +0 -0
  122. package/tsconfig.tsbuildinfo +0 -1
  123. /package/public/Images/{20.jpg → sample.jpg} +0 -0
@@ -1,23 +1,43 @@
1
1
  "use client";
2
2
 
3
3
  import Image from "next/image";
4
- import { Card, CardBody, Button, Snippet, Input, Textarea, Divider, Spinner, Avatar, addToast } from "@heroui/react";
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 [formData, setFormData] = useState({ name: "", message: "", captcha: "" });
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 q = query(collection(db, "wishes"), orderBy("createdAt", "desc"), limit(20));
33
- const unsubscribe = onSnapshot(q, (snapshot) => {
34
- const data = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
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 () => unsubscribe();
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
- parts: [{
59
- 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 Titas and Sukanya. Output ONLY the paragraph in PLAIN TEXT.`
60
- }, {
61
- text: `Wishes: ${recentWishes.map(w => w.message).join(" | ")}`
62
- }]
63
- }],
64
- generationConfig: { temperature: 0.7, maxOutputTokens: 150 }
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: "Please answer the question correctly (Hint: It is a number!).",
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. Share your blessings and wishes with us as we begin our new journey together.
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 className={`${fontMono.className} text-xs md:text-sm text-default-400 uppercase tracking-[0.3em]`}>
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
- eugenics@ybl
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=eugenics@ybl&pn=Titas"
185
- startContent={<HeartFilledIcon className="w-5 h-5 md:w-6 md:h-6" />}
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 className={`${fontCursive.className} text-5xl text-wedding-pink-600 dark:text-wedding-pink-400`}>
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 the couple.
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 onSubmit={handleSubmit} className="space-y-6">
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
- onChange={(e) => setFormData({ ...formData, name: e.target.value })}
219
- classNames={{
220
- label: "text-wedding-pink-600 font-semibold",
221
- inputWrapper: "border-wedding-pink-100 focus-within:!border-wedding-pink-500",
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: "border-wedding-pink-100 focus-within:!border-wedding-pink-500 h-32"
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
- <p className="text-xs font-bold text-wedding-pink-600 dark:text-wedding-pink-400 uppercase tracking-widest mb-2">Security Question</p>
243
- <Input
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">✨</span>
284
- <h3 className={`${fontMono.className} text-xs uppercase tracking-[0.4em] text-wedding-gold-600 font-black`}>
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 className={`${fontCursive.className} text-3xl md:text-4xl text-default-800 dark:text-white leading-snug`}>
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 className={`${fontSans.className} text-xl font-bold text-default-800 dark:text-white`}>
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">No wishes yet. Be the first to bless the couple!</p>
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">{wish.name}</p>
333
- <p className="text-default-600 dark:text-gray-100 italic">&quot;{wish.message}&quot;</p>
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
+ &quot;{wish.message}&quot;
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
  }