@scalemule/news-publication 0.0.61 → 0.0.63
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/dist/chunk-3QU6BJVF.js +540 -0
- package/dist/chunk-4WVAT5M2.js +213 -0
- package/dist/chunk-6SBK3D3B.js +347 -0
- package/dist/chunk-CHF3YPDV.js +314 -0
- package/dist/chunk-FHRO2Z3F.js +174 -0
- package/dist/{chunk-DM4KQNJH.js → chunk-FWEYKWKP.js} +1 -1
- package/dist/{chunk-FMCHQI7K.js → chunk-KYUVXEJY.js} +34 -12
- package/dist/{chunk-F53CPZQO.js → chunk-Q77T2S7B.js} +1 -1
- package/dist/chunk-SKG5C3MS.js +379 -0
- package/dist/{chunk-GYYUJYI4.js → chunk-X7AU7DV6.js} +2 -3
- package/dist/{chunk-XPOZGZTM.js → chunk-XSAVJJVW.js} +11 -4
- package/dist/{chunk-6S5FARL3.js → chunk-XWXPERLK.js} +2 -2
- package/dist/cookie-consent-banner.cjs +10 -3
- package/dist/cookie-consent-banner.d.cts +1 -1
- package/dist/cookie-consent-banner.d.ts +1 -1
- package/dist/cookie-consent-banner.js +2 -2
- package/dist/cookie-settings-view.cjs +2 -3
- package/dist/cookie-settings-view.js +2 -2
- package/dist/index.cjs +60 -18
- package/dist/index.d.cts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +10 -8
- package/dist/meet-view.d.cts +1 -1
- package/dist/meet-view.d.ts +1 -1
- package/dist/network-bar.js +2 -2
- package/dist/network-copyright.js +2 -2
- package/dist/network.js +1 -1
- package/dist/pets-plugin.js +4 -347
- package/dist/{snapshot-DmRw8G_B.d.ts → snapshot-CjP7bSXJ.d.ts} +1 -1
- package/dist/snapshot-file.d.cts +2 -2
- package/dist/snapshot-file.d.ts +2 -2
- package/dist/{snapshot-Dx4Lv-lB.d.cts → snapshot-qj4BeNzB.d.cts} +1 -1
- package/dist/toys-forms.cjs +609 -0
- package/dist/toys-forms.d.cts +35 -0
- package/dist/toys-forms.d.ts +35 -0
- package/dist/toys-forms.js +4 -0
- package/dist/toys-interactions.cjs +594 -0
- package/dist/toys-interactions.d.cts +54 -0
- package/dist/toys-interactions.d.ts +54 -0
- package/dist/toys-interactions.js +520 -0
- package/dist/toys-plugin.cjs +713 -0
- package/dist/toys-plugin.d.cts +88 -0
- package/dist/toys-plugin.d.ts +88 -0
- package/dist/toys-plugin.js +10 -0
- package/dist/toys-publication.cjs +437 -0
- package/dist/toys-publication.d.cts +26 -0
- package/dist/toys-publication.d.ts +26 -0
- package/dist/toys-publication.js +72 -0
- package/dist/toys-server.cjs +275 -0
- package/dist/toys-server.d.cts +28 -0
- package/dist/toys-server.d.ts +28 -0
- package/dist/toys-server.js +4 -0
- package/dist/toys-view.cjs +420 -0
- package/dist/toys-view.d.cts +55 -0
- package/dist/toys-view.d.ts +55 -0
- package/dist/toys-view.js +2 -0
- package/dist/toys.cjs +196 -0
- package/dist/toys.d.cts +278 -0
- package/dist/toys.d.ts +278 -0
- package/dist/toys.js +1 -0
- package/dist/{types-BJFCWWi0.d.cts → types-DBi49N8m.d.cts} +12 -3
- package/dist/{types-BJFCWWi0.d.ts → types-DBi49N8m.d.ts} +12 -3
- package/dist/verified-card.cjs +34 -12
- package/dist/verified-card.d.cts +1 -1
- package/dist/verified-card.d.ts +1 -1
- package/dist/verified-card.js +1 -1
- package/package.json +41 -4
- package/toys.css +823 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/toys.ts
|
|
8
|
+
var TOY_CATEGORIES = [
|
|
9
|
+
{ key: "building", label: "Building & construction" },
|
|
10
|
+
{ key: "puzzles", label: "Puzzles" },
|
|
11
|
+
{ key: "games", label: "Board & card games" },
|
|
12
|
+
{ key: "dolls_figures", label: "Dolls & action figures" },
|
|
13
|
+
{ key: "vehicles", label: "Vehicles & trains" },
|
|
14
|
+
{ key: "outdoor", label: "Outdoor & active play" },
|
|
15
|
+
{ key: "arts_crafts", label: "Arts & crafts" },
|
|
16
|
+
{ key: "pretend_play", label: "Pretend play & play sets" },
|
|
17
|
+
{ key: "stem_learning", label: "STEM & learning" },
|
|
18
|
+
{ key: "plush", label: "Plush & soft toys" },
|
|
19
|
+
{ key: "books_media", label: "Books & media" },
|
|
20
|
+
{ key: "baby_toddler", label: "Baby & toddler toys" },
|
|
21
|
+
{ key: "musical", label: "Musical toys" },
|
|
22
|
+
{ key: "other", label: "Other toys" }
|
|
23
|
+
];
|
|
24
|
+
var TOY_AGE_RANGES = [
|
|
25
|
+
{ key: "0-12m", label: "0\u201312 months" },
|
|
26
|
+
{ key: "1-2", label: "1\u20132 years" },
|
|
27
|
+
{ key: "3-5", label: "3\u20135 years" },
|
|
28
|
+
{ key: "6-8", label: "6\u20138 years" },
|
|
29
|
+
{ key: "9-12", label: "9\u201312 years" },
|
|
30
|
+
{ key: "13+", label: "13 and up" },
|
|
31
|
+
{ key: "unknown", label: "Not stated on the item" }
|
|
32
|
+
];
|
|
33
|
+
var TOY_CONDITIONS = [
|
|
34
|
+
{ key: "like_new", label: "Like new" },
|
|
35
|
+
{ key: "good", label: "Good \u2014 normal wear" },
|
|
36
|
+
{ key: "fair", label: "Fair \u2014 visible wear, works" }
|
|
37
|
+
];
|
|
38
|
+
var TOY_EXCLUSIONS = [
|
|
39
|
+
{ key: "car_seats", label: "Car seats and booster seats" },
|
|
40
|
+
{ key: "cribs_sleep", label: "Cribs, bassinets and infant sleep equipment" },
|
|
41
|
+
{ key: "helmets", label: "Helmets and protective gear" },
|
|
42
|
+
{ key: "weapons", label: "Weapons, replica weapons and hard-tipped projectile toys" },
|
|
43
|
+
{ key: "water_beads", label: "Water beads" },
|
|
44
|
+
{ key: "magnets", label: "Loose high-powered magnets and magnet sets" },
|
|
45
|
+
{ key: "recalled", label: "Anything subject to a CPSC recall" },
|
|
46
|
+
{ key: "broken", label: "Visibly unsafe, broken or sharp-edged items" },
|
|
47
|
+
{ key: "batteries", label: "Items with loose button or coin batteries" }
|
|
48
|
+
];
|
|
49
|
+
var TOY_CPSC_RECALLS_URL = "https://www.cpsc.gov/Recalls";
|
|
50
|
+
var TOY_STATUS_LABELS = {
|
|
51
|
+
draft: "Draft",
|
|
52
|
+
pending_review: "Waiting for review",
|
|
53
|
+
available: "Available",
|
|
54
|
+
reserved: "Reserved",
|
|
55
|
+
completed: "Completed",
|
|
56
|
+
rejected: "Needs changes",
|
|
57
|
+
withdrawn: "Withdrawn",
|
|
58
|
+
expired: "Expired",
|
|
59
|
+
suspended: "Suspended for safety review"
|
|
60
|
+
};
|
|
61
|
+
var TOY_SAFETY = {
|
|
62
|
+
verification: "\u201CPhone verified\u201D and \u201Cemail verified\u201D mean a working contact method was confirmed. They are not an identity check, a background check or a statement that someone is trustworthy.",
|
|
63
|
+
recall: "A member's recall check is their own statement after searching the CPSC database. It is not a safety certification, and ToyRound never labels an item recall-free or certified."};
|
|
64
|
+
function ToyRoundMark({ size = 40 }) {
|
|
65
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "toy-mark", width: size, height: size, viewBox: "0 0 64 64", "aria-hidden": "true", focusable: "false", children: [
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "32", cy: "32", r: "24", fill: "none", stroke: "var(--toy-logo-ring, #1f2a44)", strokeWidth: "7" }),
|
|
67
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "32", cy: "32", r: "24", fill: "none", stroke: "var(--toy-logo-arc, #ffb703)", strokeWidth: "7", strokeDasharray: "52 200", strokeLinecap: "round", transform: "rotate(-70 32 32)" }),
|
|
68
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "40", y: "6", width: "16", height: "16", rx: "4", fill: "var(--toy-logo-block, #ef6351)", transform: "rotate(14 48 14)" }),
|
|
69
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "32", cy: "32", r: "8", fill: "var(--toy-logo-core, #2a9d8f)" })
|
|
70
|
+
] });
|
|
71
|
+
}
|
|
72
|
+
async function toyCall(url, method = "GET", data) {
|
|
73
|
+
const response = await fetch(url, {
|
|
74
|
+
method,
|
|
75
|
+
credentials: "same-origin",
|
|
76
|
+
headers: data instanceof FormData ? void 0 : { "content-type": "application/json" },
|
|
77
|
+
body: data === void 0 ? void 0 : data instanceof FormData ? data : JSON.stringify(data)
|
|
78
|
+
});
|
|
79
|
+
const body = await response.json().catch(() => ({}));
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
const error = new Error(typeof body.error === "string" ? body.error : body.error?.message || "Please try again.");
|
|
82
|
+
error.code = typeof body.code === "string" ? body.code : void 0;
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
return body.data ?? body;
|
|
86
|
+
}
|
|
87
|
+
function ToySignIn({ returnTo = "/account", authPath = "/api/paw-auth" }) {
|
|
88
|
+
const [busy, setBusy] = react.useState(false), [error, setError] = react.useState("");
|
|
89
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-signin", children: [
|
|
90
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
91
|
+
"button",
|
|
92
|
+
{
|
|
93
|
+
className: "toy-button",
|
|
94
|
+
disabled: busy,
|
|
95
|
+
onClick: async () => {
|
|
96
|
+
setBusy(true);
|
|
97
|
+
setError("");
|
|
98
|
+
try {
|
|
99
|
+
const { url } = await toyCall(authPath + "/start?" + new URLSearchParams({ next: returnTo }), "POST");
|
|
100
|
+
window.location.assign(url);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
setError(e instanceof Error ? e.message : "Sign-in is unavailable.");
|
|
103
|
+
setBusy(false);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
children: busy ? "Opening sign-in\u2026" : "Continue with your network sign-in"
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "toy-error", children: error }),
|
|
110
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "toy-note", children: "Your existing Bay Area News Network sign-in works here. Browsing never needs an account." })
|
|
111
|
+
] });
|
|
112
|
+
}
|
|
113
|
+
function ToyMemberSetup({ member, apiPath = "/api/toys", compact = false }) {
|
|
114
|
+
const [name, setName] = react.useState(member.display_name === "Neighbor" ? "" : member.display_name), [home, setHome] = react.useState(member.community?.id || ""), [attest, setAttest] = react.useState(member.verification.adult_attested), [revision, setRevision] = react.useState(member.revision), [saved, setSaved] = react.useState(member.verification.adult_attested), [message, setMessage] = react.useState(""), [busy, setBusy] = react.useState(false);
|
|
115
|
+
const [phone, setPhone] = react.useState(""), [code, setCode] = react.useState(""), [sent, setSent] = react.useState(false), [phoneVerified, setPhoneVerified] = react.useState(member.verification.phone_verified), [phoneMessage, setPhoneMessage] = react.useState("");
|
|
116
|
+
const ready = saved && member.verification.email_verified && phoneVerified;
|
|
117
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: `toy-form toy-setup${compact ? " toy-setup-compact" : ""}`, "aria-labelledby": "toy-setup-title", children: [
|
|
118
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { id: "toy-setup-title", children: ready ? "You're ready to give, swap and request." : "Three quick steps before you can list or request." }),
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "toy-checklist", children: [
|
|
120
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: member.verification.email_verified ? "done" : "", children: [
|
|
121
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Verified email" }),
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: member.verification.email_verified ? "Confirmed through your network sign-in." : "Confirm your email from your network account, then sign in again." })
|
|
123
|
+
] }),
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: phoneVerified ? "done" : "", children: [
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Verified phone" }),
|
|
126
|
+
phoneVerified ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
127
|
+
"A working mobile number is confirmed. ",
|
|
128
|
+
TOY_SAFETY.verification
|
|
129
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
130
|
+
"form",
|
|
131
|
+
{
|
|
132
|
+
onSubmit: async (event) => {
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
setBusy(true);
|
|
135
|
+
setPhoneMessage("");
|
|
136
|
+
try {
|
|
137
|
+
if (!sent) {
|
|
138
|
+
await toyCall(apiPath + "/phone", "POST", { action: "send", phone });
|
|
139
|
+
setSent(true);
|
|
140
|
+
setPhoneMessage("We sent a code by text message.");
|
|
141
|
+
} else {
|
|
142
|
+
await toyCall(apiPath + "/phone", "POST", { action: "verify", phone, code });
|
|
143
|
+
setPhoneVerified(true);
|
|
144
|
+
setPhoneMessage("Phone verified.");
|
|
145
|
+
}
|
|
146
|
+
} catch (e) {
|
|
147
|
+
setPhoneMessage(e instanceof Error ? e.message : "Please try again.");
|
|
148
|
+
} finally {
|
|
149
|
+
setBusy(false);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
154
|
+
"Mobile number",
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "tel", value: phone, onChange: (e) => setPhone(e.target.value), placeholder: "+1 925 555 0100", autoComplete: "tel", required: true })
|
|
156
|
+
] }),
|
|
157
|
+
sent && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
158
|
+
"Code from the text message",
|
|
159
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { inputMode: "numeric", value: code, onChange: (e) => setCode(e.target.value), autoComplete: "one-time-code", required: true })
|
|
160
|
+
] }),
|
|
161
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { disabled: busy, children: sent ? "Confirm code" : "Send code" }),
|
|
162
|
+
sent && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "toy-link-button", disabled: busy, onClick: () => setSent(false), children: "Use a different number" }),
|
|
163
|
+
phoneMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", children: phoneMessage }),
|
|
164
|
+
/* @__PURE__ */ jsxRuntime.jsxs("small", { children: [
|
|
165
|
+
"Your number is used for verification and is never shown to other members. ",
|
|
166
|
+
TOY_SAFETY.verification
|
|
167
|
+
] })
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: saved ? "done" : "", children: [
|
|
173
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Your public name, community and 18+ statement" }),
|
|
174
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
175
|
+
"form",
|
|
176
|
+
{
|
|
177
|
+
onSubmit: async (event) => {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
setBusy(true);
|
|
180
|
+
setMessage("");
|
|
181
|
+
try {
|
|
182
|
+
const result = await toyCall(apiPath + "/member", "PUT", {
|
|
183
|
+
expected_revision: revision,
|
|
184
|
+
display_name: name.trim(),
|
|
185
|
+
home_area_id: home || null,
|
|
186
|
+
adult_attestation: { version: member.adult_attestation.version, accepted: attest }
|
|
187
|
+
});
|
|
188
|
+
setRevision(result.revision);
|
|
189
|
+
setSaved(attest);
|
|
190
|
+
setMessage(attest ? "Saved." : "Saved. Confirm the 18+ statement to list or request toys.");
|
|
191
|
+
} catch (e) {
|
|
192
|
+
setMessage(e instanceof Error ? e.message : "Please try again.");
|
|
193
|
+
} finally {
|
|
194
|
+
setBusy(false);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
children: [
|
|
198
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
199
|
+
"First name or display name",
|
|
200
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { value: name, onChange: (e) => setName(e.target.value), maxLength: 60, required: true, placeholder: "Shown on your listings" })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
203
|
+
"Home community",
|
|
204
|
+
/* @__PURE__ */ jsxRuntime.jsxs("select", { value: home, onChange: (e) => setHome(e.target.value), children: [
|
|
205
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Choose a community" }),
|
|
206
|
+
member.communities.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.id, children: c.name }, c.id))
|
|
207
|
+
] })
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
210
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: attest, onChange: (e) => setAttest(e.target.checked) }),
|
|
211
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: member.adult_attestation.text })
|
|
212
|
+
] }),
|
|
213
|
+
/* @__PURE__ */ jsxRuntime.jsx("small", { children: "We do not ask for your birth date, identity documents or any child's details." }),
|
|
214
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { disabled: busy, children: "Save" }),
|
|
215
|
+
message && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", children: message })
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
] })
|
|
220
|
+
] }),
|
|
221
|
+
member.status === "suspended" && /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "toy-suspended", role: "status", children: [
|
|
222
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Your account is suspended from the exchange." }),
|
|
223
|
+
member.moderation_note && /* @__PURE__ */ jsxRuntime.jsx("p", { children: member.moderation_note }),
|
|
224
|
+
member.suspended_until && /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
225
|
+
"Until ",
|
|
226
|
+
new Date(member.suspended_until).toLocaleDateString(),
|
|
227
|
+
"."
|
|
228
|
+
] }),
|
|
229
|
+
member.appeal_status === "open" ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Your appeal is with the editors." }) : /* @__PURE__ */ jsxRuntime.jsx(ToyAppealForm, { apiPath, revision, name: name || member.display_name, home, onSaved: setRevision })
|
|
230
|
+
] })
|
|
231
|
+
] });
|
|
232
|
+
}
|
|
233
|
+
function ToyAppealForm({ apiPath, revision, name, home, onSaved }) {
|
|
234
|
+
const [note, setNote] = react.useState(""), [message, setMessage] = react.useState(""), [busy, setBusy] = react.useState(false);
|
|
235
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
236
|
+
"form",
|
|
237
|
+
{
|
|
238
|
+
onSubmit: async (event) => {
|
|
239
|
+
event.preventDefault();
|
|
240
|
+
setBusy(true);
|
|
241
|
+
try {
|
|
242
|
+
const result = await toyCall(apiPath + "/member", "PUT", { expected_revision: revision, display_name: name, home_area_id: home || null, appeal_note: note });
|
|
243
|
+
onSaved(result.revision);
|
|
244
|
+
setMessage("Your appeal was sent to the editors.");
|
|
245
|
+
} catch (e) {
|
|
246
|
+
setMessage(e instanceof Error ? e.message : "Please try again.");
|
|
247
|
+
} finally {
|
|
248
|
+
setBusy(false);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
253
|
+
"Appeal",
|
|
254
|
+
/* @__PURE__ */ jsxRuntime.jsx("textarea", { value: note, onChange: (e) => setNote(e.target.value), maxLength: 2e3, required: true })
|
|
255
|
+
] }),
|
|
256
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { disabled: busy, children: "Send appeal" }),
|
|
257
|
+
message && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", children: message })
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
var emptyContent = {
|
|
263
|
+
title: "",
|
|
264
|
+
description: "",
|
|
265
|
+
brand: "",
|
|
266
|
+
model: "",
|
|
267
|
+
completeness: "complete",
|
|
268
|
+
missing_parts: "",
|
|
269
|
+
age_guidance: "",
|
|
270
|
+
warnings: "",
|
|
271
|
+
swap_wishes: "",
|
|
272
|
+
recall_check: { searched_cpsc: false, searched_at: null, search_terms: "", result: "", notes: "" },
|
|
273
|
+
attestations: { original_photos: false, damage_disclosed: false, not_excluded: false, not_recalled: false }
|
|
274
|
+
};
|
|
275
|
+
function ToyListingForm({
|
|
276
|
+
communities,
|
|
277
|
+
policy,
|
|
278
|
+
initial,
|
|
279
|
+
defaultCommunity,
|
|
280
|
+
apiPath = "/api/toys",
|
|
281
|
+
accountUrl = "/account",
|
|
282
|
+
listingBasePath = "/toys"
|
|
283
|
+
}) {
|
|
284
|
+
const [content, setContent] = react.useState(initial?.content || emptyContent), [exchangeType, setExchangeType] = react.useState(initial?.exchange_type || "giveaway"), [category, setCategory] = react.useState(initial?.category || "building"), [ageRange, setAgeRange] = react.useState(initial?.age_range || "3-5"), [condition, setCondition] = react.useState(initial?.condition || "good"), [community, setCommunity] = react.useState(initial?.area_id || defaultCommunity || ""), [files, setFiles] = react.useState([]), [step, setStep] = react.useState(0), [consent, setConsent] = react.useState(false), [busy, setBusy] = react.useState(false), [progress, setProgress] = react.useState(0), [error, setError] = react.useState(""), [saved, setSaved] = react.useState();
|
|
285
|
+
const receipt = react.useRef(initial ? { id: initial.id, revision: initial.revision } : null);
|
|
286
|
+
const key = react.useRef("");
|
|
287
|
+
const uploads = react.useRef(/* @__PURE__ */ new Set());
|
|
288
|
+
react.useEffect(() => () => files.forEach((f) => URL.revokeObjectURL(f.preview)), [files]);
|
|
289
|
+
const change = (field, value) => setContent((c) => ({ ...c, [field]: value }));
|
|
290
|
+
const a = content.attestations;
|
|
291
|
+
async function save(submit) {
|
|
292
|
+
setBusy(true);
|
|
293
|
+
setError("");
|
|
294
|
+
try {
|
|
295
|
+
if (!community) throw new Error("Choose the community where you'll exchange.");
|
|
296
|
+
if (submit && !files.length && !initial?.media.length) throw new Error("Add at least one photo of the actual toy.");
|
|
297
|
+
if (submit && !consent) throw new Error("Read and accept the publication permission.");
|
|
298
|
+
const input = {
|
|
299
|
+
area_id: community,
|
|
300
|
+
exchange_type: exchangeType,
|
|
301
|
+
category,
|
|
302
|
+
age_range: ageRange,
|
|
303
|
+
condition,
|
|
304
|
+
content: { ...content, swap_wishes: exchangeType === "swap" ? content.swap_wishes : "", recall_check: { ...content.recall_check, searched_at: content.recall_check.searched_cpsc ? content.recall_check.searched_at || (/* @__PURE__ */ new Date()).toISOString() : null } },
|
|
305
|
+
consent: consent ? { version: policy.version, accepted: true } : null
|
|
306
|
+
};
|
|
307
|
+
if (!key.current) key.current = crypto.randomUUID();
|
|
308
|
+
if (!receipt.current) {
|
|
309
|
+
const result2 = await toyCall(apiPath, "POST", { ...input, submit: false, idempotency_key: key.current });
|
|
310
|
+
receipt.current = { id: result2.id, revision: result2.revision || 1 };
|
|
311
|
+
} else if (initial && progress === 0 && initial.status !== "draft") {
|
|
312
|
+
}
|
|
313
|
+
setProgress(1);
|
|
314
|
+
for (const [index, photo] of files.entries()) {
|
|
315
|
+
if (!uploads.current.has(photo.key)) {
|
|
316
|
+
const form = new FormData();
|
|
317
|
+
form.set("file", photo.file);
|
|
318
|
+
form.set("caption", content.title);
|
|
319
|
+
form.set("attribution", "");
|
|
320
|
+
form.set("rights_confirmed", "true");
|
|
321
|
+
form.set("consent_version", policy.version);
|
|
322
|
+
form.set("idempotency_key", photo.key);
|
|
323
|
+
await toyCall(`${apiPath}/submissions/${receipt.current.id}/media`, "POST", form);
|
|
324
|
+
uploads.current.add(photo.key);
|
|
325
|
+
}
|
|
326
|
+
setProgress(index + 2);
|
|
327
|
+
}
|
|
328
|
+
const result = await toyCall(`${apiPath}/${receipt.current.id}`, "PUT", { ...input, submit, expected_revision: receipt.current.revision });
|
|
329
|
+
receipt.current = { id: result.id, revision: result.revision };
|
|
330
|
+
setSaved({ id: result.id, status: result.status });
|
|
331
|
+
} catch (e) {
|
|
332
|
+
setError(e instanceof Error ? e.message : "We couldn't save this listing. Please try again.");
|
|
333
|
+
} finally {
|
|
334
|
+
setBusy(false);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (saved)
|
|
338
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "toy-form", role: "status", children: [
|
|
339
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToyRoundMark, { size: 56 }),
|
|
340
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { children: saved.status === "draft" ? "Draft saved." : "Thanks \u2014 your listing is in review." }),
|
|
341
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: saved.status === "draft" ? "Only you can see this draft. Submit it when you're ready." : "A local editor checks the photos, the recall search and the disclosures before it appears. You'll see it under My toys." }),
|
|
342
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { className: "toy-button", href: accountUrl, children: "Go to My toys" })
|
|
343
|
+
] });
|
|
344
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
345
|
+
"form",
|
|
346
|
+
{
|
|
347
|
+
className: "toy-form toy-listing-form",
|
|
348
|
+
noValidate: true,
|
|
349
|
+
onSubmit: (event) => {
|
|
350
|
+
event.preventDefault();
|
|
351
|
+
if (!event.currentTarget.reportValidity()) return;
|
|
352
|
+
if (step === 0) {
|
|
353
|
+
setError("");
|
|
354
|
+
setStep(1);
|
|
355
|
+
window.scrollTo({ top: 0 });
|
|
356
|
+
} else void save(true);
|
|
357
|
+
},
|
|
358
|
+
children: [
|
|
359
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "toy-form-intro", children: [
|
|
360
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "toy-form-steps", "aria-label": "Listing progress", children: [
|
|
361
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { "aria-current": step === 0 ? "step" : void 0, children: "1. The toy" }),
|
|
362
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { "aria-current": step === 1 ? "step" : void 0, children: "2. Preview & permission" })
|
|
363
|
+
] }),
|
|
364
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { children: step === 0 ? initial ? "Update your listing." : "Give or swap a toy." : "Looks good." }),
|
|
365
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "toy-note", children: step === 0 ? "Real photos, honest condition, and a quick recall search. Editors review every listing." : "Check the details. Nothing is published until an editor approves it." })
|
|
366
|
+
] }),
|
|
367
|
+
step === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-form-body", children: [
|
|
368
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
|
|
369
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Giveaway or swap?" }),
|
|
370
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-radio", children: [
|
|
371
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "radio", name: "exchange_type", checked: exchangeType === "giveaway", onChange: () => setExchangeType("giveaway") }),
|
|
372
|
+
" Free giveaway \u2014 nothing asked in return"
|
|
373
|
+
] }),
|
|
374
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-radio", children: [
|
|
375
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "radio", name: "exchange_type", checked: exchangeType === "swap", onChange: () => setExchangeType("swap") }),
|
|
376
|
+
" Direct swap \u2014 a neighbor offers one of their toys"
|
|
377
|
+
] }),
|
|
378
|
+
exchangeType === "swap" && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
379
|
+
"What are you hoping to swap for? (optional)",
|
|
380
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { value: content.swap_wishes, maxLength: 300, onChange: (e) => change("swap_wishes", e.target.value), placeholder: "A floor puzzle, a toddler bike\u2026" })
|
|
381
|
+
] })
|
|
382
|
+
] }),
|
|
383
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
384
|
+
"Toy name",
|
|
385
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { required: true, maxLength: 120, value: content.title, onChange: (e) => change("title", e.target.value), placeholder: "Wooden train set" })
|
|
386
|
+
] }),
|
|
387
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-field-row", children: [
|
|
388
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
389
|
+
"Category",
|
|
390
|
+
/* @__PURE__ */ jsxRuntime.jsx("select", { value: category, onChange: (e) => setCategory(e.target.value), children: TOY_CATEGORIES.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.key, children: c.label }, c.key)) })
|
|
391
|
+
] }),
|
|
392
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
393
|
+
"Manufacturer age guidance",
|
|
394
|
+
/* @__PURE__ */ jsxRuntime.jsx("select", { value: ageRange, onChange: (e) => setAgeRange(e.target.value), children: TOY_AGE_RANGES.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.key, children: c.label }, c.key)) })
|
|
395
|
+
] }),
|
|
396
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
397
|
+
"Condition",
|
|
398
|
+
/* @__PURE__ */ jsxRuntime.jsx("select", { value: condition, onChange: (e) => setCondition(e.target.value), children: TOY_CONDITIONS.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.key, children: c.label }, c.key)) })
|
|
399
|
+
] })
|
|
400
|
+
] }),
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
402
|
+
"Community for the exchange",
|
|
403
|
+
/* @__PURE__ */ jsxRuntime.jsxs("select", { required: true, value: community, onChange: (e) => setCommunity(e.target.value), children: [
|
|
404
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Choose a community" }),
|
|
405
|
+
communities.map((c) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: c.id, children: c.name }, c.id))
|
|
406
|
+
] })
|
|
407
|
+
] }),
|
|
408
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
409
|
+
"Description",
|
|
410
|
+
/* @__PURE__ */ jsxRuntime.jsx("textarea", { required: true, maxLength: 3e3, value: content.description, onChange: (e) => change("description", e.target.value), placeholder: "What's included, how it's been used, anything a neighbor should know." })
|
|
411
|
+
] }),
|
|
412
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-field-row", children: [
|
|
413
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
414
|
+
"Brand (if known)",
|
|
415
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { maxLength: 100, value: content.brand, onChange: (e) => change("brand", e.target.value) })
|
|
416
|
+
] }),
|
|
417
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
418
|
+
"Model or item number (if known)",
|
|
419
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { maxLength: 100, value: content.model, onChange: (e) => change("model", e.target.value) })
|
|
420
|
+
] })
|
|
421
|
+
] }),
|
|
422
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
|
|
423
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Completeness and damage" }),
|
|
424
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
425
|
+
"Is it complete?",
|
|
426
|
+
/* @__PURE__ */ jsxRuntime.jsxs("select", { value: content.completeness, onChange: (e) => change("completeness", e.target.value), children: [
|
|
427
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "complete", children: "Complete" }),
|
|
428
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "missing_parts", children: "Missing parts" }),
|
|
429
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "unknown", children: "Not sure" })
|
|
430
|
+
] })
|
|
431
|
+
] }),
|
|
432
|
+
content.completeness === "missing_parts" && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
433
|
+
"Which parts are missing?",
|
|
434
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { required: true, maxLength: 500, value: content.missing_parts, onChange: (e) => change("missing_parts", e.target.value) })
|
|
435
|
+
] }),
|
|
436
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
437
|
+
"Age guidance and warnings printed on the item (if any)",
|
|
438
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { maxLength: 200, value: content.age_guidance, onChange: (e) => change("age_guidance", e.target.value), placeholder: "Ages 3+, small parts" })
|
|
439
|
+
] }),
|
|
440
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
441
|
+
"Other manufacturer warnings",
|
|
442
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { maxLength: 500, value: content.warnings, onChange: (e) => change("warnings", e.target.value) })
|
|
443
|
+
] })
|
|
444
|
+
] }),
|
|
445
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "toy-recall", children: [
|
|
446
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Recall check" }),
|
|
447
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
448
|
+
"Search the",
|
|
449
|
+
" ",
|
|
450
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: TOY_CPSC_RECALLS_URL, target: "_blank", rel: "noopener noreferrer", children: "CPSC recall database" }),
|
|
451
|
+
" ",
|
|
452
|
+
"for the brand and model. Recalled items can't be listed. ",
|
|
453
|
+
TOY_SAFETY.recall
|
|
454
|
+
] }),
|
|
455
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
456
|
+
"What did you search for?",
|
|
457
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { maxLength: 200, value: content.recall_check.search_terms, onChange: (e) => change("recall_check", { ...content.recall_check, search_terms: e.target.value }), placeholder: "Brio train 33773" })
|
|
458
|
+
] }),
|
|
459
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: content.recall_check.searched_cpsc, onChange: (e) => change("recall_check", { ...content.recall_check, searched_cpsc: e.target.checked, searched_at: e.target.checked ? (/* @__PURE__ */ new Date()).toISOString() : null }) }),
|
|
461
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "I searched the CPSC recall database for this item." })
|
|
462
|
+
] }),
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
464
|
+
"Result",
|
|
465
|
+
/* @__PURE__ */ jsxRuntime.jsxs("select", { value: content.recall_check.result, onChange: (e) => change("recall_check", { ...content.recall_check, result: e.target.value }), children: [
|
|
466
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Choose" }),
|
|
467
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "no_match_found", children: "I found no recall for this item" }),
|
|
468
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "unsure", children: "I'm not sure \u2014 please have an editor check" }),
|
|
469
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "possible_match", children: "It may be recalled (cannot be listed)" })
|
|
470
|
+
] })
|
|
471
|
+
] })
|
|
472
|
+
] }),
|
|
473
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { className: "toy-exclusions", children: [
|
|
474
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Not accepted on ToyRound" }),
|
|
475
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { children: TOY_EXCLUSIONS.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item.label }, item.key)) })
|
|
476
|
+
] }),
|
|
477
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-photo-picker", children: [
|
|
478
|
+
"Photos of the actual toy",
|
|
479
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
480
|
+
"input",
|
|
481
|
+
{
|
|
482
|
+
type: "file",
|
|
483
|
+
accept: "image/jpeg,image/png,image/webp",
|
|
484
|
+
multiple: true,
|
|
485
|
+
required: !files.length && !initial?.media.length,
|
|
486
|
+
onChange: (e) => {
|
|
487
|
+
const selected = Array.from(e.target.files || []);
|
|
488
|
+
if (selected.length + (initial?.media.length || 0) > 10 || selected.some((f) => f.size > 8 * 1024 * 1024 || !["image/jpeg", "image/png", "image/webp"].includes(f.type))) {
|
|
489
|
+
setError("Choose up to ten JPEG, PNG or WebP photos, each under 8 MB.");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
setError("");
|
|
493
|
+
setFiles(selected.map((file) => ({ file, key: crypto.randomUUID(), preview: URL.createObjectURL(file) })));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
),
|
|
497
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: files.length ? `${files.length} photo${files.length === 1 ? "" : "s"} selected` : "Choose photos from your device" })
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsx("small", { children: "Location data and other metadata are removed from photos. Please keep children out of the pictures." }),
|
|
500
|
+
files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "toy-upload-previews", "aria-label": "Selected photos", children: files.map((file) => /* @__PURE__ */ jsxRuntime.jsx("img", { src: file.preview, alt: `Selected photo: ${file.file.name}`, width: 120, height: 120 }, file.key)) }),
|
|
501
|
+
initial?.media.map((photo) => /* @__PURE__ */ jsxRuntime.jsx("img", { className: "toy-existing-photo", src: `${apiPath}/submissions/${initial.id}/media/${photo.id}`, alt: photo.caption || content.title, width: 160, height: 120 }, photo.id)),
|
|
502
|
+
/* @__PURE__ */ jsxRuntime.jsxs("fieldset", { children: [
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsx("legend", { children: "Your statements" }),
|
|
504
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
505
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", required: true, checked: a.original_photos, onChange: (e) => change("attestations", { ...a, original_photos: e.target.checked }) }),
|
|
506
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "These are my own photos of this exact item." })
|
|
507
|
+
] }),
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
509
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", required: true, checked: a.damage_disclosed, onChange: (e) => change("attestations", { ...a, damage_disclosed: e.target.checked }) }),
|
|
510
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "I have disclosed all damage, wear and missing pieces." })
|
|
511
|
+
] }),
|
|
512
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
513
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", required: true, checked: a.not_excluded, onChange: (e) => change("attestations", { ...a, not_excluded: e.target.checked }) }),
|
|
514
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "This item is not in a category listed above as not accepted." })
|
|
515
|
+
] }),
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", required: true, checked: a.not_recalled, onChange: (e) => change("attestations", { ...a, not_recalled: e.target.checked }) }),
|
|
518
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "To the best of my knowledge this item has not been recalled and is not visibly unsafe." })
|
|
519
|
+
] })
|
|
520
|
+
] })
|
|
521
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-form-body", children: [
|
|
522
|
+
/* @__PURE__ */ jsxRuntime.jsxs("article", { className: "toy-preview", children: [
|
|
523
|
+
files[0] && /* @__PURE__ */ jsxRuntime.jsx("img", { src: files[0].preview, alt: `Preview of ${content.title}`, width: 800, height: 600 }),
|
|
524
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "toy-context", children: [
|
|
525
|
+
exchangeType === "swap" ? "Swap" : "Free",
|
|
526
|
+
" \xB7 ",
|
|
527
|
+
communities.find((c) => c.id === community)?.name
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { children: content.title }),
|
|
530
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { whiteSpace: "pre-wrap" }, children: content.description }),
|
|
531
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "toy-note", children: [
|
|
532
|
+
TOY_CATEGORIES.find((c) => c.key === category)?.label,
|
|
533
|
+
" \xB7 ",
|
|
534
|
+
TOY_AGE_RANGES.find((c) => c.key === ageRange)?.label,
|
|
535
|
+
" \xB7 ",
|
|
536
|
+
TOY_CONDITIONS.find((c) => c.key === condition)?.label
|
|
537
|
+
] })
|
|
538
|
+
] }),
|
|
539
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { children: [
|
|
540
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Publication permission and guidelines" }),
|
|
541
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: policy.text }),
|
|
542
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: policy.guidelines })
|
|
543
|
+
] }),
|
|
544
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "toy-consent", children: [
|
|
545
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", required: true, checked: consent, onChange: (e) => setConsent(e.target.checked) }),
|
|
546
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "I have read and accept the publication permission." })
|
|
547
|
+
] })
|
|
548
|
+
] }),
|
|
549
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "toy-error", children: error }),
|
|
550
|
+
busy && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
551
|
+
"Saving",
|
|
552
|
+
/* @__PURE__ */ jsxRuntime.jsx("progress", { value: progress, max: files.length + 2 })
|
|
553
|
+
] }),
|
|
554
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-form-actions", children: [
|
|
555
|
+
step === 1 && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "toy-button toy-button-secondary", disabled: busy, onClick: () => setStep(0), children: "Keep editing" }),
|
|
556
|
+
step === 0 && !initial && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "toy-button toy-button-secondary", disabled: busy, onClick: () => void save(false), children: "Save draft" }),
|
|
557
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: "toy-button", disabled: busy, type: "submit", children: busy ? "Saving\u2026" : step === 0 ? "Preview listing" : "Submit for review" })
|
|
558
|
+
] }),
|
|
559
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "toy-note", children: step === 0 ? "Next: a private preview. Nothing is published yet." : "Your listing stays private until an editor approves it." }),
|
|
560
|
+
initial && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "toy-note", children: [
|
|
561
|
+
"Current status: ",
|
|
562
|
+
TOY_STATUS_LABELS[initial.status] || initial.status,
|
|
563
|
+
". ",
|
|
564
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: `${listingBasePath}/${initial.id}`, children: "View listing" })
|
|
565
|
+
] })
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
function ToyOwnerActions({ listing, apiPath = "/api/toys", submitUrl = "/submit" }) {
|
|
571
|
+
const [error, setError] = react.useState(""), [confirm, setConfirm] = react.useState(false), [busy, setBusy] = react.useState(false);
|
|
572
|
+
const editable = !["reserved", "completed", "withdrawn"].includes(listing.status);
|
|
573
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "toy-form-actions toy-owner-actions", children: [
|
|
574
|
+
editable && /* @__PURE__ */ jsxRuntime.jsx("a", { href: `${submitUrl}?edit=${listing.id}`, children: "Edit and resubmit" }),
|
|
575
|
+
!["completed", "withdrawn"].includes(listing.status) && (confirm ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
576
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
577
|
+
"Withdraw this listing",
|
|
578
|
+
listing.status === "reserved" ? " and cancel the current exchange" : "",
|
|
579
|
+
"?"
|
|
580
|
+
] }),
|
|
581
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
582
|
+
"button",
|
|
583
|
+
{
|
|
584
|
+
className: "toy-button toy-button-secondary",
|
|
585
|
+
disabled: busy,
|
|
586
|
+
onClick: async () => {
|
|
587
|
+
setBusy(true);
|
|
588
|
+
try {
|
|
589
|
+
await toyCall(`${apiPath}/${listing.id}`, "DELETE", { expected_revision: listing.revision });
|
|
590
|
+
window.location.reload();
|
|
591
|
+
} catch (e) {
|
|
592
|
+
setError(e instanceof Error ? e.message : "Please try again.");
|
|
593
|
+
setBusy(false);
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
children: "Yes, withdraw"
|
|
597
|
+
}
|
|
598
|
+
),
|
|
599
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { className: "toy-button toy-button-secondary", disabled: busy, onClick: () => setConfirm(false), children: "Keep it" })
|
|
600
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("button", { className: "toy-link-button", disabled: busy, onClick: () => setConfirm(true), children: "Withdraw" })),
|
|
601
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: error })
|
|
602
|
+
] });
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
exports.ToyListingForm = ToyListingForm;
|
|
606
|
+
exports.ToyMemberSetup = ToyMemberSetup;
|
|
607
|
+
exports.ToyOwnerActions = ToyOwnerActions;
|
|
608
|
+
exports.ToySignIn = ToySignIn;
|
|
609
|
+
exports.toyCall = toyCall;
|