@titas_mallick/wedding-site-gen 1.1.0 → 2.0.1

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 +104 -170
  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
@@ -3,10 +3,12 @@
3
3
  import { motion } from "framer-motion";
4
4
  import Link from "next/link";
5
5
  import { Button } from "@heroui/button";
6
+ import { useEffect, useState } from "react";
6
7
 
7
8
  import ImportantNews from "./importantNews";
8
9
  import CertificatePage from "./certificate";
9
10
  import WeddingCard from "./weddingcard";
11
+ import WeddingCard2 from "./weddingcard2";
10
12
  import { HeartFilledIcon } from "./icons";
11
13
  import Updates from "./updates";
12
14
  import CountdownTimer from "./CountdownTimer";
@@ -22,6 +24,19 @@ const fadeInUp = {
22
24
  };
23
25
 
24
26
  const Herohome = () => {
27
+ const [showReception, setShowReception] = useState(false);
28
+
29
+ useEffect(() => {
30
+ const now = new Date();
31
+ const weddingDate = new Date("2026-01-23");
32
+
33
+ weddingDate.setHours(23, 59, 59, 999);
34
+
35
+ if (now > weddingDate) {
36
+ setShowReception(true);
37
+ }
38
+ }, []);
39
+
25
40
  return (
26
41
  <div className="flex flex-col gap-16 md:gap-32 pb-20 overflow-hidden">
27
42
  {/* Latest Update Bar */}
@@ -55,7 +70,7 @@ const Herohome = () => {
55
70
  initial={{ opacity: 0 }}
56
71
  transition={{ delay: 1, duration: 1 }}
57
72
  >
58
- Titas & Sukanya
73
+ Groom & Bride
59
74
  </motion.p>
60
75
  </section>
61
76
 
@@ -77,8 +92,7 @@ const Herohome = () => {
77
92
  </h2>
78
93
  </div>
79
94
  <div className="relative z-10">
80
- <WeddingCard />
81
- {/* <WeddingCard2 /> */}
95
+ {showReception ? <WeddingCard2 /> : <WeddingCard />}
82
96
  </div>
83
97
  </motion.div>
84
98
  </section>
@@ -120,7 +134,36 @@ const Herohome = () => {
120
134
  <ImportantNews />
121
135
  </section>
122
136
 
123
- {/* 5. Official Record */}
137
+ {/* 5. Mini Game Section */}
138
+ <section className="container mx-auto px-4 py-20 bg-wedding-gold-50/30 dark:bg-white/5 rounded-3xl mx-4 md:mx-0">
139
+ <motion.div
140
+ {...fadeInUp}
141
+ className="flex flex-col items-center text-center max-w-4xl mx-auto"
142
+ >
143
+ <h2
144
+ className={`${fontCursive.className} text-4xl text-wedding-gold-600 dark:text-wedding-gold-400 mb-6`}
145
+ >
146
+ A Little Fun
147
+ </h2>
148
+ <p
149
+ className={`${fontMono.className} text-lg mb-8 text-default-600 dark:text-white`}
150
+ >
151
+ Take a moment to enjoy a small wedding-themed memory game while you
152
+ are here!
153
+ </p>
154
+ <Button
155
+ as={Link}
156
+ className="bg-wedding-gold-500 text-white font-medium shadow-lg hover:bg-wedding-gold-600 transition-all transform hover:-translate-y-1"
157
+ href="/game"
158
+ radius="full"
159
+ size="lg"
160
+ >
161
+ Play the Game
162
+ </Button>
163
+ </motion.div>
164
+ </section>
165
+
166
+ {/* 6. Official Record */}
124
167
  <section className="container mx-auto px-4 pb-10">
125
168
  <motion.div
126
169
  {...fadeInUp}
@@ -281,3 +281,48 @@ export const SearchIcon = (props: IconSvgProps) => (
281
281
  />
282
282
  </svg>
283
283
  );
284
+
285
+ export const TrashIcon = ({
286
+ size = 24,
287
+ width,
288
+ height,
289
+ ...props
290
+ }: IconSvgProps) => (
291
+ <svg
292
+ fill="none"
293
+ height={size || height}
294
+ viewBox="0 0 24 24"
295
+ width={size || width}
296
+ stroke="currentColor"
297
+ strokeWidth="2"
298
+ strokeLinecap="round"
299
+ strokeLinejoin="round"
300
+ {...props}
301
+ >
302
+ <polyline points="3 6 5 6 21 6" />
303
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
304
+ <line x1="10" y1="11" x2="10" y2="17" />
305
+ <line x1="14" y1="11" x2="14" y2="17" />
306
+ </svg>
307
+ );
308
+
309
+ export const CheckIcon = ({
310
+ size = 24,
311
+ width,
312
+ height,
313
+ ...props
314
+ }: IconSvgProps) => (
315
+ <svg
316
+ fill="none"
317
+ height={size || height}
318
+ viewBox="0 0 24 24"
319
+ width={size || width}
320
+ stroke="currentColor"
321
+ strokeWidth="3"
322
+ strokeLinecap="round"
323
+ strokeLinejoin="round"
324
+ {...props}
325
+ >
326
+ <polyline points="20 6 9 17 4 12" />
327
+ </svg>
328
+ );
@@ -1,168 +1,168 @@
1
- "use client";
2
-
3
- import { motion } from "framer-motion";
4
- import { Card, CardBody, Button, Link } from "@heroui/react";
5
- import { CalendarIcon, ClockIcon, MapPinIcon } from "./icons";
6
- import { useEffect, useState } from "react";
7
-
8
- export default function ImportantNews() {
9
- const [currentDate, setCurrentDate] = useState(null);
10
-
11
- useEffect(() => {
12
- setCurrentDate(new Date());
13
- }, []);
14
-
15
- const events = {
16
- registration: {
17
- title: "Engagement Ceremony",
18
- date: "23rd November 2025",
19
- venue: "Srerampore",
20
- map: "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d229.96354422747754!2d88.34617843336005!3d22.749911773448982!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x39f89b1ee7f14647%3A0xe44dfacd65bb5f48!2sMohan%20Joyti%20Banquet%20Hall!5e0!3m2!1sen!2sin!4v1758679491438!5m2!1sen!2sin",
21
- mapHref: "https://maps.app.goo.gl/diSeycey1hyqqtAu8",
22
- time: "10:00 AM",
23
- direction: "Near Battala",
24
- },
25
- wedding: {
26
- title: "Wedding Ceremony",
27
- date: "23rd January 2026",
28
- venue: "Serampore",
29
- map: "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3679.3779465759876!2d88.33077587399126!3d22.75135112639763!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x39f89b287ddfb39b%3A0xc67348083f7cea9d!2sAnandamayee%20Bhawan!5e0!3m2!1sen!2sin!4v1765848206729!5m2!1sen!2sin",
30
- mapHref: "https://maps.app.goo.gl/G5R3bkcTwwa2d54R8",
31
- time: "06:00 PM",
32
- direction: "Near Belting Bazar",
33
- },
34
- reception: {
35
- title: "Reception Celebration",
36
- date: "25th January 2026",
37
- venue: "Konnagar",
38
- map: "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3680.8284397695707!2d88.35295167398938!3d22.697429628386114!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x39f89c85a91d38fd%3A0x19dcd2c61895f79f!2sKonnagar%20Friends%20Union%20Club%20Community%20Centre%2FGangadhar%20Chatterjee%20Bhaban!5e0!3m2!1sen!2sin!4v1765848530552!5m2!1sen!2sin",
39
- mapHref: "https://maps.app.goo.gl/ubdTsy6tnYMsvSSXA",
40
- time: "06:00 PM",
41
- direction: "Near GT Road",
42
- },
43
- };
44
-
45
- const parseDate = (dateStr) => {
46
- // Remove ordinal suffixes (st, nd, rd, th) from the day
47
- const cleanedDate = dateStr.replace(/(\d+)(st|nd|rd|th)/, "$1");
48
- return new Date(cleanedDate);
49
- };
50
-
51
- return (
52
- <div className="w-full flex items-center justify-center font-sans my-12 px-4">
53
- <motion.div
54
- className="w-full max-w-6xl"
55
- initial={{ opacity: 0, y: 20 }}
56
- whileInView={{ opacity: 1, y: 0 }}
57
- viewport={{ once: true }}
58
- transition={{ duration: 0.8, ease: "easeOut" }}
59
- >
60
- <Card className="relative w-full rounded-3xl overflow-hidden shadow-2xl border border-white/20 dark:border-white/10 bg-white/80 dark:bg-black/80 backdrop-blur-xl">
61
- {/* Decorative background gradients */}
62
- <div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-300 dark:from-pink-900 dark:via-purple-900 dark:to-indigo-900" />
63
- <div className="absolute -top-24 -right-24 w-64 h-64 bg-pink-100/50 dark:bg-pink-900/20 rounded-full blur-3xl pointer-events-none" />
64
- <div className="absolute -bottom-24 -left-24 w-64 h-64 bg-blue-100/50 dark:bg-blue-900/20 rounded-full blur-3xl pointer-events-none" />
65
-
66
- <CardBody className="p-8 md:p-12 relative z-10">
67
- {/* Header */}
68
- <div className="text-center mb-12">
69
- <motion.span
70
- initial={{ opacity: 0 }}
71
- whileInView={{ opacity: 1 }}
72
- transition={{ delay: 0.2 }}
73
- className="inline-block px-3 py-1 rounded-full bg-pink-50 dark:bg-pink-900/30 text-pink-600 dark:text-pink-300 text-xs font-bold tracking-widest uppercase mb-3 border border-pink-100 dark:border-pink-800"
74
- >
75
- Save The Dates
76
- </motion.span>
77
- <h1 className="text-3xl md:text-5xl font-bold font-serif bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent mb-4">
78
- Wedding Events
79
- </h1>
80
- <p className="text-gray-500 dark:text-gray-400 text-sm md:text-base max-w-2xl mx-auto italic leading-relaxed">
81
- &quot;We invite you to share in our joy at the following events as we begin our new journey together.&quot;
82
- </p>
83
- </div>
84
-
85
- {/* Events Grid */}
86
- <div className="grid md:grid-cols-3 gap-6 lg:gap-8">
87
- {Object.entries(events).map(([key, event], index) => {
88
- const eventDate = parseDate(event.date);
89
- // Set time to end of day to consider event active today
90
- eventDate.setHours(23, 59, 59, 999);
91
- const isPast = currentDate ? currentDate > eventDate : false;
92
-
93
- return (
94
- <motion.div
95
- key={key}
96
- initial={{ opacity: 0, y: 20 }}
97
- whileInView={{ opacity: 1, y: 0 }}
98
- viewport={{ once: true }}
99
- transition={{ delay: index * 0.1 + 0.3 }}
100
- className={`group relative flex flex-col items-center bg-white dark:bg-white/5 border border-gray-100 dark:border-white/10 rounded-2xl p-6 transition-all duration-300
101
- ${isPast
102
- ? "opacity-60 grayscale hover:opacity-80 hover:grayscale-0"
103
- : "hover:shadow-xl hover:shadow-pink-500/10 dark:hover:shadow-pink-500/5 hover:-translate-y-1"
104
- }`}
105
- >
106
- {isPast && (
107
- <div className="absolute top-4 right-4 bg-gray-100 dark:bg-gray-800 text-gray-500 text-[10px] font-bold px-2 py-1 rounded-full uppercase tracking-wider border border-gray-200 dark:border-gray-700">
108
- Completed
109
- </div>
110
- )}
111
-
112
- <div className={`absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-pink-200 dark:via-pink-800 to-transparent transition-opacity ${isPast ? 'opacity-0' : 'opacity-0 group-hover:opacity-100'}`} />
113
-
114
- <h3 className="font-serif font-bold text-xl text-gray-800 dark:text-gray-100 mb-6 text-center">
115
- {event.title}
116
- </h3>
117
-
118
- <div className="w-full space-y-4 flex-grow">
119
- <div className="flex items-start gap-3 text-gray-600 dark:text-gray-300">
120
- <CalendarIcon className="w-5 h-5 text-pink-500 mt-0.5 shrink-0" />
121
- <div>
122
- <p className="font-semibold">{event.date}</p>
123
- </div>
124
- </div>
125
-
126
- <div className="flex items-start gap-3 text-gray-600 dark:text-gray-300">
127
- <ClockIcon className="w-5 h-5 text-purple-500 mt-0.5 shrink-0" />
128
- <p>{event.time}</p>
129
- </div>
130
-
131
- <div className="flex items-start gap-3 text-gray-600 dark:text-gray-300">
132
- <MapPinIcon className="w-5 h-5 text-indigo-500 mt-0.5 shrink-0" />
133
- <div>
134
- <p className="font-medium">{event.venue}</p>
135
- <p className="text-xs text-gray-400 mt-0.5">{event.direction}</p>
136
- </div>
137
- </div>
138
- </div>
139
-
140
- <Button
141
- as={Link}
142
- href={event.mapHref}
143
- isExternal
144
- className={`mt-8 w-full font-medium shadow-sm border
145
- ${isPast
146
- ? "bg-gray-100 dark:bg-gray-800 text-gray-400 border-gray-200 dark:border-gray-700 cursor-not-allowed"
147
- : "bg-gradient-to-r from-pink-50 to-purple-50 dark:from-pink-900/20 dark:to-purple-900/20 hover:from-pink-100 hover:to-purple-100 dark:hover:from-pink-900/40 dark:hover:to-purple-900/40 text-pink-700 dark:text-pink-300 border-pink-100 dark:border-pink-800/50"
148
- }`}
149
- variant="flat"
150
- isDisabled={isPast}
151
- endContent={
152
- <MapPinIcon className="w-4 h-4 opacity-50" />
153
- }
154
- >
155
- {isPast ? "Event Concluded" : "View Map"}
156
- </Button>
157
- </motion.div>
158
- );})}
159
- </div>
160
- </CardBody>
161
- </Card>
162
- </motion.div>
163
- </div>
164
- );
165
- }
166
-
167
-
168
-
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
4
+ import { Card, CardBody, Button, Link } from "@heroui/react";
5
+ import { CalendarIcon, ClockIcon, MapPinIcon } from "./icons";
6
+ import { useEffect, useState } from "react";
7
+
8
+ export default function ImportantNews() {
9
+ const [currentDate, setCurrentDate] = useState(null);
10
+
11
+ useEffect(() => {
12
+ setCurrentDate(new Date());
13
+ }, []);
14
+
15
+ const events = {
16
+ registration: {
17
+ title: "Engagement Ceremony",
18
+ date: "23rd November 2025",
19
+ venue: "Venue City",
20
+ map: "",
21
+ mapHref: "#",
22
+ time: "10:00 AM",
23
+ direction: "Sample Direction",
24
+ },
25
+ wedding: {
26
+ title: "Wedding Ceremony",
27
+ date: "23rd January 2026",
28
+ venue: "Venue City",
29
+ map: "",
30
+ mapHref: "#",
31
+ time: "06:00 PM",
32
+ direction: "Sample Direction",
33
+ },
34
+ reception: {
35
+ title: "Reception Celebration",
36
+ date: "25th January 2026",
37
+ venue: "Venue City",
38
+ map: "",
39
+ mapHref: "#",
40
+ time: "06:00 PM",
41
+ direction: "Sample Direction",
42
+ },
43
+ };
44
+
45
+ const parseDate = (dateStr) => {
46
+ // Remove ordinal suffixes (st, nd, rd, th) from the day
47
+ const cleanedDate = dateStr.replace(/(\d+)(st|nd|rd|th)/, "$1");
48
+ return new Date(cleanedDate);
49
+ };
50
+
51
+ return (
52
+ <div className="w-full flex items-center justify-center font-sans my-12 px-4">
53
+ <motion.div
54
+ className="w-full max-w-6xl"
55
+ initial={{ opacity: 0, y: 20 }}
56
+ whileInView={{ opacity: 1, y: 0 }}
57
+ viewport={{ once: true }}
58
+ transition={{ duration: 0.8, ease: "easeOut" }}
59
+ >
60
+ <Card className="relative w-full rounded-3xl overflow-hidden shadow-2xl border border-white/20 dark:border-white/10 bg-white/80 dark:bg-black/80 backdrop-blur-xl">
61
+ {/* Decorative background gradients */}
62
+ <div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-pink-300 via-purple-300 to-indigo-300 dark:from-pink-900 dark:via-purple-900 dark:to-indigo-900" />
63
+ <div className="absolute -top-24 -right-24 w-64 h-64 bg-pink-100/50 dark:bg-pink-900/20 rounded-full blur-3xl pointer-events-none" />
64
+ <div className="absolute -bottom-24 -left-24 w-64 h-64 bg-blue-100/50 dark:bg-blue-900/20 rounded-full blur-3xl pointer-events-none" />
65
+
66
+ <CardBody className="p-8 md:p-12 relative z-10">
67
+ {/* Header */}
68
+ <div className="text-center mb-12">
69
+ <motion.span
70
+ initial={{ opacity: 0 }}
71
+ whileInView={{ opacity: 1 }}
72
+ transition={{ delay: 0.2 }}
73
+ className="inline-block px-3 py-1 rounded-full bg-pink-50 dark:bg-pink-900/30 text-pink-600 dark:text-pink-300 text-xs font-bold tracking-widest uppercase mb-3 border border-pink-100 dark:border-pink-800"
74
+ >
75
+ Save The Dates
76
+ </motion.span>
77
+ <h1 className="text-3xl md:text-5xl font-bold font-serif bg-gradient-to-r from-pink-600 to-purple-600 bg-clip-text text-transparent mb-4">
78
+ Wedding Events
79
+ </h1>
80
+ <p className="text-gray-500 dark:text-gray-400 text-sm md:text-base max-w-2xl mx-auto italic leading-relaxed">
81
+ &quot;We invite you to share in our joy at the following events as we begin our new journey together.&quot;
82
+ </p>
83
+ </div>
84
+
85
+ {/* Events Grid */}
86
+ <div className="grid md:grid-cols-3 gap-6 lg:gap-8">
87
+ {Object.entries(events).map(([key, event], index) => {
88
+ const eventDate = parseDate(event.date);
89
+ // Mark as completed only after one full day has passed (24 hours after end of day)
90
+ const completionThreshold = new Date(eventDate);
91
+ completionThreshold.setDate(completionThreshold.getDate() + 1);
92
+ completionThreshold.setHours(23, 59, 59, 999);
93
+
94
+ const isPast = currentDate ? currentDate > completionThreshold : false;
95
+
96
+ return (
97
+ <motion.div
98
+ key={key}
99
+ initial={{ opacity: 0, y: 20 }}
100
+ whileInView={{ opacity: 1, y: 0 }}
101
+ viewport={{ once: true }}
102
+ transition={{ delay: index * 0.1 + 0.3 }}
103
+ className={`group relative flex flex-col items-center bg-white dark:bg-white/5 border border-gray-100 dark:border-white/10 rounded-2xl p-6 transition-all duration-300
104
+ ${isPast
105
+ ? "opacity-60 grayscale hover:opacity-80 hover:grayscale-0"
106
+ : "hover:shadow-xl hover:shadow-pink-500/10 dark:hover:shadow-pink-500/5 hover:-translate-y-1"
107
+ }`}
108
+ >
109
+ {isPast && (
110
+ <div className="absolute top-4 right-4 bg-gray-100 dark:bg-gray-800 text-gray-500 text-[10px] font-bold px-2 py-1 rounded-full uppercase tracking-wider border border-gray-200 dark:border-gray-700">
111
+ Completed
112
+ </div>
113
+ )}
114
+
115
+ <div className={`absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-pink-200 dark:via-pink-800 to-transparent transition-opacity ${isPast ? 'opacity-0' : 'opacity-0 group-hover:opacity-100'}`} />
116
+
117
+ <h3 className="font-serif font-bold text-xl text-gray-800 dark:text-gray-100 mb-6 text-center">
118
+ {event.title}
119
+ </h3>
120
+
121
+ <div className="w-full space-y-4 flex-grow">
122
+ <div className="flex items-start gap-3 text-gray-600 dark:text-gray-300">
123
+ <CalendarIcon className="w-5 h-5 text-pink-500 mt-0.5 shrink-0" />
124
+ <div>
125
+ <p className="font-semibold">{event.date}</p>
126
+ </div>
127
+ </div>
128
+
129
+ <div className="flex items-start gap-3 text-gray-600 dark:text-gray-300">
130
+ <ClockIcon className="w-5 h-5 text-purple-500 mt-0.5 shrink-0" />
131
+ <p>{event.time}</p>
132
+ </div>
133
+
134
+ <div className="flex items-start gap-3 text-gray-600 dark:text-gray-300">
135
+ <MapPinIcon className="w-5 h-5 text-indigo-500 mt-0.5 shrink-0" />
136
+ <div>
137
+ <p className="font-medium">{event.venue}</p>
138
+ <p className="text-xs text-gray-400 mt-0.5">{event.direction}</p>
139
+ </div>
140
+ </div>
141
+ </div>
142
+
143
+ <Button
144
+ as={Link}
145
+ href={event.mapHref}
146
+ isExternal
147
+ className={`mt-8 w-full font-medium shadow-sm border
148
+ ${isPast
149
+ ? "bg-gray-100 dark:bg-gray-800 text-gray-400 border-gray-200 dark:border-gray-700 cursor-not-allowed"
150
+ : "bg-gradient-to-r from-pink-50 to-purple-50 dark:from-pink-900/20 dark:to-purple-900/20 hover:from-pink-100 hover:to-purple-100 dark:hover:from-pink-900/40 dark:hover:to-purple-900/40 text-pink-700 dark:text-pink-300 border-pink-100 dark:border-pink-800/50"
151
+ }`}
152
+ variant="flat"
153
+ isDisabled={isPast || event.mapHref === "#"}
154
+ endContent={
155
+ <MapPinIcon className="w-4 h-4 opacity-50" />
156
+ }
157
+ >
158
+ {isPast ? "Event Concluded" : "View Map"}
159
+ </Button>
160
+ </motion.div>
161
+ );})}
162
+ </div>
163
+ </CardBody>
164
+ </Card>
165
+ </motion.div>
166
+ </div>
167
+ );
168
+ }
@@ -15,18 +15,42 @@ import { link as linkStyles } from "@heroui/theme";
15
15
  import NextLink from "next/link";
16
16
  import clsx from "clsx";
17
17
  import { useState } from "react";
18
+ import { motion, AnimatePresence } from "framer-motion";
18
19
 
19
20
  import { siteConfig } from "@/config/site";
20
21
  import { ThemeSwitch } from "@/components/theme-switch";
21
- import { HeartFilledIcon } from "@/components/icons";
22
22
  import { fontCursive } from "@/config/fonts";
23
+ import {
24
+ HeartFilledIcon,
25
+ CalendarIcon,
26
+ MapPinIcon,
27
+ Logo,
28
+ } from "@/components/icons";
29
+
30
+ const getIcon = (label: string) => {
31
+ switch (label.toLowerCase()) {
32
+ case "home":
33
+ return <HeartFilledIcon size={18} />;
34
+ case "our story":
35
+ return <Logo size={18} />;
36
+ case "events":
37
+ return <CalendarIcon size={18} />;
38
+ case "gallery":
39
+ return <HeartFilledIcon size={18} />;
40
+ case "travel guide":
41
+ return <MapPinIcon size={18} />;
42
+ default:
43
+ return <HeartFilledIcon size={18} />;
44
+ }
45
+ };
23
46
 
24
47
  export const Navbar = () => {
25
- const [, setIsMenuOpen] = useState(false);
48
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
26
49
 
27
50
  return (
28
51
  <HeroUINavbar
29
52
  className="bg-wedding-cream/80 dark:bg-black/80 backdrop-blur-md"
53
+ isMenuOpen={isMenuOpen}
30
54
  maxWidth="xl"
31
55
  position="sticky"
32
56
  onMenuOpenChange={setIsMenuOpen}
@@ -81,24 +105,95 @@ export const Navbar = () => {
81
105
 
82
106
  <NavbarContent className="sm:hidden basis-1 pl-4" justify="end">
83
107
  <ThemeSwitch />
84
- <NavbarMenuToggle />
108
+ <NavbarMenuToggle
109
+ aria-label={isMenuOpen ? "Close menu" : "Open menu"}
110
+ className="flex items-center justify-center gap-2.5 ml-2 px-4 py-1.5 h-10 w-auto rounded-full bg-gradient-to-r from-wedding-pink-50 to-wedding-cream dark:from-wedding-pink-900/20 dark:to-zinc-900 border-1.5 border-wedding-pink-200/50 dark:border-wedding-pink-800/50 text-wedding-pink-600 dark:text-wedding-pink-400 transition-all hover:scale-105 active:scale-95 shadow-lg shadow-wedding-pink-500/5"
111
+ >
112
+ <span className="text-[10px] font-black tracking-[0.2em] ml-1">
113
+ {isMenuOpen ? "CLOSE" : "MENU"}
114
+ </span>
115
+ <div className="relative w-5 h-5 flex items-center justify-center">
116
+ <AnimatePresence mode="wait">
117
+ {!isMenuOpen ? (
118
+ <motion.div
119
+ key="menu-heart"
120
+ animate={{
121
+ opacity: 1,
122
+ scale: [1, 1.15, 1],
123
+ }}
124
+ exit={{ opacity: 0, scale: 0.5, rotate: 180 }}
125
+ initial={{ opacity: 0, scale: 0.5 }}
126
+ transition={{
127
+ scale: {
128
+ repeat: Infinity,
129
+ duration: 2,
130
+ ease: "easeInOut",
131
+ },
132
+ opacity: { duration: 0.2 },
133
+ }}
134
+ >
135
+ <HeartFilledIcon size={16} />
136
+ </motion.div>
137
+ ) : (
138
+ <motion.div
139
+ key="menu-close"
140
+ animate={{ opacity: 1, scale: 1, rotate: 0 }}
141
+ exit={{ opacity: 0, scale: 0.5, rotate: -180 }}
142
+ initial={{ opacity: 0, scale: 0.5, rotate: 180 }}
143
+ transition={{ duration: 0.3, ease: "backOut" }}
144
+ >
145
+ <svg
146
+ fill="none"
147
+ height="16"
148
+ stroke="currentColor"
149
+ strokeLinecap="round"
150
+ strokeLinejoin="round"
151
+ strokeWidth="3"
152
+ viewBox="0 0 24 24"
153
+ width="16"
154
+ >
155
+ <line x1="18" x2="6" y1="6" y2="18" />
156
+ <line x1="6" x2="18" y1="6" y2="18" />
157
+ </svg>
158
+ </motion.div>
159
+ )}
160
+ </AnimatePresence>
161
+ </div>
162
+ </NavbarMenuToggle>
85
163
  </NavbarContent>
86
164
 
87
- <NavbarMenu>
88
- <div className="mx-4 mt-8 flex flex-col gap-4">
89
- {siteConfig.navMenuItems.map((item, index) => (
90
- <NavbarMenuItem key={`${item}-${index}`}>
91
- <Link
92
- className="w-full text-lg py-2 hover:text-wedding-pink-500"
93
- color="foreground"
94
- href={item.href}
95
- size="lg"
96
- onPress={() => setIsMenuOpen(false)}
97
- >
98
- {item.label}
99
- </Link>
100
- </NavbarMenuItem>
101
- ))}
165
+ <NavbarMenu className="bg-wedding-cream/95 dark:bg-black/95 pt-12">
166
+ <div className="mx-4 flex flex-col h-full">
167
+ <div className="grid grid-cols-1 gap-3">
168
+ {siteConfig.navMenuItems.map((item, index) => (
169
+ <NavbarMenuItem key={`${item.href}-${index}`}>
170
+ <Link
171
+ as={NextLink}
172
+ className="w-full flex items-center gap-4 py-4 px-6 rounded-2xl bg-white/50 dark:bg-white/5 border border-wedding-pink-100/50 dark:border-wedding-pink-900/20 text-lg transition-all text-foreground hover:bg-wedding-pink-500 hover:text-white group"
173
+ color="foreground"
174
+ href={item.href}
175
+ onPress={() => setIsMenuOpen(false)}
176
+ >
177
+ <span className="text-wedding-pink-500 group-hover:text-white transition-colors">
178
+ {getIcon(item.label)}
179
+ </span>
180
+ <span className="font-medium">{item.label}</span>
181
+ </Link>
182
+ </NavbarMenuItem>
183
+ ))}
184
+ </div>
185
+
186
+ <div className="mt-auto mb-12 flex flex-col items-center text-center space-y-4">
187
+ <div className="w-12 h-[1px] bg-wedding-pink-200" />
188
+ <p
189
+ className={`${fontCursive.className} text-3xl text-wedding-pink-600 dark:text-wedding-pink-400`}
190
+ >
191
+ Titas & Sukanya
192
+ </p>
193
+ <p className="text-[10px] uppercase tracking-[0.5em] text-default-400">
194
+ January 2026
195
+ </p>
196
+ </div>
102
197
  </div>
103
198
  </NavbarMenu>
104
199
  </HeroUINavbar>