@scalemule/news-publication 0.0.7

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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/cookie-consent.css +419 -0
  4. package/dist/chunk-C7JTEHTH.js +158 -0
  5. package/dist/chunk-GB36YM56.js +72 -0
  6. package/dist/chunk-PYCU2T5O.js +93 -0
  7. package/dist/chunk-R33MJWND.js +261 -0
  8. package/dist/chunk-TJ7RL3PV.js +252 -0
  9. package/dist/chunk-YQXS2KQI.js +112 -0
  10. package/dist/cookie-consent-banner.cjs +210 -0
  11. package/dist/cookie-consent-banner.d.cts +19 -0
  12. package/dist/cookie-consent-banner.d.ts +19 -0
  13. package/dist/cookie-consent-banner.js +3 -0
  14. package/dist/cookie-settings-view.cjs +470 -0
  15. package/dist/cookie-settings-view.d.cts +15 -0
  16. package/dist/cookie-settings-view.d.ts +15 -0
  17. package/dist/cookie-settings-view.js +3 -0
  18. package/dist/index.cjs +1460 -0
  19. package/dist/index.d.cts +317 -0
  20. package/dist/index.d.ts +317 -0
  21. package/dist/index.js +441 -0
  22. package/dist/marketplace-Ch7mdJZv.d.cts +123 -0
  23. package/dist/marketplace-Ch7mdJZv.d.ts +123 -0
  24. package/dist/marketplace-forms.cjs +134 -0
  25. package/dist/marketplace-forms.d.cts +10 -0
  26. package/dist/marketplace-forms.d.ts +10 -0
  27. package/dist/marketplace-forms.js +115 -0
  28. package/dist/marketplace-view.cjs +110 -0
  29. package/dist/marketplace-view.d.cts +21 -0
  30. package/dist/marketplace-view.d.ts +21 -0
  31. package/dist/marketplace-view.js +85 -0
  32. package/dist/meet-5Ym5bxfh.d.cts +79 -0
  33. package/dist/meet-5Ym5bxfh.d.ts +79 -0
  34. package/dist/meet-forms.cjs +188 -0
  35. package/dist/meet-forms.d.cts +13 -0
  36. package/dist/meet-forms.d.ts +13 -0
  37. package/dist/meet-forms.js +166 -0
  38. package/dist/meet-view.cjs +82 -0
  39. package/dist/meet-view.d.cts +23 -0
  40. package/dist/meet-view.d.ts +23 -0
  41. package/dist/meet-view.js +59 -0
  42. package/dist/snapshot-BS4hn1dk.d.cts +234 -0
  43. package/dist/snapshot-BS4hn1dk.d.ts +234 -0
  44. package/dist/snapshot-file.cjs +192 -0
  45. package/dist/snapshot-file.d.cts +21 -0
  46. package/dist/snapshot-file.d.ts +21 -0
  47. package/dist/snapshot-file.js +38 -0
  48. package/marketplace.css +61 -0
  49. package/package.json +103 -0
  50. package/tokens/broadsheet.css +61 -0
@@ -0,0 +1,79 @@
1
+ /** Rules for in-person meetups and the paper's chat. */
2
+ declare const MEET_ACTIVITIES: readonly [{
3
+ readonly value: "coffee";
4
+ readonly label: "Coffee";
5
+ }, {
6
+ readonly value: "walk";
7
+ readonly label: "Walk";
8
+ }, {
9
+ readonly value: "hike";
10
+ readonly label: "Hike";
11
+ }, {
12
+ readonly value: "pickleball";
13
+ readonly label: "Pickleball";
14
+ }, {
15
+ readonly value: "tennis";
16
+ readonly label: "Tennis";
17
+ }, {
18
+ readonly value: "soccer";
19
+ readonly label: "Soccer";
20
+ }, {
21
+ readonly value: "bike";
22
+ readonly label: "Bike ride";
23
+ }, {
24
+ readonly value: "dogs";
25
+ readonly label: "Dog walk";
26
+ }, {
27
+ readonly value: "books";
28
+ readonly label: "Book club";
29
+ }, {
30
+ readonly value: "games";
31
+ readonly label: "Board games";
32
+ }, {
33
+ readonly value: "kids";
34
+ readonly label: "Kids outing";
35
+ }, {
36
+ readonly value: "volunteer";
37
+ readonly label: "Volunteer";
38
+ }, {
39
+ readonly value: "other";
40
+ readonly label: "Something else";
41
+ }];
42
+ type MeetActivity = (typeof MEET_ACTIVITIES)[number]["value"];
43
+ type PublicPlace = {
44
+ id: string;
45
+ name: string;
46
+ detail: string;
47
+ };
48
+ type MeetupInput = {
49
+ activity?: unknown;
50
+ otherLabel?: unknown;
51
+ placeId?: unknown;
52
+ startsAt?: unknown;
53
+ notes?: unknown;
54
+ capacity?: unknown;
55
+ };
56
+ type PreparedMeetup = {
57
+ activity: MeetActivity;
58
+ activityLabel: string;
59
+ placeId: string;
60
+ placeName: string;
61
+ placeDetail: string;
62
+ startsAt: string;
63
+ notes: string;
64
+ capacity: number;
65
+ };
66
+ declare function isAdult(bornOn: string, now?: Date): boolean;
67
+ declare function canHostMeetup(accountCreatedAt: string, now?: Date): boolean;
68
+ /** Refuse phone numbers, email addresses, and contact links. */
69
+ declare function screenMessage(value: string): {
70
+ ok: true;
71
+ text: string;
72
+ } | {
73
+ ok: false;
74
+ reason: string;
75
+ };
76
+ declare function activityLabel(value: string, otherLabel?: string): string;
77
+ declare function prepareMeetup(input: MeetupInput, places: PublicPlace[], now?: Date): PreparedMeetup;
78
+
79
+ export { MEET_ACTIVITIES as M, type PreparedMeetup as P, type MeetActivity as a, type MeetupInput as b, type PublicPlace as c, activityLabel as d, canHostMeetup as e, isAdult as i, prepareMeetup as p, screenMessage as s };
@@ -0,0 +1,188 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/meet-forms.tsx
8
+
9
+ // src/meet.ts
10
+ var MEET_ACTIVITIES = [
11
+ { value: "coffee", label: "Coffee" },
12
+ { value: "walk", label: "Walk" },
13
+ { value: "hike", label: "Hike" },
14
+ { value: "pickleball", label: "Pickleball" },
15
+ { value: "tennis", label: "Tennis" },
16
+ { value: "soccer", label: "Soccer" },
17
+ { value: "bike", label: "Bike ride" },
18
+ { value: "dogs", label: "Dog walk" },
19
+ { value: "books", label: "Book club" },
20
+ { value: "games", label: "Board games" },
21
+ { value: "kids", label: "Kids outing" },
22
+ { value: "volunteer", label: "Volunteer" },
23
+ { value: "other", label: "Something else" }
24
+ ];
25
+ function useJson(url) {
26
+ const [busy, setBusy] = react.useState(false);
27
+ const [error, setError] = react.useState("");
28
+ async function submit(event, next) {
29
+ event.preventDefault();
30
+ setBusy(true);
31
+ setError("");
32
+ const form = event.currentTarget;
33
+ const data = {};
34
+ new FormData(form).forEach((value, key) => {
35
+ if (typeof value === "string") data[key] = value;
36
+ });
37
+ try {
38
+ const response = await fetch(url, {
39
+ method: "POST",
40
+ headers: { "content-type": "application/json" },
41
+ credentials: "same-origin",
42
+ body: JSON.stringify(data)
43
+ });
44
+ const body = await response.json().catch(() => ({}));
45
+ if (response.status === 401) {
46
+ window.location.assign("/account?next=/meet");
47
+ return;
48
+ }
49
+ if (!response.ok) throw new Error(typeof body.error === "string" ? body.error : "Please try again.");
50
+ if (next || body.id) window.location.assign(next || `/meet/${encodeURIComponent(String(body.id))}`);
51
+ } catch (err) {
52
+ setError(err instanceof Error ? err.message : "Please try again.");
53
+ setBusy(false);
54
+ }
55
+ }
56
+ return { busy, error, submit };
57
+ }
58
+ function MeetHostForm({ places }) {
59
+ const post = useJson("/api/meet");
60
+ return /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "np-market form", onSubmit: (event) => post.submit(event), children: [
61
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
62
+ "Activity",
63
+ /* @__PURE__ */ jsxRuntime.jsx("select", { name: "activity", required: true, defaultValue: "coffee", children: MEET_ACTIVITIES.map((item) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: item.value, children: item.label }, item.value)) })
64
+ ] }),
65
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
66
+ "If something else, name it",
67
+ /* @__PURE__ */ jsxRuntime.jsx("input", { name: "otherLabel", maxLength: 40 })
68
+ ] }),
69
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
70
+ "Public place",
71
+ /* @__PURE__ */ jsxRuntime.jsx("select", { name: "placeId", required: true, defaultValue: places[0]?.id ?? "", children: places.map((place) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: place.id, children: place.name }, place.id)) })
72
+ ] }),
73
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
74
+ "When",
75
+ /* @__PURE__ */ jsxRuntime.jsx("input", { name: "startsAt", type: "datetime-local", required: true })
76
+ ] }),
77
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
78
+ "Group size",
79
+ /* @__PURE__ */ jsxRuntime.jsx("input", { name: "capacity", type: "number", min: 2, max: 12, defaultValue: 6, required: true })
80
+ ] }),
81
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
82
+ "Note for the group",
83
+ /* @__PURE__ */ jsxRuntime.jsx("textarea", { name: "notes", maxLength: 1e3, rows: 4, placeholder: "Where to stand, what to bring. No phone numbers." })
84
+ ] }),
85
+ post.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "error", role: "alert", children: post.error }),
86
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "primary", disabled: post.busy, children: post.busy ? "Saving\u2026" : "Publish meetup" })
87
+ ] });
88
+ }
89
+ function PhoneCheckForm() {
90
+ const [sent, setSent] = react.useState(false);
91
+ const [error, setError] = react.useState("");
92
+ const [busy, setBusy] = react.useState(false);
93
+ async function send(event) {
94
+ event.preventDefault();
95
+ setBusy(true);
96
+ setError("");
97
+ const data = {};
98
+ new FormData(event.currentTarget).forEach((value, key) => {
99
+ if (typeof value === "string") data[key] = value;
100
+ });
101
+ const response = await fetch("/api/meet/verify-phone", {
102
+ method: "POST",
103
+ headers: { "content-type": "application/json" },
104
+ credentials: "same-origin",
105
+ body: JSON.stringify({ ...data, step: sent ? "check" : "send" })
106
+ });
107
+ const body = await response.json().catch(() => ({}));
108
+ setBusy(false);
109
+ if (!response.ok) {
110
+ setError(typeof body.error === "string" ? body.error : "Please try again.");
111
+ return;
112
+ }
113
+ if (!sent) setSent(true);
114
+ else window.location.reload();
115
+ }
116
+ return /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "np-market form", onSubmit: send, children: [
117
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
118
+ "Mobile number",
119
+ /* @__PURE__ */ jsxRuntime.jsx("input", { name: "phone", type: "tel", autoComplete: "tel", required: true, disabled: sent })
120
+ ] }),
121
+ sent && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
122
+ "Code from the text",
123
+ /* @__PURE__ */ jsxRuntime.jsx("input", { name: "code", inputMode: "numeric", autoComplete: "one-time-code", required: true })
124
+ ] }),
125
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "error", role: "alert", children: error }),
126
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "primary", disabled: busy, children: sent ? "Confirm code" : "Text me a code" })
127
+ ] });
128
+ }
129
+ function BirthdayForm() {
130
+ const post = useJson("/api/meet/birthday");
131
+ return /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "np-market form", onSubmit: (event) => post.submit(event, "/meet"), children: [
132
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
133
+ "Date of birth",
134
+ /* @__PURE__ */ jsxRuntime.jsx("input", { name: "bornOn", type: "date", required: true })
135
+ ] }),
136
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "meta", children: "Meet and town chat are for adults 18 and over. Your birthday is not shown to other readers." }),
137
+ post.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "error", role: "alert", children: post.error }),
138
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "primary", disabled: post.busy, children: "Continue" })
139
+ ] });
140
+ }
141
+ function MeetupChat({ meetupId }) {
142
+ const [text, setText] = react.useState("");
143
+ const [lines, setLines] = react.useState([]);
144
+ const [error, setError] = react.useState("");
145
+ async function load() {
146
+ const response = await fetch(`/api/meet/${encodeURIComponent(meetupId)}/messages`, { credentials: "same-origin" });
147
+ const body = await response.json().catch(() => ({}));
148
+ if (response.ok && Array.isArray(body.messages)) setLines(body.messages);
149
+ }
150
+ async function send(event) {
151
+ event.preventDefault();
152
+ setError("");
153
+ const response = await fetch(`/api/meet/${encodeURIComponent(meetupId)}/messages`, {
154
+ method: "POST",
155
+ headers: { "content-type": "application/json" },
156
+ credentials: "same-origin",
157
+ body: JSON.stringify({ content: text })
158
+ });
159
+ const body = await response.json().catch(() => ({}));
160
+ if (!response.ok) {
161
+ setError(typeof body.error === "string" ? body.error : "Message was not sent.");
162
+ return;
163
+ }
164
+ setText("");
165
+ await load();
166
+ }
167
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "np-market", children: [
168
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "secondary", type: "button", onClick: () => void load(), children: "Refresh chat" }),
169
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "list", children: lines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "item", children: [
170
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: line.author || "Reader" }),
171
+ " ",
172
+ line.content
173
+ ] }, line.id)) }),
174
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "form", onSubmit: send, children: [
175
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
176
+ "Message",
177
+ /* @__PURE__ */ jsxRuntime.jsx("textarea", { value: text, onChange: (event) => setText(event.target.value), maxLength: 1e3, required: true, rows: 3 })
178
+ ] }),
179
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "error", role: "alert", children: error }),
180
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "primary", type: "submit", children: "Send" })
181
+ ] })
182
+ ] });
183
+ }
184
+
185
+ exports.BirthdayForm = BirthdayForm;
186
+ exports.MeetHostForm = MeetHostForm;
187
+ exports.MeetupChat = MeetupChat;
188
+ exports.PhoneCheckForm = PhoneCheckForm;
@@ -0,0 +1,13 @@
1
+ import * as react from 'react';
2
+ import { c as PublicPlace } from './meet-5Ym5bxfh.cjs';
3
+
4
+ declare function MeetHostForm({ places }: {
5
+ places: PublicPlace[];
6
+ }): react.JSX.Element;
7
+ declare function PhoneCheckForm(): react.JSX.Element;
8
+ declare function BirthdayForm(): react.JSX.Element;
9
+ declare function MeetupChat({ meetupId }: {
10
+ meetupId: string;
11
+ }): react.JSX.Element;
12
+
13
+ export { BirthdayForm, MeetHostForm, MeetupChat, PhoneCheckForm };
@@ -0,0 +1,13 @@
1
+ import * as react from 'react';
2
+ import { c as PublicPlace } from './meet-5Ym5bxfh.js';
3
+
4
+ declare function MeetHostForm({ places }: {
5
+ places: PublicPlace[];
6
+ }): react.JSX.Element;
7
+ declare function PhoneCheckForm(): react.JSX.Element;
8
+ declare function BirthdayForm(): react.JSX.Element;
9
+ declare function MeetupChat({ meetupId }: {
10
+ meetupId: string;
11
+ }): react.JSX.Element;
12
+
13
+ export { BirthdayForm, MeetHostForm, MeetupChat, PhoneCheckForm };
@@ -0,0 +1,166 @@
1
+ "use client";
2
+ import { MEET_ACTIVITIES } from './chunk-GB36YM56.js';
3
+ import { useState } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ function useJson(url) {
7
+ const [busy, setBusy] = useState(false);
8
+ const [error, setError] = useState("");
9
+ async function submit(event, next) {
10
+ event.preventDefault();
11
+ setBusy(true);
12
+ setError("");
13
+ const form = event.currentTarget;
14
+ const data = {};
15
+ new FormData(form).forEach((value, key) => {
16
+ if (typeof value === "string") data[key] = value;
17
+ });
18
+ try {
19
+ const response = await fetch(url, {
20
+ method: "POST",
21
+ headers: { "content-type": "application/json" },
22
+ credentials: "same-origin",
23
+ body: JSON.stringify(data)
24
+ });
25
+ const body = await response.json().catch(() => ({}));
26
+ if (response.status === 401) {
27
+ window.location.assign("/account?next=/meet");
28
+ return;
29
+ }
30
+ if (!response.ok) throw new Error(typeof body.error === "string" ? body.error : "Please try again.");
31
+ if (next || body.id) window.location.assign(next || `/meet/${encodeURIComponent(String(body.id))}`);
32
+ } catch (err) {
33
+ setError(err instanceof Error ? err.message : "Please try again.");
34
+ setBusy(false);
35
+ }
36
+ }
37
+ return { busy, error, submit };
38
+ }
39
+ function MeetHostForm({ places }) {
40
+ const post = useJson("/api/meet");
41
+ return /* @__PURE__ */ jsxs("form", { className: "np-market form", onSubmit: (event) => post.submit(event), children: [
42
+ /* @__PURE__ */ jsxs("label", { children: [
43
+ "Activity",
44
+ /* @__PURE__ */ jsx("select", { name: "activity", required: true, defaultValue: "coffee", children: MEET_ACTIVITIES.map((item) => /* @__PURE__ */ jsx("option", { value: item.value, children: item.label }, item.value)) })
45
+ ] }),
46
+ /* @__PURE__ */ jsxs("label", { children: [
47
+ "If something else, name it",
48
+ /* @__PURE__ */ jsx("input", { name: "otherLabel", maxLength: 40 })
49
+ ] }),
50
+ /* @__PURE__ */ jsxs("label", { children: [
51
+ "Public place",
52
+ /* @__PURE__ */ jsx("select", { name: "placeId", required: true, defaultValue: places[0]?.id ?? "", children: places.map((place) => /* @__PURE__ */ jsx("option", { value: place.id, children: place.name }, place.id)) })
53
+ ] }),
54
+ /* @__PURE__ */ jsxs("label", { children: [
55
+ "When",
56
+ /* @__PURE__ */ jsx("input", { name: "startsAt", type: "datetime-local", required: true })
57
+ ] }),
58
+ /* @__PURE__ */ jsxs("label", { children: [
59
+ "Group size",
60
+ /* @__PURE__ */ jsx("input", { name: "capacity", type: "number", min: 2, max: 12, defaultValue: 6, required: true })
61
+ ] }),
62
+ /* @__PURE__ */ jsxs("label", { children: [
63
+ "Note for the group",
64
+ /* @__PURE__ */ jsx("textarea", { name: "notes", maxLength: 1e3, rows: 4, placeholder: "Where to stand, what to bring. No phone numbers." })
65
+ ] }),
66
+ post.error && /* @__PURE__ */ jsx("p", { className: "error", role: "alert", children: post.error }),
67
+ /* @__PURE__ */ jsx("button", { className: "primary", disabled: post.busy, children: post.busy ? "Saving\u2026" : "Publish meetup" })
68
+ ] });
69
+ }
70
+ function PhoneCheckForm() {
71
+ const [sent, setSent] = useState(false);
72
+ const [error, setError] = useState("");
73
+ const [busy, setBusy] = useState(false);
74
+ async function send(event) {
75
+ event.preventDefault();
76
+ setBusy(true);
77
+ setError("");
78
+ const data = {};
79
+ new FormData(event.currentTarget).forEach((value, key) => {
80
+ if (typeof value === "string") data[key] = value;
81
+ });
82
+ const response = await fetch("/api/meet/verify-phone", {
83
+ method: "POST",
84
+ headers: { "content-type": "application/json" },
85
+ credentials: "same-origin",
86
+ body: JSON.stringify({ ...data, step: sent ? "check" : "send" })
87
+ });
88
+ const body = await response.json().catch(() => ({}));
89
+ setBusy(false);
90
+ if (!response.ok) {
91
+ setError(typeof body.error === "string" ? body.error : "Please try again.");
92
+ return;
93
+ }
94
+ if (!sent) setSent(true);
95
+ else window.location.reload();
96
+ }
97
+ return /* @__PURE__ */ jsxs("form", { className: "np-market form", onSubmit: send, children: [
98
+ /* @__PURE__ */ jsxs("label", { children: [
99
+ "Mobile number",
100
+ /* @__PURE__ */ jsx("input", { name: "phone", type: "tel", autoComplete: "tel", required: true, disabled: sent })
101
+ ] }),
102
+ sent && /* @__PURE__ */ jsxs("label", { children: [
103
+ "Code from the text",
104
+ /* @__PURE__ */ jsx("input", { name: "code", inputMode: "numeric", autoComplete: "one-time-code", required: true })
105
+ ] }),
106
+ error && /* @__PURE__ */ jsx("p", { className: "error", role: "alert", children: error }),
107
+ /* @__PURE__ */ jsx("button", { className: "primary", disabled: busy, children: sent ? "Confirm code" : "Text me a code" })
108
+ ] });
109
+ }
110
+ function BirthdayForm() {
111
+ const post = useJson("/api/meet/birthday");
112
+ return /* @__PURE__ */ jsxs("form", { className: "np-market form", onSubmit: (event) => post.submit(event, "/meet"), children: [
113
+ /* @__PURE__ */ jsxs("label", { children: [
114
+ "Date of birth",
115
+ /* @__PURE__ */ jsx("input", { name: "bornOn", type: "date", required: true })
116
+ ] }),
117
+ /* @__PURE__ */ jsx("p", { className: "meta", children: "Meet and town chat are for adults 18 and over. Your birthday is not shown to other readers." }),
118
+ post.error && /* @__PURE__ */ jsx("p", { className: "error", role: "alert", children: post.error }),
119
+ /* @__PURE__ */ jsx("button", { className: "primary", disabled: post.busy, children: "Continue" })
120
+ ] });
121
+ }
122
+ function MeetupChat({ meetupId }) {
123
+ const [text, setText] = useState("");
124
+ const [lines, setLines] = useState([]);
125
+ const [error, setError] = useState("");
126
+ async function load() {
127
+ const response = await fetch(`/api/meet/${encodeURIComponent(meetupId)}/messages`, { credentials: "same-origin" });
128
+ const body = await response.json().catch(() => ({}));
129
+ if (response.ok && Array.isArray(body.messages)) setLines(body.messages);
130
+ }
131
+ async function send(event) {
132
+ event.preventDefault();
133
+ setError("");
134
+ const response = await fetch(`/api/meet/${encodeURIComponent(meetupId)}/messages`, {
135
+ method: "POST",
136
+ headers: { "content-type": "application/json" },
137
+ credentials: "same-origin",
138
+ body: JSON.stringify({ content: text })
139
+ });
140
+ const body = await response.json().catch(() => ({}));
141
+ if (!response.ok) {
142
+ setError(typeof body.error === "string" ? body.error : "Message was not sent.");
143
+ return;
144
+ }
145
+ setText("");
146
+ await load();
147
+ }
148
+ return /* @__PURE__ */ jsxs("div", { className: "np-market", children: [
149
+ /* @__PURE__ */ jsx("button", { className: "secondary", type: "button", onClick: () => void load(), children: "Refresh chat" }),
150
+ /* @__PURE__ */ jsx("div", { className: "list", children: lines.map((line) => /* @__PURE__ */ jsxs("p", { className: "item", children: [
151
+ /* @__PURE__ */ jsx("strong", { children: line.author || "Reader" }),
152
+ " ",
153
+ line.content
154
+ ] }, line.id)) }),
155
+ /* @__PURE__ */ jsxs("form", { className: "form", onSubmit: send, children: [
156
+ /* @__PURE__ */ jsxs("label", { children: [
157
+ "Message",
158
+ /* @__PURE__ */ jsx("textarea", { value: text, onChange: (event) => setText(event.target.value), maxLength: 1e3, required: true, rows: 3 })
159
+ ] }),
160
+ error && /* @__PURE__ */ jsx("p", { className: "error", role: "alert", children: error }),
161
+ /* @__PURE__ */ jsx("button", { className: "primary", type: "submit", children: "Send" })
162
+ ] })
163
+ ] });
164
+ }
165
+
166
+ export { BirthdayForm, MeetHostForm, MeetupChat, PhoneCheckForm };
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+
5
+ // src/meet.ts
6
+ var MEET_ACTIVITIES = [
7
+ { value: "coffee", label: "Coffee" },
8
+ { value: "walk", label: "Walk" },
9
+ { value: "hike", label: "Hike" },
10
+ { value: "pickleball", label: "Pickleball" },
11
+ { value: "tennis", label: "Tennis" },
12
+ { value: "soccer", label: "Soccer" },
13
+ { value: "bike", label: "Bike ride" },
14
+ { value: "dogs", label: "Dog walk" },
15
+ { value: "books", label: "Book club" },
16
+ { value: "games", label: "Board games" },
17
+ { value: "kids", label: "Kids outing" },
18
+ { value: "volunteer", label: "Volunteer" },
19
+ { value: "other", label: "Something else" }
20
+ ];
21
+ function activityLabel(value, otherLabel = "") {
22
+ if (value === "other") return Array.from(otherLabel.trim()).slice(0, 40).join("") || "Something else";
23
+ return MEET_ACTIVITIES.find((item) => item.value === value)?.label ?? "";
24
+ }
25
+ function MeetBoard({ region, meetups }) {
26
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "np-market", children: [
27
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { children: "Meet" }),
28
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "lead", children: [
29
+ "Get together in ",
30
+ region,
31
+ ". Every meetup is at a public place. You talk in the paper\u2019s chat, and you trade a phone number in person if you want to."
32
+ ] }),
33
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "actions", children: [
34
+ /* @__PURE__ */ jsxRuntime.jsx("a", { className: "primary", href: "/meet/new", children: "Host a meetup" }),
35
+ /* @__PURE__ */ jsxRuntime.jsx("a", { className: "secondary", href: "/chat", children: "Town chat" })
36
+ ] }),
37
+ meetups.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "empty", children: [
38
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: "No meetups yet." }),
39
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Host a coffee, a walk, or a game. The first meeting is public." })
40
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "list", children: meetups.map((meetup) => /* @__PURE__ */ jsxRuntime.jsxs("a", { className: "item", href: `/meet/${encodeURIComponent(meetup.id)}`, children: [
41
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { children: meetup.activityLabel }),
42
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "meta", children: [
43
+ meetup.placeName,
44
+ " \xB7 ",
45
+ new Date(meetup.startsAt).toLocaleString("en-US", { weekday: "short", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }),
46
+ " \xB7 ",
47
+ meetup.going,
48
+ " going"
49
+ ] })
50
+ ] }, meetup.id)) })
51
+ ] });
52
+ }
53
+ function MeetupDetail({ meetup, joined }) {
54
+ return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "np-market", children: [
55
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "meta", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/meet", children: "Meet" }) }),
56
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { children: meetup.activityLabel }),
57
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "meta", children: [
58
+ meetup.placeName,
59
+ " \xB7 ",
60
+ meetup.placeDetail
61
+ ] }),
62
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "meta", children: [
63
+ new Date(meetup.startsAt).toLocaleString("en-US", { weekday: "long", month: "long", day: "numeric", hour: "numeric", minute: "2-digit" }),
64
+ " \xB7 Host ",
65
+ meetup.hostName,
66
+ " \xB7 ",
67
+ meetup.going,
68
+ " of ",
69
+ meetup.capacity
70
+ ] }),
71
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "body", children: meetup.notes }),
72
+ !joined && meetup.going < meetup.capacity && /* @__PURE__ */ jsxRuntime.jsx("form", { className: "actions", method: "post", action: `/api/meet/${encodeURIComponent(meetup.id)}/join`, children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "primary", type: "submit", children: "I\u2019ll be there" }) }),
73
+ joined && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "meta", children: "You\u2019re going. Use the chat below. Keep phone numbers for when you meet." })
74
+ ] });
75
+ }
76
+ function activityChoices() {
77
+ return MEET_ACTIVITIES.map((item) => ({ value: item.value, label: activityLabel(item.value) }));
78
+ }
79
+
80
+ exports.MeetBoard = MeetBoard;
81
+ exports.MeetupDetail = MeetupDetail;
82
+ exports.activityChoices = activityChoices;
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { P as PreparedMeetup } from './meet-5Ym5bxfh.cjs';
3
+
4
+ type MeetupCard = PreparedMeetup & {
5
+ id: string;
6
+ hostName: string;
7
+ going: number;
8
+ conversationId?: string | null;
9
+ };
10
+ declare function MeetBoard({ region, meetups }: {
11
+ region: string;
12
+ meetups: MeetupCard[];
13
+ }): react.JSX.Element;
14
+ declare function MeetupDetail({ meetup, joined }: {
15
+ meetup: MeetupCard;
16
+ joined: boolean;
17
+ }): react.JSX.Element;
18
+ declare function activityChoices(): {
19
+ value: "coffee" | "walk" | "hike" | "pickleball" | "tennis" | "soccer" | "bike" | "dogs" | "books" | "games" | "kids" | "volunteer" | "other";
20
+ label: string;
21
+ }[];
22
+
23
+ export { MeetBoard, type MeetupCard, MeetupDetail, activityChoices };
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { P as PreparedMeetup } from './meet-5Ym5bxfh.js';
3
+
4
+ type MeetupCard = PreparedMeetup & {
5
+ id: string;
6
+ hostName: string;
7
+ going: number;
8
+ conversationId?: string | null;
9
+ };
10
+ declare function MeetBoard({ region, meetups }: {
11
+ region: string;
12
+ meetups: MeetupCard[];
13
+ }): react.JSX.Element;
14
+ declare function MeetupDetail({ meetup, joined }: {
15
+ meetup: MeetupCard;
16
+ joined: boolean;
17
+ }): react.JSX.Element;
18
+ declare function activityChoices(): {
19
+ value: "coffee" | "walk" | "hike" | "pickleball" | "tennis" | "soccer" | "bike" | "dogs" | "books" | "games" | "kids" | "volunteer" | "other";
20
+ label: string;
21
+ }[];
22
+
23
+ export { MeetBoard, type MeetupCard, MeetupDetail, activityChoices };
@@ -0,0 +1,59 @@
1
+ import { MEET_ACTIVITIES, activityLabel } from './chunk-GB36YM56.js';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ function MeetBoard({ region, meetups }) {
5
+ return /* @__PURE__ */ jsxs("div", { className: "np-market", children: [
6
+ /* @__PURE__ */ jsx("h1", { children: "Meet" }),
7
+ /* @__PURE__ */ jsxs("p", { className: "lead", children: [
8
+ "Get together in ",
9
+ region,
10
+ ". Every meetup is at a public place. You talk in the paper\u2019s chat, and you trade a phone number in person if you want to."
11
+ ] }),
12
+ /* @__PURE__ */ jsxs("div", { className: "actions", children: [
13
+ /* @__PURE__ */ jsx("a", { className: "primary", href: "/meet/new", children: "Host a meetup" }),
14
+ /* @__PURE__ */ jsx("a", { className: "secondary", href: "/chat", children: "Town chat" })
15
+ ] }),
16
+ meetups.length === 0 ? /* @__PURE__ */ jsxs("section", { className: "empty", children: [
17
+ /* @__PURE__ */ jsx("h2", { children: "No meetups yet." }),
18
+ /* @__PURE__ */ jsx("p", { children: "Host a coffee, a walk, or a game. The first meeting is public." })
19
+ ] }) : /* @__PURE__ */ jsx("div", { className: "list", children: meetups.map((meetup) => /* @__PURE__ */ jsxs("a", { className: "item", href: `/meet/${encodeURIComponent(meetup.id)}`, children: [
20
+ /* @__PURE__ */ jsx("h2", { children: meetup.activityLabel }),
21
+ /* @__PURE__ */ jsxs("p", { className: "meta", children: [
22
+ meetup.placeName,
23
+ " \xB7 ",
24
+ new Date(meetup.startsAt).toLocaleString("en-US", { weekday: "short", month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }),
25
+ " \xB7 ",
26
+ meetup.going,
27
+ " going"
28
+ ] })
29
+ ] }, meetup.id)) })
30
+ ] });
31
+ }
32
+ function MeetupDetail({ meetup, joined }) {
33
+ return /* @__PURE__ */ jsxs("article", { className: "np-market", children: [
34
+ /* @__PURE__ */ jsx("p", { className: "meta", children: /* @__PURE__ */ jsx("a", { href: "/meet", children: "Meet" }) }),
35
+ /* @__PURE__ */ jsx("h1", { children: meetup.activityLabel }),
36
+ /* @__PURE__ */ jsxs("p", { className: "meta", children: [
37
+ meetup.placeName,
38
+ " \xB7 ",
39
+ meetup.placeDetail
40
+ ] }),
41
+ /* @__PURE__ */ jsxs("p", { className: "meta", children: [
42
+ new Date(meetup.startsAt).toLocaleString("en-US", { weekday: "long", month: "long", day: "numeric", hour: "numeric", minute: "2-digit" }),
43
+ " \xB7 Host ",
44
+ meetup.hostName,
45
+ " \xB7 ",
46
+ meetup.going,
47
+ " of ",
48
+ meetup.capacity
49
+ ] }),
50
+ /* @__PURE__ */ jsx("div", { className: "body", children: meetup.notes }),
51
+ !joined && meetup.going < meetup.capacity && /* @__PURE__ */ jsx("form", { className: "actions", method: "post", action: `/api/meet/${encodeURIComponent(meetup.id)}/join`, children: /* @__PURE__ */ jsx("button", { className: "primary", type: "submit", children: "I\u2019ll be there" }) }),
52
+ joined && /* @__PURE__ */ jsx("p", { className: "meta", children: "You\u2019re going. Use the chat below. Keep phone numbers for when you meet." })
53
+ ] });
54
+ }
55
+ function activityChoices() {
56
+ return MEET_ACTIVITIES.map((item) => ({ value: item.value, label: activityLabel(item.value) }));
57
+ }
58
+
59
+ export { MeetBoard, MeetupDetail, activityChoices };