1import { useState, useMemo } from "react";
2import { makeId } from "@/utils";
3
4const getDefaultValues = (activity) => {
5 const defaultValue = {
6 activityTitle: "",
7 itemSpends: [
8 {
9 id: makeId(),
10 title: "",
11 amount: 10,
12 },
13 ],
14 selectedFriends: [],
15 isComplete: false,
16 };
17 if (!activity) {
18 return defaultValue;
19 }
20
21 defaultValue.activityTitle = activity.title || "";
22 defaultValue.itemSpends = activity.itemSpends || [];
23 defaultValue.selectedFriends = activity.friends?.map((f) => f.id) || [];
24 defaultValue.isComplete = activity.isCompleted || false;
25
26 return defaultValue;
27};
28
29const ActivityForm = (props) => {
30 const { friends, onSubmit, formInfo } = props;
31 const defaultValues = getDefaultValues(formInfo.defaultValues);
32 const isEdit = formInfo.type === "edit";
33 const isDisabled = defaultValues.isComplete;
34
35 const [selectedFriends, setSelectedFriends] = useState(
36 defaultValues.selectedFriends
37 ); // array of ids
38 const [activityTitle, setActivityTitle] = useState(
39 defaultValues.activityTitle
40 );
41 const [itemSpends, setItemSpends] = useState(defaultValues.itemSpends);
42 const [isComplete, setIsComplete] = useState(defaultValues.isComplete);
43
44 const totalAmount = useMemo(() => {
45 let total = 0;
46 let splitPerPerson = 0;
47 for (const item of itemSpends) {
48 if (item["amount"] !== "") {
49 total += item["amount"];
50 }
51 }
52
53 if (total > 0 && selectedFriends.length) {
54 const totalPeople = 1 + selectedFriends.length; // me + friends
55 splitPerPerson = Math.round(total / totalPeople);
56 }
57
58 return { total, splitPerPerson };
59 }, [itemSpends, selectedFriends]);
60
61 const updateItemSpend = (idx, key, newValue) => {
62 setItemSpends((prev) => {
63 const _itemSpends = [...prev];
64 _itemSpends[idx] = {
65 ..._itemSpends[idx],
66 [key]: newValue,
67 };
68 return _itemSpends;
69 });
70 };
71
72 const addNewItemSpend = () => {
73 setItemSpends((prev) => {
74 const _itemSpends = [...prev];
75 _itemSpends.push({
76 id: makeId(),
77 title: "",
78 amount: 10,
79 });
80 return _itemSpends;
81 });
82 };
83
84 const removeItemSpend = (idx) => {
85 setItemSpends((prev) => {
86 const _itemSpends = [...prev];
87 _itemSpends.splice(idx, 1);
88 return _itemSpends;
89 });
90 };
91
92 const handleSelectionChange = (event) => {
93 const options = event.target.options;
94 const values = [];
95 for (let i = 0; i < options.length; i++) {
96 if (options[i].selected) {
97 values.push(options[i].value);
98 }
99 }
100 setSelectedFriends(values);
101 };
102
103 function handleSubmit(e) {
104 e.preventDefault();
105
106 const activity = {
107 id: makeId(),
108 title: activityTitle,
109 itemSpends,
110 friends: friends.filter((f) => selectedFriends.includes(f.id)),
111 totalAmount: totalAmount.total,
112 splitPerPerson: totalAmount.splitPerPerson,
113 createdAt: new Date().toISOString(),
114 isCompleted: isComplete,
115 };
116
117 if (isEdit) {
118 delete activity["id"];
119 delete activity["createdAt"];
120 }
121
122 onSubmit(activity);
123 return;
124 }
125
126 return (
127 <form onSubmit={handleSubmit}>
128 <label htmlFor="title">what was the activity?</label>
129 <input
130 type="text"
131 name="title"
132 id="title"
133 required
134 placeholder="Trip to the mansion"
135 value={activityTitle}
136 onChange={(e) => setActivityTitle(e.target.value)}
137 disabled={isDisabled}
138 />
139
140 <fieldset disabled={isDisabled}>
141 <legend>items and spends</legend>
142 <ol style={{ paddingLeft: "22px" }}>
143 {itemSpends.map((item, idx) => (
144 <li key={item.id}>
145 <input
146 type="text"
147 placeholder="Food"
148 required
149 value={item.title}
150 onChange={(e) => updateItemSpend(idx, "title", e.target.value)}
151 />
152 <input
153 type="number"
154 placeholder="250"
155 style={{ marginTop: "3px" }}
156 required
157 min={1}
158 value={item.amount}
159 onChange={(e) => {
160 let val = e.target.value;
161 if (val !== "") {
162 val = parseInt(val);
163 }
164 updateItemSpend(idx, "amount", val);
165 }}
166 />
167 {/* cant remove all spends */}
168 {itemSpends.length > 1 && (
169 <button
170 className="btn btn-text"
171 type="button"
172 onClick={() => removeItemSpend(idx)}
173 disabled={isDisabled}
174 >
175 − remove
176 </button>
177 )}
178 </li>
179 ))}
180 </ol>
181
182 <div className="text-right">
183 <button
184 className="btn"
185 type="button"
186 onClick={addNewItemSpend}
187 disabled={isDisabled}
188 >
189 + add
190 </button>
191 </div>
192
193 <div>
194 <p>
195 <small>total:</small> <strong>₹{totalAmount.total}</strong>
196 </p>
197 </div>
198 </fieldset>
199
200 <fieldset disabled={isDisabled}>
201 <legend>friends</legend>
202
203 <select
204 name="friends"
205 id="friends"
206 multiple={true}
207 className="multi-select"
208 required
209 value={selectedFriends}
210 onChange={handleSelectionChange}
211 >
212 {friends.map((f) => (
213 <option value={f.id} key={f.id}>
214 {f.name}
215 </option>
216 ))}
217 </select>
218 <span className="muted">
219 <small>{selectedFriends.length} selected + me</small>
220 </span>
221 </fieldset>
222
223 <div className="text-center">
224 <p>
225 <small>per person:</small>{" "}
226 <strong>₹{totalAmount.splitPerPerson}</strong>
227 </p>
228 </div>
229
230 {isEdit && (
231 <div className="info-box">
232 <label>
233 <input
234 type="checkbox"
235 checked={isComplete}
236 onChange={(e) => {
237 setIsComplete(e.target.checked);
238 }}
239 disabled={isDisabled}
240 />
241 This activity is now complete.
242 </label>
243 </div>
244 )}
245
246 <div className="text-center">
247 <button className="btn" type="submit" disabled={isDisabled}>
248 {isEdit ? "update" : "add"} this activity
249 </button>
250 </div>
251 </form>
252 );
253};
254
255export default ActivityForm;