@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.
- package/README.md +104 -170
- 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 +26 -15
- 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
|
@@ -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
|
-
|
|
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
|
|
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:
|
|
60
|
-
- Engagement: 23rd Nov 2025 at
|
|
61
|
-
- Wedding: 23rd Jan 2026 at
|
|
62
|
-
- Reception: 25th Jan 2026 at
|
|
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 =
|
|
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) => [
|
|
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
|
|
134
|
+
<Avatar
|
|
135
|
+
className="bg-white/20"
|
|
136
|
+
size="sm"
|
|
137
|
+
src="/love-birds.png"
|
|
138
|
+
/>
|
|
123
139
|
<div>
|
|
124
|
-
<p
|
|
125
|
-
|
|
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
|
|
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
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
196
|
-
|
|
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 =
|
|
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 (
|
|
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") &&
|
|
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"
|
|
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"
|
|
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"
|
|
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-
|
|
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-
|
|
120
|
-
<h2
|
|
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
|
|
130
|
-
{
|
|
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") &&
|
|
154
|
+
{(phase === "WEDDING_DAY" || phase === "RECEPTION_DAY") &&
|
|
155
|
+
"The Celebration Continues..."}
|
|
134
156
|
</p>
|
|
135
157
|
</div>
|
|
136
158
|
);
|
|
137
|
-
}
|
|
159
|
+
}
|
package/components/Gallery.tsx
CHANGED
|
@@ -13,26 +13,36 @@ const fadeInUp = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const LiveVideos = () => {
|
|
16
|
-
const [weddingStatus, setWeddingStatus] = useState({
|
|
17
|
-
|
|
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(
|
|
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
|
|
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;
|
package/components/OurStory.tsx
CHANGED
|
@@ -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
|
+
};
|