@acms-subir/core 0.3.51 → 0.3.53
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/CHANGELOG.md +33 -0
- package/cli/migrations.generated.mjs +16 -1
- package/docs/calendar.md +24 -10
- package/docs/mobile.md +1 -1
- package/integration.mjs +5 -0
- package/mobile-template/recipes/events/files/components/RsvpPanel.tsx +2 -2
- package/mobile-template/recipes/rsvp/files/components/RsvpPanel.tsx +2 -2
- package/package.json +1 -1
- package/src/components/ThemeSections.astro +2 -2
- package/src/components/display/ScreensManager.tsx +481 -0
- package/src/components/editor/PostEditor.tsx +2 -0
- package/src/components/editor/event/EventSheet.tsx +6 -2
- package/src/components/editor/event/RegistrationCard.tsx +3 -3
- package/src/components/editor/event/RegistrationFormEditor.tsx +6 -3
- package/src/components/forms/Canvas.tsx +31 -19
- package/src/components/forms/FormBuildPage.tsx +12 -6
- package/src/components/forms/FormBuilder.tsx +60 -11
- package/src/components/forms/Inspector.tsx +116 -8
- package/src/components/forms/TypeIcon.tsx +1 -0
- package/src/components/forms/meta.ts +71 -9
- package/src/layouts/Admin.astro +2 -0
- package/src/lib/backup/content.ts +10 -1
- package/src/lib/calendar/api.ts +3 -1
- package/src/lib/display/board.ts +4 -3
- package/src/lib/display/screens.ts +111 -0
- package/src/lib/display/store.ts +113 -0
- package/src/lib/display/words.ts +16 -6
- package/src/lib/dynamicBlocks.ts +4 -2
- package/src/lib/forms/fields.ts +14 -9
- package/src/lib/forms/localize.ts +68 -0
- package/src/lib/forms/render.ts +8 -2
- package/src/lib/forms/schema.ts +31 -2
- package/src/lib/forms/service.ts +9 -1
- package/src/lib/forms/validate.ts +16 -4
- package/src/lib/forms/words.ts +32 -0
- package/src/lib/mobile/forms.ts +45 -13
- package/src/lib/mobile/openapi.ts +3 -3
- package/src/lib/mobile/rsvp.ts +4 -2
- package/src/lib/mobile/schemas.ts +3 -1
- package/src/lib/repo/migrations.ts +3 -0
- package/src/lib/repo/sql.ts +5 -0
- package/src/lib/repo/types.ts +5 -0
- package/src/pages/[...slug].astro +1 -1
- package/src/pages/admin/calendar/screens/index.astro +24 -0
- package/src/pages/admin/calendar/screens/preview.astro +36 -0
- package/src/pages/admin/forms/[id].astro +6 -0
- package/src/pages/api/calendar/screens.ts +30 -0
- package/src/pages/blog/[slug].astro +1 -1
- package/src/pages/display/s/[key].astro +48 -0
- package/src/pages/events/[slug].astro +9 -3
- package/src/pages/forms/[id].astro +22 -4
- package/src/pages/index.astro +1 -1
- package/src/styles/display.css +2 -1
- package/src/styles/forms.css +8 -0
- package/src/styles/screens-admin.css +53 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @acms-subir/core
|
|
2
2
|
|
|
3
|
+
## 0.3.53
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1b8201d: Forms get a **Time** question and can speak every language your site has.
|
|
8
|
+
|
|
9
|
+
**Time questions.** Add one from the builder's question menu (next to Date). Visitors pick a time with their browser's time picker; it's saved as a 24-hour time like `18:30` and shows that way in Responses, CSV exports and alert emails. Required works like any other question, and anything that isn't a real time is refused. Event registration forms can ask it too.
|
|
10
|
+
|
|
11
|
+
**Forms in your site's other languages.** On a site with more than one language, the form builder now has a language switcher (English · Español …) above the canvas, and so does an event's registration form. Pick a language to write its words: the title, introduction, button and thank-you message, and each question's label, help text, placeholder and choices. You type straight onto the form, with the main language showing in grey where you haven't written anything yet, and the side panel shows the main language's words above each box. Anything left blank shows the main language. The questions themselves (types, order, choices, Required and the other settings) stay the same in every language, and when you add, remove or reorder choices, their translations move with them. Translation changes to a published form go live with **Publish changes**, and the menu opens the live form in each language.
|
|
12
|
+
|
|
13
|
+
- `/es/forms/<id>` shows the form in Spanish, and a Form block on a Spanish page, post, event or home page opens it in Spanish. Event pages in another language show the registration questions in that language too.
|
|
14
|
+
- The form's own words around your questions ("Submit", "Choose…", "Other", "Send another response" and the default thank-you) follow the page's language in English, Spanish, French, German and Portuguese. Error messages are still in English.
|
|
15
|
+
- Answers are always saved with the main-language choice, so Responses, exports and alerts read the same whatever language someone answered in. A translated choice can't read the same as a different choice, so every answer maps back to exactly one.
|
|
16
|
+
- Rewording a translated label, help text or title doesn't change a form's version, so someone halfway through the form, on the website or in an app, can still send it. Rewording a translated choice does, so an answer in the old words is sent back for a reload instead of being stored as the wrong choice. Sending the same website response again (a refresh after sending) shows the thank-you again and stores it once.
|
|
17
|
+
|
|
18
|
+
**API:** `GET /api/v1/forms/{id}` takes an optional `locale` and returns the form's words in that language (the main language's where it has none); `version` is the same in every language. `POST /api/v1/forms/{id}/responses` accepts a choice in the language it was shown in or in the main one, and an optional `?locale=` for the receipt's thank-you. `GET /api/v1/events/{slug}/rsvp?locale=` returns the registration questions in that language, and RSVPs may send their choices the same way. Field `type` can now be `time` (24-hour `HH:mm`). Stored forms gain optional `translations` on the form and on each question; nothing changes for forms without them, and no migration is needed.
|
|
19
|
+
|
|
20
|
+
## 0.3.52
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- b8ffed8: Saved TV screens: **Admin → Calendar → Screens** (for people who manage rooms and calendars).
|
|
25
|
+
|
|
26
|
+
Each screen has a name, the display settings (view, hours with "Show all hours" or "Fit the events", days, first day, calendars, rooms in column order, tags, theme, text size, what shows, title, language) beside a live 16:9 preview, and a long random key that is its whole address: `/display/s/<key>`. **Copy link** copies it, and **Rotate link** makes a new one; the old link stops working at once. **Last check-in** shows when the screen last fetched its events (a TV that's on checks in every 5 minutes), so staff can tell a TV is off. A change made in the admin reaches the TV within 5 minutes. The same editor builds quick links, can start from a pasted one, and saves any quick link as a screen.
|
|
27
|
+
|
|
28
|
+
**Show private calendars and room bookings** (off by default) lets a screen show the private calendars it names and the confirmed bookings of the rooms it names (or of rooms set to show on the calendar): titles, rooms, hosts and the name on each booking ("Birthday party · Rivera"), never emails, phone numbers, booking notes or RSVP lists. A Google booking keeps "Booked" with its own title under it. Turning it on, or previewing it, also needs **Manage bookings**. Saved screens are never cached (`private, no-store`) and send no referrer. Wrong keys are rate-limited: after 60 in 15 minutes from one address, further wrong keys from it get a 429, while a screen's own key always opens it (bad keys can't lock a church's TVs out).
|
|
29
|
+
|
|
30
|
+
Migration 67 adds the `display_screens` table. Screens go into backups without their keys; after a restore each screen has a new link to put on its TV. New: `/display/s/[key]`, `/admin/calendar/screens`, `/admin/calendar/screens/preview` and `/api/calendar/screens` (staff only). `npx acms docs calendar` covers screens, keys and private details.
|
|
31
|
+
|
|
32
|
+
The display list now keeps each day in one column when it fits, so a column never starts without its day.
|
|
33
|
+
|
|
34
|
+
Moved and cancelled dates on the display use the event pages' own words ("Cambio de horario" in Spanish), so a TV says what the site and its emails say.
|
|
35
|
+
|
|
3
36
|
## 0.3.51
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
|
@@ -434,6 +434,19 @@ var ANNOUNCEMENT_VISIBLE_COLUMNS = [
|
|
|
434
434
|
"published_at"
|
|
435
435
|
];
|
|
436
436
|
|
|
437
|
+
// src/lib/display/store.ts
|
|
438
|
+
var SCREENS_MIGRATION = {
|
|
439
|
+
version: 67,
|
|
440
|
+
statements: [
|
|
441
|
+
`CREATE TABLE display_screens (
|
|
442
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL DEFAULT '', params TEXT NOT NULL DEFAULT '', locale TEXT NOT NULL DEFAULT '',
|
|
443
|
+
private_details INTEGER NOT NULL DEFAULT 0, screen_key TEXT NOT NULL DEFAULT '', last_seen_at INTEGER,
|
|
444
|
+
ord INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
445
|
+
)`,
|
|
446
|
+
`CREATE UNIQUE INDEX display_screens_key ON display_screens (screen_key) WHERE screen_key <> ''`
|
|
447
|
+
]
|
|
448
|
+
};
|
|
449
|
+
|
|
437
450
|
// src/lib/repo/migrations.ts
|
|
438
451
|
var CONTENT_VERSION_COLUMNS = {
|
|
439
452
|
settings: [
|
|
@@ -1266,7 +1279,9 @@ var MIGRATIONS = [
|
|
|
1266
1279
|
EVENT_CHANGES_MIGRATION,
|
|
1267
1280
|
// single dates of a series moved (CalEvent.movedDates): a column on the
|
|
1268
1281
|
// row, so the triggers on events (content version, event_changes) see a move
|
|
1269
|
-
MOVED_DATES_MIGRATION(contentVersionTriggers("events", [...CONTENT_VERSION_COLUMNS.events, "cancelled_dates", "moved_dates"])[2])
|
|
1282
|
+
MOVED_DATES_MIGRATION(contentVersionTriggers("events", [...CONTENT_VERSION_COLUMNS.events, "cancelled_dates", "moved_dates"])[2]),
|
|
1283
|
+
// saved TV screens (Admin → Calendar → Screens, lib/display/store.ts)
|
|
1284
|
+
SCREENS_MIGRATION
|
|
1270
1285
|
];
|
|
1271
1286
|
var CURRENT_CONTENT_VERSION_COLUMNS = {
|
|
1272
1287
|
...CONTENT_VERSION_COLUMNS,
|
package/docs/calendar.md
CHANGED
|
@@ -195,9 +195,12 @@ client has `roomFree({ room, start, end })`, and the native package has
|
|
|
195
195
|
|
|
196
196
|
A full-screen calendar for a TV in a lobby or on a room's door. It shows a week, day, month, list, rooms or "now and next" view, with your own hours (8 AM to 10 PM by default). TVs have no mouse, so everything about a screen lives in its link. The page has nothing to focus or follow (no links, buttons or tab stops), because a remote's presses go to the page.
|
|
197
197
|
|
|
198
|
-
|
|
198
|
+
There are two kinds of link:
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
- **Quick links** spell out their settings in the address: `/display/calendar?view=week&hours=8-22`. Nothing is saved. Whatever their parameters, they show only what `calendar.ics` shows: published events in public calendars, or in an unlisted calendar named in `cal=`. There are no subscribed calendars, booking blocks, room names (until rooms are shown on public events) or hosts.
|
|
201
|
+
- **Saved screens** live in **Admin → Calendar → Screens**. Each has a name, the same settings and a long random key, which is the whole address: `/display/s/<key>`. A screen can show private details (below).
|
|
202
|
+
|
|
203
|
+
Spanish (or any other site language): `/es/display/calendar?…`, or the screen's Language setting.
|
|
201
204
|
|
|
202
205
|
### Parameters
|
|
203
206
|
|
|
@@ -209,12 +212,12 @@ Each has a default; leave out what you don't need. Lists are comma-separated or
|
|
|
209
212
|
| `hours` | `8-22` · `8:30-21` · `8-24` · `0-24` (or `all`) · `auto` | The hour grids' visible hours. `8-24` ends at midnight. `auto` fits the events shown, never less than 6 hours. Events outside the hours are cut and marked (▲ ▼), never left out. Default `8-22`. |
|
|
210
213
|
| `days` | `7` · `5` · `mon-fri` · `mon-sat` · `sun,wed,fri` | A number is that many days from the first day; names pick weekdays. For `list`, a number is how many days the list covers. Default 7. |
|
|
211
214
|
| `start` | `sun` · `mon` · `today` | The week's first day. `today` gives a rolling week. Default: Calendar settings → Defaults. |
|
|
212
|
-
| `cal` | calendar slugs | Only these calendars. Default: every public calendar. Private calendars
|
|
213
|
-
| `tag` / `notag` | tag names | Only events with one of these tags, or none of them. `tag=Class` makes a class board. Internal `#` tags
|
|
214
|
-
| `room` | room slugs | Only events held in these rooms. With `view=rooms` it also sets the columns and their order (otherwise every room of kind "Room").
|
|
215
|
+
| `cal` | calendar slugs | Only these calendars. Default: every public calendar. Private calendars need a saved screen with private details. |
|
|
216
|
+
| `tag` / `notag` | tag names | Only events with one of these tags, or none of them. `tag=Class` makes a class board. Internal `#` tags only match on a screen with private details. |
|
|
217
|
+
| `room` | room slugs | Only events held in these rooms. With `view=rooms` it also sets the columns and their order (otherwise every room of kind "Room"). On a quick link, only rooms named on public events count; any other room shows nothing. |
|
|
215
218
|
| `theme` | `dark` · `light` | Default `dark`. |
|
|
216
219
|
| `size` | `s` · `m` · `l` · `xl` | Text size for the viewing distance. Default `m`. |
|
|
217
|
-
| `show` | `time,room,host,clock,legend,qr` (or `none`) | What each event and the page show. Default `time,room,clock`. `legend` is a colour key; `qr` is a code that opens the calendar on a phone (with the same filters).
|
|
220
|
+
| `show` | `time,room,host,clock,legend,qr` (or `none`) | What each event and the page show. Default `time,room,clock`. `legend` is a colour key; `qr` is a code that opens the calendar on a phone (with the same public filters). Hosts only show on a screen with private details. |
|
|
218
221
|
| `title` | text | Replaces the heading ("This week", "Today"…). |
|
|
219
222
|
|
|
220
223
|
Examples:
|
|
@@ -223,19 +226,30 @@ Examples:
|
|
|
223
226
|
- `/display/calendar?view=list&days=14&size=l`: the next two weeks, in large text.
|
|
224
227
|
- `/display/calendar?view=rooms&room=chapel,hall,youth&hours=auto`: today, one column per room.
|
|
225
228
|
|
|
229
|
+
The editor in Admin → Calendar → Screens builds these links (**Quick link**), shows a live preview and can save any quick link as a screen.
|
|
230
|
+
|
|
226
231
|
### What a board does
|
|
227
232
|
|
|
228
233
|
- The first view is rendered on the server. A small script moves the now line, dims what has ended, and turns the list's pages (the list runs in columns, then pages, instead of scrolling).
|
|
229
234
|
- Every 5 minutes it fetches the board again from the same address, so a change made in the admin reaches the TV within 5 minutes. It also fetches at midnight, so the board rolls to the next day, week or month. It reloads once a night (between 3:00 and 3:40, site time).
|
|
230
|
-
- Offline, it keeps the last board and shows "Updated 7:41 PM". A small service worker (scope `/display/`) keeps a copy of the page, so a reload during an outage shows the last events instead of an error page.
|
|
235
|
+
- Offline, it keeps the last board and shows "Updated 7:41 PM". A small service worker (scope `/display/`) keeps a copy of the page, so a reload during an outage or a server error shows the last events instead of an error page (a copy older than a week is never shown).
|
|
231
236
|
- Events take their room's colour, else their calendar's. Appointments from Google booking pages (tagged `#google-booking`) show as "Booked". A cancelled date is struck through, and a moved date shows at its new time, marked "Moved" with its old time (and day, when that changed).
|
|
232
|
-
- The page may be framed anywhere (`frame-ancestors *`), sends no referrer, is never indexed, and needs no cookie.
|
|
237
|
+
- The page may be framed anywhere (`frame-ancestors *`), sends no referrer, is never indexed, and needs no cookie. Quick links may sit in a shared cache for a minute; saved screens never do.
|
|
238
|
+
|
|
239
|
+
### Saved screens, keys and private details
|
|
240
|
+
|
|
241
|
+
**Show private calendars and room bookings** (each screen's switch, off by default) lets the screen show the private calendars it names in `cal=` and the confirmed bookings of the rooms it names in `room=` (or of rooms set to show on the calendar). It shows their titles, times, rooms, hosts and the name on each booking ("Birthday party · Rivera"). It never shows emails, phone numbers, booking notes or RSVP lists. Turning it on (or previewing it) needs both **Manage rooms and calendars** and **Manage bookings**. Anyone with the screen's link can see what it shows, so keep the link in the TV's playlist and nowhere public. A booking isn't in any calendar, so a `cal=` filter leaves bookings out.
|
|
242
|
+
|
|
243
|
+
- **Copy link** copies the screen's address. **Rotate link** makes a new key; the old address stops working at once (the TV shows "This screen isn't available" until it gets the new link).
|
|
244
|
+
- **Last check-in** is when the screen last fetched its events. A TV that is on checks in every 5 minutes, so an old check-in means it's off or offline.
|
|
245
|
+
- Wrong keys are rate-limited per network address: after 60 wrong keys in 15 minutes, further wrong keys from that address get "Too many" until the window ends. A screen's own key always opens it.
|
|
246
|
+
- Screens go into backups without their keys. After a restore, each screen has a new link: copy it into each TV again.
|
|
233
247
|
|
|
234
248
|
### Setting up a TV with AbleSign
|
|
235
249
|
|
|
236
250
|
AbleSign (app.ablesign.tv/screens) plays a playlist on each screen; a website is one item.
|
|
237
251
|
|
|
238
|
-
1. In AbleSign, open **Websites → Add website** and paste the
|
|
252
|
+
1. In AbleSign, open **Websites → Add website** and paste the screen's link (or a quick link).
|
|
239
253
|
2. Add the website to the screen's playlist with a long duration (AbleSign reloads a website every time the playlist plays it; the page refreshes itself).
|
|
240
254
|
3. Put a short image before it in the playlist, so the page loads in the background.
|
|
241
255
|
4. The Fire TV Stick needs Fire OS 6 or later (not Vega OS) and AbleSign 2.2.2 or later.
|
|
@@ -253,4 +267,4 @@ A theme's `theme.css` loads after the board's own styles. The simplest way to re
|
|
|
253
267
|
.acms-display[data-theme='light'] { --display-bg: #fffaf2; --display-fg: #1d1a16; }
|
|
254
268
|
```
|
|
255
269
|
|
|
256
|
-
Keep blocks flat: the built-in look has no accent stripes or gradients. The display leaves out the site's header, footer, head code and analytics. A site file at `src/pages/display/calendar.astro` replaces that page.
|
|
270
|
+
Keep blocks flat: the built-in look has no accent stripes or gradients. The display leaves out the site's header, footer, head code and analytics. A site file at `src/pages/display/calendar.astro` (or `display/s/[key].astro`) replaces that page.
|
package/docs/mobile.md
CHANGED
|
@@ -124,7 +124,7 @@ Read the app's `AGENTS.md` too: it holds the same rules, next to the code.
|
|
|
124
124
|
| `useManagedBooking(token)` / `useCancelBooking` / `useRescheduleBooking` / `useRescheduleAvailability` | the booker's own booking (from `book()`'s `manageToken`): `can.cancel`, `can.reschedule`, `can.closed` (change cutoff passed: show "Contact us") |
|
|
125
125
|
| `useConfirmBooking(token)` | an RSVP on a waitlist: `waitlisted` shows "#3 in line · 2 seats" (`waitlistLineText(booking, words, locale)`) and can leave (`useCancelBooking`); `offered` holds a spot until `booking.holdUntil` (`holdText`: "We're holding it until Fri, Oct 24 at 9 PM") and `can.confirm` takes it. `isReleased(error)`: the hold ran out (410) and the spot went to the next person |
|
|
126
126
|
| `useGuest()` / `useMyBookings({ when })` | guest sign-in with an emailed code, then every booking made with that email; check `features.guestSignIn` |
|
|
127
|
-
| `useLinks()` / `useForm(id)` / `useSubmitForm(id)` | the links page; published forms and sending answers; check `features.links` / `features.forms
|
|
127
|
+
| `useLinks()` / `useForm(id)` / `useSubmitForm(id)` | the links page; published forms (in the app's language where the form has its words) and sending answers (choices as shown; `time` answers are 24-hour `HH:mm`, and `answerProblems` checks them); check `features.links` / `features.forms`. Ask a question type you don't know as short text |
|
|
128
128
|
| `useLive()` | live video: `snapshot`, `status` (`live`/`upcoming`/`offline`), `isLive`, localized `view` text, the owner's `display` choices (`style`, `bar`, `header`, `dock`). Polls every 30 s only in the foreground, rechecks on resume, drops an expired "live" even offline; idle while `features.live` is false. Render `<LiveCard>` / `<LiveTag>` |
|
|
129
129
|
| `useResources()` / `useAvailability({ resource, link })` / `useBooking()` | bookings |
|
|
130
130
|
| `useRoomFree({ room, start, end })` | bookings: is a requestable room free (no names) |
|
package/integration.mjs
CHANGED
|
@@ -863,6 +863,11 @@ export default function acms(options = {}) {
|
|
|
863
863
|
// TV displays: quick links (everything in the address) and their offline worker
|
|
864
864
|
['/display/calendar', 'display/calendar.astro'],
|
|
865
865
|
['/display/sw.js', 'display/sw.js.ts'],
|
|
866
|
+
// saved screens: a long random key is the whole address (Admin → Calendar → Screens)
|
|
867
|
+
['/display/s/[key]', 'display/s/[key].astro'],
|
|
868
|
+
['/admin/calendar/screens', 'admin/calendar/screens/index.astro'],
|
|
869
|
+
['/admin/calendar/screens/preview', 'admin/calendar/screens/preview.astro'],
|
|
870
|
+
['/api/calendar/screens', 'api/calendar/screens.ts'],
|
|
866
871
|
['/api/v1/events', 'api/v1/events.ts'],
|
|
867
872
|
['/api/v1/calendars', 'api/v1/calendars.ts'],
|
|
868
873
|
// RSVPs from apps: a date's status, then POST /bookings (room bookings there need bookings on)
|
|
@@ -32,7 +32,7 @@ const tokens = {
|
|
|
32
32
|
|
|
33
33
|
type Answers = Record<string, string | string[]>;
|
|
34
34
|
const OTHER = '__other__';
|
|
35
|
-
const keyboards: Partial<Record<FormField['type'], KeyboardTypeOptions>> = { email: 'email-address', phone: 'phone-pad', number: 'numeric', url: 'url' };
|
|
35
|
+
const keyboards: Partial<Record<FormField['type'], KeyboardTypeOptions>> = { email: 'email-address', phone: 'phone-pad', number: 'numeric', url: 'url', time: 'numbers-and-punctuation' };
|
|
36
36
|
// "Other" choices send the typed text instead
|
|
37
37
|
const withOther = (f: FormField, value: string | string[] | undefined, other: string): string | string[] => {
|
|
38
38
|
if (f.type === 'checkboxes') { const list = Array.isArray(value) ? value : []; return list.includes(OTHER) ? [...list.filter((v) => v !== OTHER), other.trim()] : list; }
|
|
@@ -269,7 +269,7 @@ export function RsvpPanel({ slug, start, children }: { slug: string; start: stri
|
|
|
269
269
|
}
|
|
270
270
|
return text(f.label, String(values[f.id] ?? ''), (v) => set(f.id, v), {
|
|
271
271
|
required: f.required, help: f.help, problem, multiline: f.type === 'textarea', keyboard: keyboards[f.type],
|
|
272
|
-
placeholder: f.placeholder || (f.type === 'date' ? words.datePlaceholder : undefined), auto: f.type === 'email' ? 'email' : f.type === 'phone' ? 'tel' : undefined,
|
|
272
|
+
placeholder: f.placeholder || (f.type === 'date' ? words.datePlaceholder : f.type === 'time' ? words.timePlaceholder : undefined), auto: f.type === 'email' ? 'email' : f.type === 'phone' ? 'tel' : undefined,
|
|
273
273
|
});
|
|
274
274
|
};
|
|
275
275
|
|
|
@@ -32,7 +32,7 @@ const tokens = {
|
|
|
32
32
|
|
|
33
33
|
type Answers = Record<string, string | string[]>;
|
|
34
34
|
const OTHER = '__other__';
|
|
35
|
-
const keyboards: Partial<Record<FormField['type'], KeyboardTypeOptions>> = { email: 'email-address', phone: 'phone-pad', number: 'numeric', url: 'url' };
|
|
35
|
+
const keyboards: Partial<Record<FormField['type'], KeyboardTypeOptions>> = { email: 'email-address', phone: 'phone-pad', number: 'numeric', url: 'url', time: 'numbers-and-punctuation' };
|
|
36
36
|
// "Other" choices send the typed text instead
|
|
37
37
|
const withOther = (f: FormField, value: string | string[] | undefined, other: string): string | string[] => {
|
|
38
38
|
if (f.type === 'checkboxes') { const list = Array.isArray(value) ? value : []; return list.includes(OTHER) ? [...list.filter((v) => v !== OTHER), other.trim()] : list; }
|
|
@@ -269,7 +269,7 @@ export function RsvpPanel({ slug, start, children }: { slug: string; start: stri
|
|
|
269
269
|
}
|
|
270
270
|
return text(f.label, String(values[f.id] ?? ''), (v) => set(f.id, v), {
|
|
271
271
|
required: f.required, help: f.help, problem, multiline: f.type === 'textarea', keyboard: keyboards[f.type],
|
|
272
|
-
placeholder: f.placeholder || (f.type === 'date' ? words.datePlaceholder : undefined), auto: f.type === 'email' ? 'email' : f.type === 'phone' ? 'tel' : undefined,
|
|
272
|
+
placeholder: f.placeholder || (f.type === 'date' ? words.datePlaceholder : f.type === 'time' ? words.timePlaceholder : undefined), auto: f.type === 'email' ? 'email' : f.type === 'phone' ? 'tel' : undefined,
|
|
273
273
|
});
|
|
274
274
|
};
|
|
275
275
|
|
package/package.json
CHANGED
|
@@ -19,11 +19,11 @@ const { page, settings } = Astro.props;
|
|
|
19
19
|
const theme = getTheme(settings.activeTheme);
|
|
20
20
|
const layout = settings.sectionPages[page.id];
|
|
21
21
|
const repo = await getRepo(Astro.locals);
|
|
22
|
-
const
|
|
22
|
+
const locale = Astro.props.locale ?? requestLocale(Astro.url, Astro.locals);
|
|
23
|
+
const bodyHtml = page.bodyHtml ? highlightCodeHtml(await expandDynamicBlocks(page.bodyHtml, repo, locale)) : '';
|
|
23
24
|
// admin design preview: sections carry their ids so the Design screen can
|
|
24
25
|
// open the one you click (see Site.astro's preview script)
|
|
25
26
|
const editable = Astro.props.editable ?? (await resolvePreview(Astro.url, Astro.cookies, Astro.locals)).role === 'admin';
|
|
26
|
-
const locale = Astro.props.locale ?? requestLocale(Astro.url, Astro.locals);
|
|
27
27
|
const now = Date.now();
|
|
28
28
|
const nowWall = wallNow(settings.timezone || 'UTC', now);
|
|
29
29
|
// another language: dates/times format in it (components read settings.locale)
|