@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.
- package/.eslintignore +20 -0
- package/.eslintrc.json +93 -0
- package/.recover +9 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/app/Neo-Lucentism/layout.tsx +7 -0
- package/app/Neo-Lucentism/page.tsx +259 -0
- package/app/couple/layout.tsx +7 -0
- package/app/couple/page.tsx +164 -0
- package/app/error.tsx +31 -0
- package/app/guestbook/page.tsx +470 -0
- package/app/invitation/[slug]/layout.tsx +36 -0
- package/app/invitation/[slug]/page.tsx +462 -0
- package/app/invitation/maker/auth.js +165 -0
- package/app/invitation/maker/dashboard.js +81 -0
- package/app/invitation/maker/guestAdder.js +204 -0
- package/app/invitation/maker/guestShower.js +287 -0
- package/app/invitation/maker/layout.tsx +11 -0
- package/app/invitation/maker/page.js +168 -0
- package/app/invitation/maker/rsvpViewer.js +122 -0
- package/app/layout.tsx +98 -0
- package/app/mark-the-dates/layout.tsx +7 -0
- package/app/mark-the-dates/page.tsx +196 -0
- package/app/memories/layout.tsx +7 -0
- package/app/memories/page.tsx +29 -0
- package/app/page.tsx +5 -0
- package/app/providers.tsx +33 -0
- package/app/sagun/layout.tsx +7 -0
- package/app/sagun/page.tsx +348 -0
- package/app/song-requests/page.tsx +354 -0
- package/app/sukanya/layout.tsx +7 -0
- package/app/sukanya/page.tsx +167 -0
- package/app/titas/layout.tsx +7 -0
- package/app/titas/page.tsx +175 -0
- package/app/travel-guide/page.tsx +400 -0
- package/app/updates/maker/page.js +323 -0
- package/app/updates/overlay/page.tsx +144 -0
- package/app/updates/page.js +207 -0
- package/cli.mjs +196 -0
- package/components/ConciergeBot.tsx +203 -0
- package/components/CountdownTimer.tsx +137 -0
- package/components/Gallery.tsx +372 -0
- package/components/LiveVideos.tsx +173 -0
- package/components/OurStory.tsx +160 -0
- package/components/certificate.jsx +300 -0
- package/components/counter.tsx +14 -0
- package/components/footer.tsx +89 -0
- package/components/hero.tsx +136 -0
- package/components/icons.tsx +283 -0
- package/components/importantNews.js +168 -0
- package/components/navbar.tsx +106 -0
- package/components/primitives.ts +53 -0
- package/components/sagun.js +22 -0
- package/components/theme-switch.tsx +81 -0
- package/components/updates.tsx +118 -0
- package/components/weddingcard.js +68 -0
- package/components/weddingcard2.js +58 -0
- package/config/firebase-admin.js +17 -0
- package/config/firebase.ts +36 -0
- package/config/fonts.ts +21 -0
- package/config/site.ts +74 -0
- package/next-env.d.ts +6 -0
- package/next.config.js +4 -0
- package/package.json +64 -0
- package/postcss.config.js +6 -0
- 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/20.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/corner1-01.svg +1 -0
- package/public/favicon.ico +0 -0
- package/public/groom.jpg +0 -0
- package/public/invite.png +0 -0
- package/public/love-birds.png +0 -0
- package/public/next.svg +1 -0
- package/public/pubqr.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/public/qr.png +0 -0
- package/public/vercel.svg +1 -0
- package/styles/globals.css +3 -0
- package/tailwind.config.js +51 -0
- package/tsconfig.json +45 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/types/index.ts +5 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Card, CardHeader, CardBody } from "@heroui/react";
|
|
4
|
+
|
|
5
|
+
const Dash = ({ guests }) => {
|
|
6
|
+
const stats = {
|
|
7
|
+
reception: { bride: 0, groom: 0, total: 0 },
|
|
8
|
+
wedding: { bride: 0, groom: 0, total: 0 },
|
|
9
|
+
registration: { bride: 0, groom: 0, total: 0 },
|
|
10
|
+
rsvpStatus: { accepted: 0, pending: 0, declined: 0, total: guests.length },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
guests.forEach((guest) => {
|
|
14
|
+
const side = guest.familySide === "groom" ? "groom" : "bride";
|
|
15
|
+
const count = guest.invitedGuests || 1;
|
|
16
|
+
|
|
17
|
+
guest.invitedFor.forEach((event) => {
|
|
18
|
+
if (stats[event]) {
|
|
19
|
+
stats[event][side] += count;
|
|
20
|
+
stats[event].total += count;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const rsvp = guest.rsvpStatus?.toLowerCase();
|
|
25
|
+
if (rsvp === "accepted") stats.rsvpStatus.accepted += 1;
|
|
26
|
+
else if (rsvp === "pending") stats.rsvpStatus.pending += 1;
|
|
27
|
+
else if (rsvp === "declined") stats.rsvpStatus.declined += 1;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const eventCards = [
|
|
31
|
+
{ title: "Reception", data: stats.reception },
|
|
32
|
+
{ title: "Wedding", data: stats.wedding },
|
|
33
|
+
{ title: "Registration", data: stats.registration },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 p-4">
|
|
38
|
+
{eventCards.map((event, idx) => (
|
|
39
|
+
<Card key={idx} shadow="none" className="rounded-xl">
|
|
40
|
+
<CardHeader>
|
|
41
|
+
<h3 className="text-lg font-semibold">{event.title}</h3>
|
|
42
|
+
</CardHeader>
|
|
43
|
+
<CardBody className="space-y-1 text-sm ">
|
|
44
|
+
<p>Bride Side: {event.data.bride}</p>
|
|
45
|
+
<p>Groom Side: {event.data.groom}</p>
|
|
46
|
+
<p>Total: {event.data.total}</p>
|
|
47
|
+
</CardBody>
|
|
48
|
+
</Card>
|
|
49
|
+
))}
|
|
50
|
+
|
|
51
|
+
<Card
|
|
52
|
+
shadow="none"
|
|
53
|
+
className="rounded-xl md:col-span-2 xl:col-span-3"
|
|
54
|
+
>
|
|
55
|
+
<CardHeader>
|
|
56
|
+
<h3 className="text-lg font-semibold">RSVP Summary</h3>
|
|
57
|
+
</CardHeader>
|
|
58
|
+
<CardBody className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm ">
|
|
59
|
+
<div>
|
|
60
|
+
<p>Accepted:</p>
|
|
61
|
+
<p className="font-medium">{stats.rsvpStatus.accepted}</p>
|
|
62
|
+
</div>
|
|
63
|
+
<div>
|
|
64
|
+
<p>Pending:</p>
|
|
65
|
+
<p className="font-medium">{stats.rsvpStatus.pending}</p>
|
|
66
|
+
</div>
|
|
67
|
+
<div>
|
|
68
|
+
<p>Declined:</p>
|
|
69
|
+
<p className="font-medium">{stats.rsvpStatus.declined}</p>
|
|
70
|
+
</div>
|
|
71
|
+
<div>
|
|
72
|
+
<p>Total Names:</p>
|
|
73
|
+
<p className="font-medium">{stats.rsvpStatus.total}</p>
|
|
74
|
+
</div>
|
|
75
|
+
</CardBody>
|
|
76
|
+
</Card>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default Dash;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Modal,
|
|
6
|
+
ModalContent,
|
|
7
|
+
ModalHeader,
|
|
8
|
+
ModalBody,
|
|
9
|
+
ModalFooter,
|
|
10
|
+
Button,
|
|
11
|
+
Input,
|
|
12
|
+
Textarea,
|
|
13
|
+
RadioGroup,
|
|
14
|
+
Radio,
|
|
15
|
+
CheckboxGroup,
|
|
16
|
+
Checkbox,
|
|
17
|
+
} from "@heroui/react";
|
|
18
|
+
import { fontSans, fontMono } from "@/config/fonts";
|
|
19
|
+
|
|
20
|
+
const defaultForm = {
|
|
21
|
+
name: "",
|
|
22
|
+
contact: "",
|
|
23
|
+
familySide: "",
|
|
24
|
+
relation: "",
|
|
25
|
+
invitedFor: [],
|
|
26
|
+
invitedGuests: "1",
|
|
27
|
+
rsvpStatus: "pending",
|
|
28
|
+
notes: "",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default function AddGuestModal({
|
|
32
|
+
isOpen,
|
|
33
|
+
onClose,
|
|
34
|
+
onAdd, // only onAdd, no onUpdate
|
|
35
|
+
existingGuest,
|
|
36
|
+
}) {
|
|
37
|
+
const [formData, setFormData] = useState(defaultForm);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (existingGuest) {
|
|
41
|
+
setFormData(existingGuest);
|
|
42
|
+
} else {
|
|
43
|
+
setFormData(defaultForm);
|
|
44
|
+
}
|
|
45
|
+
}, [existingGuest]);
|
|
46
|
+
|
|
47
|
+
const handleChange = (e) => {
|
|
48
|
+
setFormData({ ...formData, [e.target.name]: e.target.value });
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleRadioChange = (key, value) => {
|
|
52
|
+
setFormData({ ...formData, [key]: value });
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleCheckboxChange = (key, values) => {
|
|
56
|
+
setFormData({ ...formData, [key]: values });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleSubmit = (e) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
onAdd(formData); // always call onAdd for both add & update
|
|
62
|
+
onClose();
|
|
63
|
+
setFormData(defaultForm);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Modal
|
|
68
|
+
isOpen={isOpen}
|
|
69
|
+
onOpenChange={onClose}
|
|
70
|
+
backdrop="blur"
|
|
71
|
+
scrollBehavior="outside"
|
|
72
|
+
>
|
|
73
|
+
<ModalContent className="max-w-lg w-full">
|
|
74
|
+
{(close) => (
|
|
75
|
+
<form
|
|
76
|
+
onSubmit={(e) => {
|
|
77
|
+
handleSubmit(e);
|
|
78
|
+
close();
|
|
79
|
+
}}
|
|
80
|
+
className={`${fontSans.className} border-none outline-none`}
|
|
81
|
+
>
|
|
82
|
+
<ModalHeader className="text-2xl">
|
|
83
|
+
{existingGuest ? "Update Guest" : "Add New Guest"}
|
|
84
|
+
</ModalHeader>
|
|
85
|
+
<ModalBody>
|
|
86
|
+
<Input
|
|
87
|
+
isRequired
|
|
88
|
+
label="Guest Name"
|
|
89
|
+
placeholder="Enter guest name"
|
|
90
|
+
name="name"
|
|
91
|
+
value={formData.name}
|
|
92
|
+
onChange={handleChange}
|
|
93
|
+
variant="bordered"
|
|
94
|
+
labelPlacement="outside-top"
|
|
95
|
+
size="lg"
|
|
96
|
+
classNames={{
|
|
97
|
+
input: "outline-none",
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
<Input
|
|
101
|
+
label="Contact Info"
|
|
102
|
+
placeholder="Enter phone or email"
|
|
103
|
+
name="contact"
|
|
104
|
+
value={formData.contact}
|
|
105
|
+
onChange={handleChange}
|
|
106
|
+
variant="bordered"
|
|
107
|
+
size="lg"
|
|
108
|
+
labelPlacement="outside-top"
|
|
109
|
+
classNames={{
|
|
110
|
+
input: "outline-none",
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
|
|
114
|
+
<RadioGroup
|
|
115
|
+
isRequired
|
|
116
|
+
label="Family Side"
|
|
117
|
+
orientation="horizontal"
|
|
118
|
+
value={formData.familySide}
|
|
119
|
+
onValueChange={(val) => handleRadioChange("familySide", val)}
|
|
120
|
+
>
|
|
121
|
+
<Radio value="bride">Bride's Side</Radio>
|
|
122
|
+
<Radio value="groom">Groom's Side</Radio>
|
|
123
|
+
</RadioGroup>
|
|
124
|
+
|
|
125
|
+
<RadioGroup
|
|
126
|
+
isRequired
|
|
127
|
+
label="Relation to Couple"
|
|
128
|
+
orientation="horizontal"
|
|
129
|
+
value={formData.relation}
|
|
130
|
+
onValueChange={(val) => handleRadioChange("relation", val)}
|
|
131
|
+
>
|
|
132
|
+
<Radio value="family">Family</Radio>
|
|
133
|
+
<Radio value="colleague">Colleague</Radio>
|
|
134
|
+
<Radio value="friend">Friend</Radio>
|
|
135
|
+
<Radio value="other">Other</Radio>
|
|
136
|
+
</RadioGroup>
|
|
137
|
+
|
|
138
|
+
<CheckboxGroup
|
|
139
|
+
label="Invited For"
|
|
140
|
+
orientation="horizontal"
|
|
141
|
+
value={formData.invitedFor}
|
|
142
|
+
onValueChange={(val) => handleCheckboxChange("invitedFor", val)}
|
|
143
|
+
isRequired
|
|
144
|
+
>
|
|
145
|
+
<Checkbox value="registration">Registration</Checkbox>
|
|
146
|
+
<Checkbox value="wedding">Wedding</Checkbox>
|
|
147
|
+
<Checkbox value="reception">Reception</Checkbox>
|
|
148
|
+
</CheckboxGroup>
|
|
149
|
+
|
|
150
|
+
<Input
|
|
151
|
+
type="number"
|
|
152
|
+
label="Total Guests"
|
|
153
|
+
labelPlacement="outside-top"
|
|
154
|
+
placeholder="1"
|
|
155
|
+
name="invitedGuests"
|
|
156
|
+
min={1}
|
|
157
|
+
value={formData.invitedGuests}
|
|
158
|
+
onChange={handleChange}
|
|
159
|
+
variant="bordered"
|
|
160
|
+
size="lg"
|
|
161
|
+
classNames={{
|
|
162
|
+
input: "outline-none",
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
<RadioGroup
|
|
167
|
+
label="RSVP Status"
|
|
168
|
+
orientation="horizontal"
|
|
169
|
+
value={formData.rsvpStatus}
|
|
170
|
+
onValueChange={(val) => handleRadioChange("rsvpStatus", val)}
|
|
171
|
+
>
|
|
172
|
+
<Radio value="pending">Pending</Radio>
|
|
173
|
+
<Radio value="accepted">Accepted</Radio>
|
|
174
|
+
<Radio value="declined">Declined</Radio>
|
|
175
|
+
<Radio value="maybe">Maybe</Radio>
|
|
176
|
+
</RadioGroup>
|
|
177
|
+
|
|
178
|
+
<Textarea
|
|
179
|
+
label="Notes (Optional)"
|
|
180
|
+
placeholder="Enter any special notes"
|
|
181
|
+
name="notes"
|
|
182
|
+
value={formData.notes}
|
|
183
|
+
onChange={handleChange}
|
|
184
|
+
variant="bordered"
|
|
185
|
+
size="lg"
|
|
186
|
+
classNames={{
|
|
187
|
+
input: "outline-none",
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
</ModalBody>
|
|
191
|
+
<ModalFooter>
|
|
192
|
+
<Button variant="light" onPress={onClose}>
|
|
193
|
+
Cancel
|
|
194
|
+
</Button>
|
|
195
|
+
<Button color="primary" type="submit">
|
|
196
|
+
{existingGuest ? "Update Guest" : "Save Guest"}
|
|
197
|
+
</Button>
|
|
198
|
+
</ModalFooter>
|
|
199
|
+
</form>
|
|
200
|
+
)}
|
|
201
|
+
</ModalContent>
|
|
202
|
+
</Modal>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Chip,
|
|
5
|
+
Button,
|
|
6
|
+
Select,
|
|
7
|
+
SelectItem,
|
|
8
|
+
Card,
|
|
9
|
+
CardBody,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardFooter,
|
|
12
|
+
Link,
|
|
13
|
+
} from "@heroui/react";
|
|
14
|
+
import { fontSans, fontMono } from "@/config/fonts";
|
|
15
|
+
import { useState, useMemo } from "react";
|
|
16
|
+
|
|
17
|
+
const familyChipColor = {
|
|
18
|
+
bride: "primary",
|
|
19
|
+
groom: "secondary",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const rsvpChipColor = {
|
|
23
|
+
accepted: "success",
|
|
24
|
+
declined: "danger",
|
|
25
|
+
maybe: "warning",
|
|
26
|
+
pending: "default",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const rsvpBorderColor = {
|
|
30
|
+
accepted: "border-success",
|
|
31
|
+
declined: "border-danger",
|
|
32
|
+
maybe: "border-warning",
|
|
33
|
+
pending: "border-default",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const invitedForColor = {
|
|
37
|
+
registration: "success",
|
|
38
|
+
reception: "danger",
|
|
39
|
+
wedding: "warning",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default function GuestTable({ guests, onEdit, onDelete, onAdd }) {
|
|
43
|
+
const [filters, setFilters] = useState({
|
|
44
|
+
familySide: "all",
|
|
45
|
+
invitedFor: "all",
|
|
46
|
+
relation: "all",
|
|
47
|
+
rsvpStatus: "all",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const handleFilterChange = (key, value) => {
|
|
51
|
+
setFilters((prev) => ({
|
|
52
|
+
...prev,
|
|
53
|
+
[key]: value,
|
|
54
|
+
}));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const filteredGuests = useMemo(() => {
|
|
58
|
+
return guests.filter((guest) => {
|
|
59
|
+
if (
|
|
60
|
+
filters.familySide !== "all" &&
|
|
61
|
+
guest.familySide !== filters.familySide
|
|
62
|
+
)
|
|
63
|
+
return false;
|
|
64
|
+
if (
|
|
65
|
+
filters.invitedFor !== "all" &&
|
|
66
|
+
!guest.invitedFor.includes(filters.invitedFor)
|
|
67
|
+
)
|
|
68
|
+
return false;
|
|
69
|
+
if (filters.relation !== "all" && guest.relation !== filters.relation)
|
|
70
|
+
return false;
|
|
71
|
+
if (
|
|
72
|
+
filters.rsvpStatus !== "all" &&
|
|
73
|
+
guest.rsvpStatus !== filters.rsvpStatus
|
|
74
|
+
)
|
|
75
|
+
return false;
|
|
76
|
+
return true;
|
|
77
|
+
});
|
|
78
|
+
}, [guests, filters]);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className={`${fontSans.className}`}>
|
|
82
|
+
{/* FILTER SECTION */}
|
|
83
|
+
<Card className="mb-4 w-full shadow-sm bg-transparent">
|
|
84
|
+
<CardBody className="p-2 flex flex-col gap-4 sm:flex-row sm:justify-between sm:items-center">
|
|
85
|
+
<Button onPress={onAdd} color="primary" className="w-full sm:w-auto">
|
|
86
|
+
Add Guest
|
|
87
|
+
</Button>
|
|
88
|
+
|
|
89
|
+
<div className="flex flex-wrap gap-3 w-full sm:w-auto justify-start sm:justify-end">
|
|
90
|
+
<Select
|
|
91
|
+
label="Family Side"
|
|
92
|
+
variant="bordered"
|
|
93
|
+
size="sm"
|
|
94
|
+
labelPlacement="inside"
|
|
95
|
+
className="min-w-[140px] max-w-[160px] flex-1 [&_label]:!pb-1.5 [&_[data-slot=value]]:!pt-1.5"
|
|
96
|
+
selectedKeys={[filters.familySide]}
|
|
97
|
+
onSelectionChange={(e) =>
|
|
98
|
+
handleFilterChange("familySide", Array.from(e)[0])
|
|
99
|
+
}
|
|
100
|
+
>
|
|
101
|
+
<SelectItem key="all">All</SelectItem>
|
|
102
|
+
<SelectItem key="bride">Bride</SelectItem>
|
|
103
|
+
<SelectItem key="groom">Groom</SelectItem>
|
|
104
|
+
</Select>
|
|
105
|
+
|
|
106
|
+
<Select
|
|
107
|
+
label="Invited For"
|
|
108
|
+
variant="bordered"
|
|
109
|
+
size="sm"
|
|
110
|
+
labelPlacement="inside"
|
|
111
|
+
className="min-w-[140px] max-w-[160px] flex-1 [&_label]:!pb-1.5 [&_[data-slot=value]]:!pt-1.5"
|
|
112
|
+
selectedKeys={[filters.invitedFor]}
|
|
113
|
+
onSelectionChange={(e) =>
|
|
114
|
+
handleFilterChange("invitedFor", Array.from(e)[0])
|
|
115
|
+
}
|
|
116
|
+
>
|
|
117
|
+
<SelectItem key="all">All</SelectItem>
|
|
118
|
+
<SelectItem key="registration">Registration</SelectItem>
|
|
119
|
+
<SelectItem key="wedding">Wedding</SelectItem>
|
|
120
|
+
<SelectItem key="reception">Reception</SelectItem>
|
|
121
|
+
</Select>
|
|
122
|
+
|
|
123
|
+
<Select
|
|
124
|
+
label="Relation"
|
|
125
|
+
variant="bordered"
|
|
126
|
+
size="sm"
|
|
127
|
+
labelPlacement="inside"
|
|
128
|
+
className="min-w-[140px] max-w-[160px] flex-1 [&_label]:!pb-1.5 [&_[data-slot=value]]:!pt-1.5"
|
|
129
|
+
selectedKeys={[filters.relation]}
|
|
130
|
+
onSelectionChange={(e) =>
|
|
131
|
+
handleFilterChange("relation", Array.from(e)[0])
|
|
132
|
+
}
|
|
133
|
+
>
|
|
134
|
+
<SelectItem key="all">All</SelectItem>
|
|
135
|
+
<SelectItem key="friend">Friend</SelectItem>
|
|
136
|
+
<SelectItem key="family">Family</SelectItem>
|
|
137
|
+
<SelectItem key="colleague">Colleague</SelectItem>
|
|
138
|
+
<SelectItem key="other">Other</SelectItem>
|
|
139
|
+
</Select>
|
|
140
|
+
|
|
141
|
+
<Select
|
|
142
|
+
label="RSVP"
|
|
143
|
+
variant="bordered"
|
|
144
|
+
size="sm"
|
|
145
|
+
labelPlacement="inside"
|
|
146
|
+
className="min-w-[140px] max-w-[160px] flex-1 [&_label]:!pb-1.5 [&_[data-slot=value]]:!pt-1.5"
|
|
147
|
+
selectedKeys={[filters.rsvpStatus]}
|
|
148
|
+
onSelectionChange={(e) =>
|
|
149
|
+
handleFilterChange("rsvpStatus", Array.from(e)[0])
|
|
150
|
+
}
|
|
151
|
+
>
|
|
152
|
+
<SelectItem key="all">All</SelectItem>
|
|
153
|
+
<SelectItem key="accepted">Accepted</SelectItem>
|
|
154
|
+
<SelectItem key="declined">Declined</SelectItem>
|
|
155
|
+
<SelectItem key="maybe">Maybe</SelectItem>
|
|
156
|
+
<SelectItem key="pending">Pending</SelectItem>
|
|
157
|
+
</Select>
|
|
158
|
+
</div>
|
|
159
|
+
</CardBody>
|
|
160
|
+
</Card>
|
|
161
|
+
|
|
162
|
+
{/* CARD VIEW (ALL SCREENS) */}
|
|
163
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 w-full">
|
|
164
|
+
{filteredGuests.map((guest, index) => (
|
|
165
|
+
<Card
|
|
166
|
+
key={index}
|
|
167
|
+
className={`flex text-left flex-col justify-between mb-2 border-t-4 ${rsvpBorderColor[guest.rsvpStatus]} shadow-md h-full`}
|
|
168
|
+
>
|
|
169
|
+
<CardHeader className="flex justify-between items-center px-4 pt-4 pb-2">
|
|
170
|
+
<div>
|
|
171
|
+
<p className="font-bold text-base">{guest.name}</p>
|
|
172
|
+
<p className="text-sm capitalize">
|
|
173
|
+
{guest.relation} •{" "}
|
|
174
|
+
<span className="capitalize">{guest.familySide}</span>
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
<Chip
|
|
178
|
+
size="sm"
|
|
179
|
+
color={familyChipColor[guest.familySide]}
|
|
180
|
+
variant="flat"
|
|
181
|
+
className="capitalize"
|
|
182
|
+
>
|
|
183
|
+
{guest.familySide}
|
|
184
|
+
</Chip>
|
|
185
|
+
</CardHeader>
|
|
186
|
+
|
|
187
|
+
<CardBody className="px-4 py-2 space-y-2 text-sm grow">
|
|
188
|
+
<p>
|
|
189
|
+
<strong>Contact:</strong>{" "}
|
|
190
|
+
{guest.contact?.length >= 2
|
|
191
|
+
? `${guest.contact.charAt(0)}${"*".repeat(
|
|
192
|
+
guest.contact.length - 2
|
|
193
|
+
)}${guest.contact.charAt(guest.contact.length - 1)}`
|
|
194
|
+
: guest.contact}
|
|
195
|
+
</p>
|
|
196
|
+
<section>
|
|
197
|
+
<strong>Invited For:</strong>
|
|
198
|
+
<div className="flex flex-wrap gap-1 mt-1">
|
|
199
|
+
{guest.invitedFor.map((item, i) => (
|
|
200
|
+
<Chip
|
|
201
|
+
key={i}
|
|
202
|
+
variant="flat"
|
|
203
|
+
size="sm"
|
|
204
|
+
color={invitedForColor[item.toLowerCase()]}
|
|
205
|
+
className={fontMono.className}
|
|
206
|
+
>
|
|
207
|
+
{item}
|
|
208
|
+
</Chip>
|
|
209
|
+
))}
|
|
210
|
+
</div>
|
|
211
|
+
</section>
|
|
212
|
+
<p>
|
|
213
|
+
<strong>Guests Count:</strong> {guest.invitedGuests}
|
|
214
|
+
</p>
|
|
215
|
+
{guest.notes && (
|
|
216
|
+
<p>
|
|
217
|
+
<strong>Notes:</strong> {guest.notes}
|
|
218
|
+
</p>
|
|
219
|
+
)}
|
|
220
|
+
<div className="flex flex-wrap gap-1 mt-2">
|
|
221
|
+
<Link
|
|
222
|
+
color="foreground"
|
|
223
|
+
showAnchorIcon
|
|
224
|
+
href={`./${guest.id}`}
|
|
225
|
+
className="bg-yellow-500 text-black dark:text-white px-3 py-1 rounded hover:bg-yellow-700"
|
|
226
|
+
>
|
|
227
|
+
invitation
|
|
228
|
+
</Link>
|
|
229
|
+
<button
|
|
230
|
+
onClick={() => {
|
|
231
|
+
if (navigator.share) {
|
|
232
|
+
navigator
|
|
233
|
+
.share({
|
|
234
|
+
title: "Wedding Invitation",
|
|
235
|
+
text: `Join us! Here’s your invitation link:`,
|
|
236
|
+
url: `${window.location.origin}/invitation/${guest.id}`,
|
|
237
|
+
})
|
|
238
|
+
.catch((error) =>
|
|
239
|
+
console.error("Error sharing", error)
|
|
240
|
+
);
|
|
241
|
+
} else {
|
|
242
|
+
alert("Sharing is not supported in this browser.");
|
|
243
|
+
}
|
|
244
|
+
}}
|
|
245
|
+
className="bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700"
|
|
246
|
+
>
|
|
247
|
+
Share
|
|
248
|
+
</button>
|
|
249
|
+
{guest.contact && (
|
|
250
|
+
<a
|
|
251
|
+
href={`https://wa.me/${guest.contact.replace(/[^0-9]/g, "")}?text=${encodeURIComponent(
|
|
252
|
+
`Hi! You are invited to Titas & Sukanya's wedding. Here's your invitation link: ${window.location.origin}/${guest.id}`
|
|
253
|
+
)}`}
|
|
254
|
+
target="_blank"
|
|
255
|
+
rel="noopener noreferrer"
|
|
256
|
+
className="bg-green-600 text-white px-3 py-1 rounded hover:bg-green-700"
|
|
257
|
+
>
|
|
258
|
+
WhatsApp
|
|
259
|
+
</a>
|
|
260
|
+
)}
|
|
261
|
+
</div>
|
|
262
|
+
</CardBody>
|
|
263
|
+
|
|
264
|
+
<CardFooter className="flex justify-end gap-2 px-4 pb-4 pt-2">
|
|
265
|
+
<Button
|
|
266
|
+
size="sm"
|
|
267
|
+
variant="bordered"
|
|
268
|
+
color="primary"
|
|
269
|
+
onPress={() => onEdit(guest)}
|
|
270
|
+
>
|
|
271
|
+
Edit
|
|
272
|
+
</Button>
|
|
273
|
+
<Button
|
|
274
|
+
size="sm"
|
|
275
|
+
variant="bordered"
|
|
276
|
+
color="danger"
|
|
277
|
+
onPress={() => onDelete(guest)}
|
|
278
|
+
>
|
|
279
|
+
Delete
|
|
280
|
+
</Button>
|
|
281
|
+
</CardFooter>
|
|
282
|
+
</Card>
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default function PricingLayout({
|
|
2
|
+
children,
|
|
3
|
+
}: {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
}) {
|
|
6
|
+
return (
|
|
7
|
+
<section className="flex flex-col items-center justify-center gap-4 md:px-20 py-8 md:py-10">
|
|
8
|
+
<div className="inline-block text-center justify-center">{children}</div>
|
|
9
|
+
</section>
|
|
10
|
+
);
|
|
11
|
+
}
|