@titas_mallick/wedding-site-gen 1.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 (141) hide show
  1. package/.eslintignore +20 -0
  2. package/.eslintrc.json +93 -0
  3. package/.recover +9 -0
  4. package/.vscode/settings.json +3 -0
  5. package/LICENSE +21 -0
  6. package/README.md +83 -0
  7. package/app/Neo-Lucentism/layout.tsx +7 -0
  8. package/app/Neo-Lucentism/page.tsx +259 -0
  9. package/app/couple/layout.tsx +7 -0
  10. package/app/couple/page.tsx +164 -0
  11. package/app/error.tsx +31 -0
  12. package/app/guestbook/page.tsx +470 -0
  13. package/app/invitation/[slug]/layout.tsx +36 -0
  14. package/app/invitation/[slug]/page.tsx +462 -0
  15. package/app/invitation/maker/auth.js +165 -0
  16. package/app/invitation/maker/dashboard.js +81 -0
  17. package/app/invitation/maker/guestAdder.js +204 -0
  18. package/app/invitation/maker/guestShower.js +287 -0
  19. package/app/invitation/maker/layout.tsx +11 -0
  20. package/app/invitation/maker/page.js +168 -0
  21. package/app/invitation/maker/rsvpViewer.js +122 -0
  22. package/app/layout.tsx +98 -0
  23. package/app/mark-the-dates/layout.tsx +7 -0
  24. package/app/mark-the-dates/page.tsx +196 -0
  25. package/app/memories/layout.tsx +7 -0
  26. package/app/memories/page.tsx +29 -0
  27. package/app/page.tsx +5 -0
  28. package/app/providers.tsx +33 -0
  29. package/app/sagun/layout.tsx +7 -0
  30. package/app/sagun/page.tsx +348 -0
  31. package/app/song-requests/page.tsx +354 -0
  32. package/app/sukanya/layout.tsx +7 -0
  33. package/app/sukanya/page.tsx +167 -0
  34. package/app/titas/layout.tsx +7 -0
  35. package/app/titas/page.tsx +175 -0
  36. package/app/travel-guide/page.tsx +400 -0
  37. package/app/updates/maker/page.js +323 -0
  38. package/app/updates/overlay/page.tsx +144 -0
  39. package/app/updates/page.js +207 -0
  40. package/cli.mjs +196 -0
  41. package/components/ConciergeBot.tsx +203 -0
  42. package/components/CountdownTimer.tsx +137 -0
  43. package/components/Gallery.tsx +372 -0
  44. package/components/LiveVideos.tsx +173 -0
  45. package/components/OurStory.tsx +160 -0
  46. package/components/certificate.jsx +300 -0
  47. package/components/counter.tsx +14 -0
  48. package/components/footer.tsx +89 -0
  49. package/components/hero.tsx +136 -0
  50. package/components/icons.tsx +283 -0
  51. package/components/importantNews.js +168 -0
  52. package/components/navbar.tsx +106 -0
  53. package/components/primitives.ts +53 -0
  54. package/components/sagun.js +22 -0
  55. package/components/theme-switch.tsx +81 -0
  56. package/components/updates.tsx +118 -0
  57. package/components/weddingcard.js +68 -0
  58. package/components/weddingcard2.js +58 -0
  59. package/config/firebase-admin.js +17 -0
  60. package/config/firebase.ts +36 -0
  61. package/config/fonts.ts +21 -0
  62. package/config/site.ts +74 -0
  63. package/next-env.d.ts +6 -0
  64. package/next.config.js +4 -0
  65. package/package.json +64 -0
  66. package/postcss.config.js +6 -0
  67. package/public/DCV.gif +0 -0
  68. package/public/DCV2.gif +0 -0
  69. package/public/DCV3.gif +0 -0
  70. package/public/Images/1.jpg +0 -0
  71. package/public/Images/11.jpg +0 -0
  72. package/public/Images/12.jpg +0 -0
  73. package/public/Images/13.jpg +0 -0
  74. package/public/Images/14.jpg +0 -0
  75. package/public/Images/15.jpg +0 -0
  76. package/public/Images/16.jpg +0 -0
  77. package/public/Images/17.jpg +0 -0
  78. package/public/Images/18.jpg +0 -0
  79. package/public/Images/19.jpg +0 -0
  80. package/public/Images/2.jpg +0 -0
  81. package/public/Images/20.jpg +0 -0
  82. package/public/Images/21.jpg +0 -0
  83. package/public/Images/22.jpg +0 -0
  84. package/public/Images/3.jpg +0 -0
  85. package/public/Images/4.jpg +0 -0
  86. package/public/Images/5.jpg +0 -0
  87. package/public/Images/6.jpg +0 -0
  88. package/public/Images/7.jpg +0 -0
  89. package/public/Images/8.jpg +0 -0
  90. package/public/Images/9.jpg +0 -0
  91. package/public/Images/9b.jpg +0 -0
  92. package/public/Images/Patipatra.jpeg +0 -0
  93. package/public/audio (1).mp3 +0 -0
  94. package/public/audio (2).mp3 +0 -0
  95. package/public/bride.jpg +0 -0
  96. package/public/corner1-01.svg +1 -0
  97. package/public/favicon.ico +0 -0
  98. package/public/groom.jpg +0 -0
  99. package/public/invite.png +0 -0
  100. package/public/love-birds.png +0 -0
  101. package/public/next.svg +1 -0
  102. package/public/pubqr.png +0 -0
  103. package/public/pw/001.jpg +0 -0
  104. package/public/pw/002.jpg +0 -0
  105. package/public/pw/003.jpg +0 -0
  106. package/public/pw/004.jpg +0 -0
  107. package/public/pw/005.jpg +0 -0
  108. package/public/pw/006.jpg +0 -0
  109. package/public/pw/007.jpg +0 -0
  110. package/public/pw/008.jpg +0 -0
  111. package/public/pw/009.jpg +0 -0
  112. package/public/pw/010.jpg +0 -0
  113. package/public/pw/011.jpg +0 -0
  114. package/public/pw/012.jpg +0 -0
  115. package/public/pw/013.jpg +0 -0
  116. package/public/pw/014.jpg +0 -0
  117. package/public/pw/015.jpg +0 -0
  118. package/public/pw/016.jpg +0 -0
  119. package/public/pw/017.jpg +0 -0
  120. package/public/pw/018.jpg +0 -0
  121. package/public/pw/019.jpg +0 -0
  122. package/public/pw/020.jpg +0 -0
  123. package/public/pw/021.jpg +0 -0
  124. package/public/pw/022.jpg +0 -0
  125. package/public/pw/023.jpg +0 -0
  126. package/public/pw/024.jpg +0 -0
  127. package/public/pw/025.jpg +0 -0
  128. package/public/pw/026.jpg +0 -0
  129. package/public/pw/027.jpg +0 -0
  130. package/public/pw/028.jpg +0 -0
  131. package/public/pw/029.jpg +0 -0
  132. package/public/pw/030.jpg +0 -0
  133. package/public/pw/031.jpg +0 -0
  134. package/public/pw/032.jpg +0 -0
  135. package/public/qr.png +0 -0
  136. package/public/vercel.svg +1 -0
  137. package/styles/globals.css +3 -0
  138. package/tailwind.config.js +51 -0
  139. package/tsconfig.json +45 -0
  140. package/tsconfig.tsbuildinfo +1 -0
  141. package/types/index.ts +5 -0
@@ -0,0 +1,348 @@
1
+ "use client";
2
+
3
+ import Image from "next/image";
4
+ import { Card, CardBody, Button, Snippet, Input, Textarea, Divider, Spinner, Avatar, addToast } from "@heroui/react";
5
+ import { useEffect, useState } from "react";
6
+ import {
7
+ getFirestore,
8
+ collection,
9
+ addDoc,
10
+ onSnapshot,
11
+ query,
12
+ orderBy,
13
+ serverTimestamp,
14
+ limit
15
+ } from "firebase/firestore";
16
+ import { motion, AnimatePresence } from "framer-motion";
17
+
18
+ import firebaseApp from "@/config/firebase";
19
+ import { fontCursive, fontSans, fontMono } from "@/config/fonts";
20
+ import { HeartFilledIcon } from "@/components/icons";
21
+
22
+ const db = getFirestore(firebaseApp());
23
+
24
+ export default function SagunPage() {
25
+ const [wishes, setWishes] = useState<any[]>([]);
26
+ const [loading, setLoading] = useState(true);
27
+ const [submitting, setSubmitting] = useState(false);
28
+ const [formData, setFormData] = useState({ name: "", message: "", captcha: "" });
29
+ const [sentiment, setSentiment] = useState<string>("");
30
+
31
+ 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() }));
35
+ setWishes(data);
36
+ setLoading(false);
37
+
38
+ if (data.length >= 3) {
39
+ generateSentimentSummary(data.slice(0, 10));
40
+ }
41
+ });
42
+
43
+ return () => unsubscribe();
44
+ }, []);
45
+
46
+ const generateSentimentSummary = async (recentWishes: any[]) => {
47
+ const apiKey = process.env.NEXT_PUBLIC_GEMINI_API_KEY;
48
+ if (!apiKey) return;
49
+
50
+ try {
51
+ const response = await fetch(
52
+ `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`,
53
+ {
54
+ method: "POST",
55
+ headers: { "Content-Type": "application/json" },
56
+ 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
+ }
67
+ );
68
+ const result = await response.json();
69
+ const text = result.candidates?.[0]?.content?.parts?.[0]?.text;
70
+ if (text) setSentiment(text);
71
+ } catch (err) {
72
+ console.error("Sentiment error", err);
73
+ }
74
+ };
75
+
76
+ const handleSubmit = async (e: React.FormEvent) => {
77
+ e.preventDefault();
78
+
79
+ // Simple Wedding-Themed Bot Protection
80
+ if (formData.captcha.trim() !== "10") {
81
+ addToast({
82
+ title: "Security Check",
83
+ description: "Please answer the question correctly (Hint: It is a number!).",
84
+ color: "warning",
85
+ });
86
+ return;
87
+ }
88
+
89
+ if (!formData.name || !formData.message) return;
90
+
91
+ setSubmitting(true);
92
+ try {
93
+ await addDoc(collection(db, "wishes"), {
94
+ name: formData.name,
95
+ message: formData.message,
96
+ createdAt: serverTimestamp(),
97
+ });
98
+ setFormData({ name: "", message: "", captcha: "" });
99
+ addToast({
100
+ title: "Wish Sent",
101
+ description: "Thank you for your beautiful blessings!",
102
+ color: "success",
103
+ });
104
+ } catch (err) {
105
+ console.error("Error adding wish", err);
106
+ addToast({
107
+ title: "Error",
108
+ description: "Something went wrong. Please try again.",
109
+ color: "danger",
110
+ });
111
+ } finally {
112
+ setSubmitting(false);
113
+ }
114
+ };
115
+
116
+ return (
117
+ <div className="min-h-screen w-full pb-20">
118
+ {/* Header */}
119
+ <section className="relative py-16 md:py-24 text-center px-4">
120
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[500px] bg-wedding-pink-100/40 dark:bg-wedding-pink-900/10 rounded-full blur-[100px] pointer-events-none" />
121
+
122
+ <div className="relative z-10 max-w-2xl mx-auto">
123
+ <HeartFilledIcon className="text-wedding-pink-500 w-10 h-10 mx-auto mb-6 animate-pulse" />
124
+ <h1
125
+ className={`${fontCursive.className} text-6xl md:text-7xl bg-gradient-to-r from-wedding-pink-600 to-wedding-gold-600 bg-clip-text text-transparent mb-6 py-4 leading-normal`}
126
+ >
127
+ Blessings & Sagun
128
+ </h1>
129
+ <p
130
+ className={`${fontSans.className} text-lg md:text-xl text-default-600 dark:text-gray-100 leading-relaxed`}
131
+ >
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.
133
+ </p>
134
+ </div>
135
+ </section>
136
+
137
+ {/* QR & UPI Section */}
138
+ <section className="container mx-auto px-4 max-w-5xl mb-24">
139
+ <div className="grid md:grid-cols-2 gap-8 md:gap-12 items-center bg-white/50 dark:bg-zinc-900/50 backdrop-blur-md p-6 md:p-8 rounded-[32px] md:rounded-[40px] border border-wedding-gold-100 dark:border-wedding-gold-900/20 shadow-xl">
140
+ {/* QR Card */}
141
+ <div className="flex justify-center order-2 md:order-1">
142
+ <Card className="w-full max-w-[280px] md:max-w-sm bg-white dark:bg-zinc-900 shadow-2xl border-4 border-wedding-gold-100 dark:border-wedding-gold-900/30 overflow-hidden">
143
+ <CardBody className="p-6 md:p-8 flex flex-col items-center">
144
+ <div className="relative w-48 h-48 md:w-64 md:h-64 bg-white p-2 rounded-xl shadow-inner">
145
+ <Image
146
+ fill
147
+ alt="Sagun QR Code"
148
+ className="object-contain rounded-lg"
149
+ src="/qr.png"
150
+ />
151
+ </div>
152
+ <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]`}>
154
+ Scan to Bless
155
+ </p>
156
+ </div>
157
+ </CardBody>
158
+ </Card>
159
+ </div>
160
+
161
+ {/* UPI Details */}
162
+ <div className="text-center md:text-left space-y-6 md:space-y-8 order-1 md:order-2">
163
+ <div>
164
+ <h2
165
+ className={`${fontCursive.className} text-4xl md:text-5xl text-wedding-gold-600 dark:text-wedding-gold-400 mb-2 md:mb-4`}
166
+ >
167
+ UPI Details
168
+ </h2>
169
+ <p className="text-default-600 dark:text-gray-200 mb-6 md:mb-8 text-base md:text-lg">
170
+ You can use any UPI app to send your token of love directly.
171
+ </p>
172
+
173
+ <div className="flex flex-col items-center md:items-start gap-4 md:gap-6">
174
+ <Snippet
175
+ 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
+ symbol=""
177
+ >
178
+ eugenics@ybl
179
+ </Snippet>
180
+
181
+ <Button
182
+ as="a"
183
+ 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" />}
186
+ >
187
+ Open UPI App
188
+ </Button>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ </section>
194
+
195
+ {/* Wishes Section */}
196
+ <section className="container mx-auto px-4 max-w-6xl">
197
+ <div className="grid lg:grid-cols-5 gap-12">
198
+ {/* Wish Form */}
199
+ <div className="lg:col-span-2 space-y-8">
200
+ <div className="space-y-4">
201
+ <h2 className={`${fontCursive.className} text-5xl text-wedding-pink-600 dark:text-wedding-pink-400`}>
202
+ Leave a Wish
203
+ </h2>
204
+ <p className="text-default-500 dark:text-default-400">
205
+ Your words will be cherished forever. Write a small message for the couple.
206
+ </p>
207
+ </div>
208
+
209
+ <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">
211
+ <Input
212
+ isRequired
213
+ label="Your Name"
214
+ labelPlacement="outside-top"
215
+ placeholder="E.g. Rahul & Priya"
216
+ variant="bordered"
217
+ 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
+ }}
224
+ />
225
+ <Textarea
226
+ 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
+ classNames={{
234
+ label: "text-wedding-pink-600 font-semibold",
235
+ input: "outline-none p-2",
236
+ inputWrapper: "border-wedding-pink-100 focus-within:!border-wedding-pink-500 h-32"
237
+ }}
238
+ />
239
+
240
+ {/* Bot Protection */}
241
+ <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
244
+ 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
+ classNames={{
252
+ label: "text-default-600 text-xs",
253
+ input: "text-center font-bold outline-none",
254
+ }}
255
+ />
256
+ </div>
257
+
258
+ <Button
259
+ type="submit"
260
+ isLoading={submitting}
261
+ className="w-full bg-gradient-to-r from-wedding-pink-500 to-wedding-gold-500 text-white font-bold h-12"
262
+ >
263
+ Send Blessing
264
+ </Button>
265
+ </form>
266
+ </Card>
267
+ </div>
268
+
269
+ {/* Sentiment Wall & Feed */}
270
+ <div className="lg:col-span-3 space-y-12">
271
+ {/* AI Sentiment Wall */}
272
+ <AnimatePresence>
273
+ {sentiment && (
274
+ <motion.div
275
+ initial={{ opacity: 0, scale: 0.9 }}
276
+ animate={{ opacity: 1, scale: 1 }}
277
+ className="relative p-8 rounded-[32px] overflow-hidden group"
278
+ >
279
+ <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
+
281
+ <div className="relative z-10 space-y-4 text-center md:text-left">
282
+ <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`}>
285
+ A Collective Blessing
286
+ </h3>
287
+ </div>
288
+ <p className={`${fontCursive.className} text-3xl md:text-4xl text-default-800 dark:text-white leading-snug`}>
289
+ {sentiment}
290
+ </p>
291
+ </div>
292
+ </motion.div>
293
+ )}
294
+ </AnimatePresence>
295
+
296
+ {/* Wishes Feed */}
297
+ <div className="space-y-6">
298
+ <div className="flex items-center justify-between px-2">
299
+ <h3 className={`${fontSans.className} text-xl font-bold text-default-800 dark:text-white`}>
300
+ Recent Wishes
301
+ </h3>
302
+ <span className="text-xs font-mono text-default-400 bg-default-100 dark:bg-zinc-800 px-3 py-1 rounded-full">
303
+ {wishes.length} Blessings
304
+ </span>
305
+ </div>
306
+
307
+ <Divider className="opacity-50" />
308
+
309
+ <div className="grid gap-6">
310
+ {loading ? (
311
+ <div className="flex justify-center py-10">
312
+ <Spinner color="danger" />
313
+ </div>
314
+ ) : 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>
316
+ ) : (
317
+ wishes.map((wish, i) => (
318
+ <motion.div
319
+ key={wish.id}
320
+ initial={{ opacity: 0, x: 20 }}
321
+ animate={{ opacity: 1, x: 0 }}
322
+ transition={{ delay: i * 0.1 }}
323
+ >
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">
325
+ <CardBody className="p-6">
326
+ <div className="flex items-start gap-4">
327
+ <Avatar
328
+ name={wish.name}
329
+ className="bg-wedding-pink-100 text-wedding-pink-600 font-bold flex-shrink-0"
330
+ />
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>
334
+ </div>
335
+ </div>
336
+ </CardBody>
337
+ </Card>
338
+ </motion.div>
339
+ ))
340
+ )}
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </section>
346
+ </div>
347
+ );
348
+ }