@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
@@ -13,8 +13,10 @@ import {
13
13
  Badge,
14
14
  } from "@heroui/react";
15
15
  import { motion, AnimatePresence } from "framer-motion";
16
+
16
17
  import { HeartFilledIcon } from "./icons";
17
- import { fontCursive, fontMono } from "@/config/fonts";
18
+
19
+ import { fontCursive } from "@/config/fonts";
18
20
 
19
21
  interface Message {
20
22
  role: "user" | "bot";
@@ -43,23 +45,25 @@ const ConciergeBot = () => {
43
45
  if (!input.trim()) return;
44
46
 
45
47
  const userMsg = input.trim();
48
+
46
49
  setMessages((prev) => [...prev, { role: "user", text: userMsg }]);
47
50
  setInput("");
48
51
  setIsTyping(true);
49
52
 
50
53
  try {
51
54
  const apiKey = process.env.NEXT_PUBLIC_GEMINI_API_KEY;
55
+
52
56
  if (!apiKey) throw new Error("API Key missing");
53
57
 
54
58
  const systemInstruction = `
55
- You are an elegant and helpful Wedding Concierge for Titas and Sukanya's wedding.
59
+ You are an elegant and helpful Wedding Concierge for a wedding celebration.
56
60
  Your tone is warm, respectful, and slightly formal (Indian hospitality style).
57
61
 
58
62
  Key Info:
59
- - Couple: Titas Mallick & Sukanya (married after 10 years of love).
60
- - Engagement: 23rd Nov 2025 at Mohan Jyoti Banquet Hall, Serampore (10:00 AM).
61
- - Wedding: 23rd Jan 2026 at Anandamayee Bhawan, Serampore (06:00 PM).
62
- - Reception: 25th Jan 2026 at Friends Union Club, Konnagar (06:00 PM).
63
+ - Couple: Groom & Bride (married after 10 years of love).
64
+ - Engagement: 23rd Nov 2025 at Venue Name (10:00 AM).
65
+ - Wedding: 23rd Jan 2026 at Venue Name (06:00 PM).
66
+ - Reception: 25th Jan 2026 at Venue Name (06:00 PM).
63
67
  - Story: They were college classmates who became soulmates. They love the mountains.
64
68
 
65
69
  Instructions:
@@ -77,30 +81,38 @@ const ConciergeBot = () => {
77
81
  body: JSON.stringify({
78
82
  contents: [
79
83
  { role: "user", parts: [{ text: systemInstruction }] },
80
- ...messages.map(m => ({
84
+ ...messages.map((m) => ({
81
85
  role: m.role === "user" ? "user" : "model",
82
- parts: [{ text: m.text }]
86
+ parts: [{ text: m.text }],
83
87
  })),
84
- { role: "user", parts: [{ text: userMsg }] }
88
+ { role: "user", parts: [{ text: userMsg }] },
85
89
  ],
86
- generationConfig: { maxOutputTokens: 400, temperature: 0.7 }
87
- })
88
- }
90
+ generationConfig: { maxOutputTokens: 400, temperature: 0.7 },
91
+ }),
92
+ },
89
93
  );
90
94
 
91
95
  const data = await response.json();
92
-
96
+
93
97
  if (data.error) {
94
98
  console.error("Gemini API Error Detail:", data.error);
95
99
  throw new Error(data.error.message);
96
100
  }
97
101
 
98
- const botResponse = data.candidates?.[0]?.content?.parts?.[0]?.text || "I apologize, I'm having trouble connecting. Please try again soon!";
99
-
102
+ const botResponse =
103
+ data.candidates?.[0]?.content?.parts?.[0]?.text ||
104
+ "I apologize, I'm having trouble connecting. Please try again soon!";
105
+
100
106
  setMessages((prev) => [...prev, { role: "bot", text: botResponse }]);
101
107
  } catch (error: any) {
102
108
  console.error("Chatbot Error:", error);
103
- setMessages((prev) => [...prev, { role: "bot", text: `I'm having a small technical issue: ${error.message || "Please try again later."}` }]);
109
+ setMessages((prev) => [
110
+ ...prev,
111
+ {
112
+ role: "bot",
113
+ text: `I'm having a small technical issue: ${error.message || "Please try again later."}`,
114
+ },
115
+ ]);
104
116
  } finally {
105
117
  setIsTyping(false);
106
118
  }
@@ -111,34 +123,55 @@ const ConciergeBot = () => {
111
123
  <AnimatePresence>
112
124
  {isOpen && (
113
125
  <motion.div
114
- initial={{ opacity: 0, scale: 0.8, y: 20 }}
115
126
  animate={{ opacity: 1, scale: 1, y: 0 }}
116
- exit={{ opacity: 0, scale: 0.8, y: 20 }}
117
127
  className="mb-3 w-[calc(100vw-32px)] sm:w-[350px] md:w-[380px] shadow-2xl overflow-hidden"
128
+ exit={{ opacity: 0, scale: 0.8, y: 20 }}
129
+ initial={{ opacity: 0, scale: 0.8, y: 20 }}
118
130
  >
119
131
  <Card className="border-none bg-white/90 dark:bg-zinc-900/95 backdrop-blur-xl h-[400px] md:h-[500px] max-h-[70vh] flex flex-col">
120
132
  <CardHeader className="bg-gradient-to-r from-wedding-pink-500 to-wedding-gold-500 p-4 flex justify-between items-center text-white">
121
133
  <div className="flex items-center gap-3">
122
- <Avatar src="/love-birds.png" className="bg-white/20" size="sm" />
134
+ <Avatar
135
+ className="bg-white/20"
136
+ size="sm"
137
+ src="/love-birds.png"
138
+ />
123
139
  <div>
124
- <p className={`${fontCursive.className} text-xl leading-none`}>Wedding Assistant</p>
125
- <p className="text-[10px] uppercase tracking-widest opacity-80">Online & Ready</p>
140
+ <p
141
+ className={`${fontCursive.className} text-xl leading-none`}
142
+ >
143
+ Wedding Assistant
144
+ </p>
145
+ <p className="text-[10px] uppercase tracking-widest opacity-80">
146
+ Online & Ready
147
+ </p>
126
148
  </div>
127
149
  </div>
128
- <Button isIconOnly variant="light" size="sm" onPress={() => setIsOpen(false)} className="text-white min-w-0">
150
+ <Button
151
+ isIconOnly
152
+ className="text-white min-w-0"
153
+ size="sm"
154
+ variant="light"
155
+ onPress={() => setIsOpen(false)}
156
+ >
129
157
 
130
158
  </Button>
131
159
  </CardHeader>
132
-
160
+
133
161
  <CardBody className="p-0">
134
162
  <ScrollShadow ref={scrollRef} className="h-full p-4 space-y-4">
135
163
  {messages.map((msg, i) => (
136
- <div key={i} className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}>
137
- <div className={`max-w-[85%] p-3 rounded-2xl text-sm ${
138
- msg.role === "user"
139
- ? "bg-wedding-pink-500 text-white rounded-tr-none"
140
- : "bg-default-100 dark:bg-zinc-800 text-default-800 dark:text-zinc-200 rounded-tl-none"
141
- }`}>
164
+ <div
165
+ key={i}
166
+ className={`flex ${msg.role === "user" ? "justify-end" : "justify-start"}`}
167
+ >
168
+ <div
169
+ className={`max-w-[85%] p-3 rounded-2xl text-sm ${
170
+ msg.role === "user"
171
+ ? "bg-wedding-pink-500 text-white rounded-tr-none"
172
+ : "bg-default-100 dark:bg-zinc-800 text-default-800 dark:text-zinc-200 rounded-tl-none"
173
+ }`}
174
+ >
142
175
  {msg.text}
143
176
  </div>
144
177
  </div>
@@ -158,26 +191,45 @@ const ConciergeBot = () => {
158
191
  </CardBody>
159
192
 
160
193
  <CardFooter className="p-4 pt-2">
161
- <form className="flex w-full gap-2" onSubmit={(e) => { e.preventDefault(); handleSend(); }}>
194
+ <form
195
+ className="flex w-full gap-2"
196
+ onSubmit={(e) => {
197
+ e.preventDefault();
198
+ handleSend();
199
+ }}
200
+ >
162
201
  <Input
202
+ className="flex-1"
203
+ classNames={{
204
+ inputWrapper:
205
+ "border-wedding-pink-100 focus-within:!border-wedding-pink-500",
206
+ input: "outline-none",
207
+ }}
163
208
  placeholder="Ask me anything..."
209
+ size="sm"
164
210
  value={input}
165
- onChange={(e) => setInput(e.target.value)}
166
211
  variant="bordered"
167
- size="sm"
168
- className="flex-1"
169
- classNames={{ inputWrapper: "border-wedding-pink-100 focus-within:!border-wedding-pink-500", input: "outline-none" }}
212
+ onChange={(e) => setInput(e.target.value)}
170
213
  />
171
- <Button
172
- isIconOnly
173
- color="danger"
174
- size="sm"
175
- onPress={handleSend}
214
+ <Button
215
+ isIconOnly
176
216
  className="bg-wedding-pink-500"
217
+ color="danger"
218
+ size="sm"
219
+ onPress={handleSend}
177
220
  >
178
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
179
- <line x1="22" y1="2" x2="11" y2="13"></line>
180
- <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
221
+ <svg
222
+ fill="none"
223
+ height="16"
224
+ stroke="currentColor"
225
+ strokeLinecap="round"
226
+ strokeLinejoin="round"
227
+ strokeWidth="2.5"
228
+ viewBox="0 0 24 24"
229
+ width="16"
230
+ >
231
+ <line x1="22" x2="11" y1="2" y2="13" />
232
+ <polygon points="22 2 15 22 11 13 2 9 22 2" />
181
233
  </svg>
182
234
  </Button>
183
235
  </form>
@@ -192,8 +244,16 @@ const ConciergeBot = () => {
192
244
  className="w-10 h-10 md:w-14 md:h-14 rounded-full bg-gradient-to-tr from-wedding-pink-500 to-wedding-gold-500 shadow-wedding-pink-500/30 shadow-2xl group transition-transform hover:scale-110 active:scale-95"
193
245
  onPress={() => setIsOpen(!isOpen)}
194
246
  >
195
- <Badge content="" color="success" shape="circle" placement="top-right" className="border-2 border-white dark:border-zinc-900 min-w-0 h-2.5 w-2.5">
196
- <HeartFilledIcon className={`text-white w-4 h-4 md:w-6 md:h-6 transition-transform duration-500 ${isOpen ? "rotate-180" : ""}`} />
247
+ <Badge
248
+ className="border-2 border-white dark:border-zinc-900 min-w-0 h-2.5 w-2.5"
249
+ color="success"
250
+ content=""
251
+ placement="top-right"
252
+ shape="circle"
253
+ >
254
+ <HeartFilledIcon
255
+ className={`text-white w-4 h-4 md:w-6 md:h-6 transition-transform duration-500 ${isOpen ? "rotate-180" : ""}`}
256
+ />
197
257
  </Badge>
198
258
  </Button>
199
259
  </div>
@@ -3,9 +3,10 @@
3
3
  import { useEffect, useState } from "react";
4
4
  import { motion, AnimatePresence } from "framer-motion";
5
5
 
6
- import { fontMono, fontCursive } from "@/config/fonts";
7
6
  import { HeartFilledIcon } from "./icons";
8
7
 
8
+ import { fontMono, fontCursive } from "@/config/fonts";
9
+
9
10
  interface TimeLeft {
10
11
  days: number;
11
12
  hours: number;
@@ -13,7 +14,12 @@ interface TimeLeft {
13
14
  seconds: number;
14
15
  }
15
16
 
16
- type TimerPhase = "BEFORE_WEDDING" | "WEDDING_DAY" | "BEFORE_RECEPTION" | "RECEPTION_DAY" | "POST_EVENT";
17
+ type TimerPhase =
18
+ | "BEFORE_WEDDING"
19
+ | "WEDDING_DAY"
20
+ | "BEFORE_RECEPTION"
21
+ | "RECEPTION_DAY"
22
+ | "POST_EVENT";
17
23
 
18
24
  export default function CountdownTimer() {
19
25
  const [timeLeft, setTimeLeft] = useState<TimeLeft | null>(null);
@@ -27,7 +33,7 @@ export default function CountdownTimer() {
27
33
 
28
34
  const calculateTimeLeft = () => {
29
35
  const now = new Date().getTime();
30
-
36
+
31
37
  let currentPhase: TimerPhase = "BEFORE_WEDDING";
32
38
  let targetDate = weddingStart;
33
39
 
@@ -47,7 +53,10 @@ export default function CountdownTimer() {
47
53
 
48
54
  setPhase(currentPhase);
49
55
 
50
- if (currentPhase === "BEFORE_WEDDING" || currentPhase === "BEFORE_RECEPTION") {
56
+ if (
57
+ currentPhase === "BEFORE_WEDDING" ||
58
+ currentPhase === "BEFORE_RECEPTION"
59
+ ) {
51
60
  const difference = targetDate - now;
52
61
 
53
62
  setTimeLeft({
@@ -92,7 +101,8 @@ export default function CountdownTimer() {
92
101
  return (
93
102
  <div className="flex flex-col items-center gap-6 py-8">
94
103
  <AnimatePresence mode="wait">
95
- {(phase === "BEFORE_WEDDING" || phase === "BEFORE_RECEPTION") && timeLeft ? (
104
+ {(phase === "BEFORE_WEDDING" || phase === "BEFORE_RECEPTION") &&
105
+ timeLeft ? (
96
106
  <motion.div
97
107
  key="countdown"
98
108
  animate={{ opacity: 1, scale: 1 }}
@@ -101,23 +111,31 @@ export default function CountdownTimer() {
101
111
  initial={{ opacity: 0, scale: 0.9 }}
102
112
  >
103
113
  <TimeUnit label="Days" value={timeLeft.days} />
104
- <div className="text-wedding-gold-400 text-3xl md:text-5xl font-light mb-6">:</div>
114
+ <div className="text-wedding-gold-400 text-3xl md:text-5xl font-light mb-6">
115
+ :
116
+ </div>
105
117
  <TimeUnit label="Hours" value={timeLeft.hours} />
106
- <div className="text-wedding-gold-400 text-3xl md:text-5xl font-light mb-6">:</div>
118
+ <div className="text-wedding-gold-400 text-3xl md:text-5xl font-light mb-6">
119
+ :
120
+ </div>
107
121
  <TimeUnit label="Mins" value={timeLeft.minutes} />
108
- <div className="text-wedding-gold-400 text-3xl md:text-5xl font-light mb-6">:</div>
122
+ <div className="text-wedding-gold-400 text-3xl md:text-5xl font-light mb-6">
123
+ :
124
+ </div>
109
125
  <TimeUnit label="Secs" value={timeLeft.seconds} />
110
126
  </motion.div>
111
127
  ) : (
112
128
  <motion.div
113
129
  key="celebration"
114
130
  animate={{ opacity: 1, y: 0 }}
115
- className="flex flex-col items-center gap-4 bg-gradient-to-br from-wedding-pink-500 to-wedding-gold-500 p-8 rounded-[40px] shadow-2xl text-white"
131
+ className="flex flex-col items-center gap-2 bg-white/30 dark:bg-black/20 backdrop-blur-md px-8 md:px-16 py-6 rounded-[40px] border border-wedding-gold-200/50 dark:border-wedding-gold-800/50 shadow-xl overflow-visible"
116
132
  exit={{ opacity: 0, y: 20 }}
117
133
  initial={{ opacity: 0, y: 20 }}
118
134
  >
119
- <HeartFilledIcon className="w-12 h-12 animate-beat text-white" />
120
- <h2 className={`${fontCursive.className} text-4xl md:text-5xl text-center`}>
135
+ <HeartFilledIcon className="w-12 h-12 animate-beat text-wedding-pink-500" />
136
+ <h2
137
+ className={`${fontCursive.className} text-4xl md:text-6xl text-center bg-gradient-to-r from-wedding-pink-500 to-wedding-gold-500 bg-clip-text text-transparent leading-[1.6] py-12 px-4 overflow-visible`}
138
+ >
121
139
  {phase === "WEDDING_DAY" && "Today is our Wedding Day!"}
122
140
  {phase === "RECEPTION_DAY" && "Today is our Reception!"}
123
141
  {phase === "POST_EVENT" && "Just Married!"}
@@ -126,12 +144,16 @@ export default function CountdownTimer() {
126
144
  )}
127
145
  </AnimatePresence>
128
146
 
129
- <p className={`${fontCursive.className} text-2xl text-wedding-gold-600 dark:text-wedding-gold-400`}>
130
- {phase === "BEFORE_WEDDING" && "Until we say \"I Do\""}
147
+ <p
148
+ className={`${fontCursive.className} text-2xl text-wedding-gold-600 dark:text-wedding-gold-400`}
149
+ >
150
+ {" "}
151
+ {phase === "BEFORE_WEDDING" && 'Until we say "I Do"'}
131
152
  {phase === "BEFORE_RECEPTION" && "Until the Grand Reception"}
132
153
  {phase === "POST_EVENT" && "Happily Ever After"}
133
- {(phase === "WEDDING_DAY" || phase === "RECEPTION_DAY") && "The Celebration Continues..."}
154
+ {(phase === "WEDDING_DAY" || phase === "RECEPTION_DAY") &&
155
+ "The Celebration Continues..."}
134
156
  </p>
135
157
  </div>
136
158
  );
137
- }
159
+ }
@@ -155,7 +155,7 @@ const images = [
155
155
  },
156
156
  ];
157
157
 
158
- const pwImages = Array.from({ length: 32 }, (_, i) => {
158
+ const pwImages = Array.from({ length: 57 }, (_, i) => {
159
159
  const num = String(i + 1).padStart(3, "0");
160
160
 
161
161
  return {
@@ -13,26 +13,36 @@ const fadeInUp = {
13
13
  };
14
14
 
15
15
  const LiveVideos = () => {
16
- const [weddingStatus, setWeddingStatus] = useState({ show: false, isLive: false });
17
- const [receptionStatus, setReceptionStatus] = useState({ show: false, isLive: false });
16
+ const [weddingStatus, setWeddingStatus] = useState({
17
+ show: false,
18
+ isLive: false,
19
+ });
20
+ const [receptionStatus, setReceptionStatus] = useState({
21
+ show: false,
22
+ isLive: false,
23
+ });
18
24
 
19
25
  useEffect(() => {
20
26
  const today = new Date();
21
27
  // Normalize today to start of day for accurate comparison
22
- const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
23
-
28
+ const startOfToday = new Date(
29
+ today.getFullYear(),
30
+ today.getMonth(),
31
+ today.getDate(),
32
+ );
33
+
24
34
  // Dates from CountdownTimer: Wedding Jan 23, Reception Jan 25 (2026)
25
35
  const weddingDate = new Date(2026, 0, 23);
26
36
  const receptionDate = new Date(2026, 0, 25);
27
37
 
28
38
  setWeddingStatus({
29
39
  show: startOfToday >= weddingDate,
30
- isLive: startOfToday.getTime() === weddingDate.getTime()
40
+ isLive: startOfToday.getTime() === weddingDate.getTime(),
31
41
  });
32
42
 
33
43
  setReceptionStatus({
34
44
  show: startOfToday >= receptionDate,
35
- isLive: startOfToday.getTime() === receptionDate.getTime()
45
+ isLive: startOfToday.getTime() === receptionDate.getTime(),
36
46
  });
37
47
  }, []);
38
48
 
@@ -41,16 +51,16 @@ const LiveVideos = () => {
41
51
  {/* Background with subtle animation */}
42
52
  <div className="absolute inset-0 bg-gradient-to-br from-wedding-pink-50/80 via-white to-wedding-gold-50/50 dark:from-wedding-pink-950/20 dark:via-black dark:to-wedding-gold-950/10 -z-10" />
43
53
  <div className="absolute top-0 left-0 w-full h-full bg-[url('/corner1-01.svg')] opacity-[0.03] dark:opacity-[0.05] bg-cover bg-center pointer-events-none" />
44
-
54
+
45
55
  {/* Animated glowing rings */}
46
56
  <div className="absolute w-32 h-32 md:w-40 md:h-40 rounded-full border border-wedding-pink-200/30 dark:border-wedding-pink-500/10 animate-[ping_3s_infinite]" />
47
57
  <div className="absolute w-24 h-24 md:w-32 md:h-32 rounded-full border border-wedding-gold-200/30 dark:border-wedding-gold-500/10 animate-[ping_4s_infinite]" />
48
58
 
49
- <motion.div
59
+ <motion.div
50
60
  className="relative z-10 flex flex-col items-center"
51
61
  initial={{ opacity: 0, scale: 0.9 }}
52
- whileInView={{ opacity: 1, scale: 1 }}
53
62
  transition={{ duration: 0.5 }}
63
+ whileInView={{ opacity: 1, scale: 1 }}
54
64
  >
55
65
  <div className="w-16 h-16 md:w-20 md:h-20 mb-4 md:mb-6 rounded-full bg-white dark:bg-zinc-900 shadow-xl flex items-center justify-center border border-wedding-pink-100 dark:border-wedding-pink-900 group-hover:scale-110 transition-transform duration-500">
56
66
  <svg
@@ -68,19 +78,21 @@ const LiveVideos = () => {
68
78
  </svg>
69
79
  </div>
70
80
 
71
- <h4 className={`${fontCursive.className} text-2xl md:text-4xl text-wedding-gold-600 dark:text-wedding-gold-400 mb-2 md:mb-3`}>
81
+ <h4
82
+ className={`${fontCursive.className} text-2xl md:text-4xl text-wedding-gold-600 dark:text-wedding-gold-400 mb-2 md:mb-3`}
83
+ >
72
84
  {title} Stream
73
85
  </h4>
74
-
86
+
75
87
  <div className="h-px w-16 md:w-24 bg-gradient-to-r from-transparent via-wedding-pink-200 dark:via-wedding-pink-800 to-transparent mb-3 md:mb-4" />
76
-
88
+
77
89
  <p className="text-default-600 dark:text-gray-300 max-w-[240px] md:max-w-sm mx-auto leading-relaxed text-sm md:text-base">
78
90
  Our hearts are filled with joy as we prepare for our big day.
79
91
  </p>
80
92
  <p className="mt-2 font-bold text-wedding-pink-600 dark:text-wedding-pink-400 tracking-wide uppercase text-[10px] md:text-xs">
81
93
  Available on {date}
82
94
  </p>
83
-
95
+
84
96
  <div className="mt-6 md:mt-8 px-4 md:px-6 py-1.5 md:py-2 rounded-full border border-wedding-gold-200 dark:border-wedding-gold-800 bg-white/50 dark:bg-black/20 backdrop-blur-sm text-[9px] md:text-[10px] uppercase tracking-[0.2em] text-default-500 font-bold">
85
97
  Reserved for Guests
86
98
  </div>
@@ -117,7 +129,7 @@ const LiveVideos = () => {
117
129
  The Wedding
118
130
  </h3>
119
131
  </div>
120
-
132
+
121
133
  {weddingStatus.show ? (
122
134
  <div className="w-full aspect-video rounded-2xl overflow-hidden shadow-2xl border-2 md:border-4 border-white dark:border-white/10 hover:scale-[1.01] transition-transform duration-500">
123
135
  <iframe
@@ -170,4 +182,4 @@ const LiveVideos = () => {
170
182
  );
171
183
  };
172
184
 
173
- export default LiveVideos;
185
+ export default LiveVideos;
@@ -152,7 +152,7 @@ export default function OurStorySection() {
152
152
  </p>
153
153
 
154
154
  <p className={`${fontCursive.className} text-right text-3xl mt-4`}>
155
- -- Titas
155
+ -- Groom
156
156
  </p>
157
157
  </div>
158
158
  </section>
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+
3
+ export const SchemaMarkup = () => {
4
+ const weddingSchema = {
5
+ "@context": "https://schema.org",
6
+ "@type": "Event",
7
+ "name": "The Wedding of Titas and Sukanya",
8
+ "startDate": "2026-01-23T18:00",
9
+ "endDate": "2026-01-23T23:59",
10
+ "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
11
+ "eventStatus": "https://schema.org/EventScheduled",
12
+ "location": {
13
+ "@type": "Place",
14
+ "name": "Anandamayee Bhawan",
15
+ "address": {
16
+ "@type": "PostalAddress",
17
+ "addressLocality": "Serampore",
18
+ "addressRegion": "West Bengal",
19
+ "addressCountry": "IN"
20
+ }
21
+ },
22
+ "image": [
23
+ "https://www.titas-sukanya-for.life/invite.jpeg",
24
+ "https://www.titas-sukanya-for.life/pw/020.jpg"
25
+ ],
26
+ "description": "Join us in celebrating the wedding of Titas and Sukanya. A journey of love beginning in Serampore.",
27
+ "organizer": {
28
+ "@type": "Person",
29
+ "name": "Titas Mallick"
30
+ },
31
+ "performer": {
32
+ "@type": "Person",
33
+ "name": "Titas and Sukanya"
34
+ }
35
+ };
36
+
37
+ const breadcrumbSchema = {
38
+ "@context": "https://schema.org",
39
+ "@type": "BreadcrumbList",
40
+ "itemListElement": [
41
+ {
42
+ "@type": "ListItem",
43
+ "position": 1,
44
+ "name": "Home",
45
+ "item": "https://www.titas-sukanya-for.life/"
46
+ },
47
+ {
48
+ "@type": "ListItem",
49
+ "position": 2,
50
+ "name": "Our Story",
51
+ "item": "https://www.titas-sukanya-for.life/couple"
52
+ },
53
+ {
54
+ "@type": "ListItem",
55
+ "position": 3,
56
+ "name": "Gallery",
57
+ "item": "https://www.titas-sukanya-for.life/memories"
58
+ }
59
+ ]
60
+ };
61
+
62
+ return (
63
+ <>
64
+ <script
65
+ type="application/ld+json"
66
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(weddingSchema) }}
67
+ />
68
+ <script
69
+ type="application/ld+json"
70
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
71
+ />
72
+ </>
73
+ );
74
+ };