@acms-subir/core 0.3.56 → 0.3.57

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 (42) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/cli/migrations.generated.mjs +16 -4
  3. package/docs/calendar.md +116 -2
  4. package/docs/server.md +33 -1
  5. package/package.json +1 -1
  6. package/src/actions/index.ts +52 -9
  7. package/src/components/EventPage.astro +6 -5
  8. package/src/components/calendar/CalendarBoard.tsx +6 -3
  9. package/src/components/editor/event/SingleDates.tsx +75 -15
  10. package/src/lib/calendar/api.ts +23 -2
  11. package/src/lib/calendar/board.ts +3 -2
  12. package/src/lib/calendar/cards.ts +4 -4
  13. package/src/lib/calendar/change-migration.ts +17 -3
  14. package/src/lib/calendar/data.ts +9 -2
  15. package/src/lib/calendar/event-words.ts +8 -0
  16. package/src/lib/calendar/events.ts +128 -15
  17. package/src/lib/calendar/google/map.ts +1 -1
  18. package/src/lib/calendar/host-requests.ts +65 -10
  19. package/src/lib/calendar/ics-write.ts +65 -15
  20. package/src/lib/calendar/member-words.ts +14 -1
  21. package/src/lib/calendar/moves.ts +15 -9
  22. package/src/lib/calendar/request-admin.ts +2 -1
  23. package/src/lib/calendar/request-service.ts +5 -5
  24. package/src/lib/calendar/schedule.ts +42 -8
  25. package/src/lib/calendar/segments.ts +522 -0
  26. package/src/lib/editor/schedule.ts +2 -2
  27. package/src/lib/mobile/calendar.ts +1 -0
  28. package/src/lib/mobile/schemas.ts +10 -3
  29. package/src/lib/push/service.ts +42 -10
  30. package/src/lib/push/types.ts +2 -0
  31. package/src/lib/repo/migrations.ts +5 -2
  32. package/src/lib/repo/sql.ts +24 -8
  33. package/src/lib/repo/types.ts +3 -1
  34. package/src/lib/schema.ts +24 -0
  35. package/src/lib/sections.ts +1 -1
  36. package/src/lib/sync/catalog.ts +1 -0
  37. package/src/pages/admin/calendar/events/index.astro +9 -8
  38. package/src/pages/admin/calendar/requests.astro +5 -5
  39. package/src/pages/api/calendar/events/index.ts +2 -1
  40. package/src/pages/members/events/index.astro +84 -6
  41. package/src/server/index.ts +46 -3
  42. package/src/styles/editor.css +2 -0
@@ -256,6 +256,39 @@ export async function syncGoogleCalendar(repo: Repo, context: Pick<WriteContext,
256
256
  return syncGoogle(internal(repo), context.locals);
257
257
  }
258
258
 
259
+ // ---- from this date on -------------------------------------------------------
260
+ // A repeating event changed from one of its dates on (new times, weekday,
261
+ // rule, rooms or place), the dates before it kept as they were. It stays one
262
+ // event; the change is a segment on its row (CalEvent.segments). RSVPs,
263
+ // cancelled and moved dates follow to the new date nearest them within half
264
+ // a week (`npx acms docs calendar`). Staff and GOOGLE_SYNC only (room clashes
265
+ // warn); hosts send requestFromDate. All are idempotent (`changed: false`);
266
+ // `quiet` skips the member push, and RSVP emails still go.
267
+
268
+ const segments = () => import('../lib/calendar/segments');
269
+ export type { ChangeFromInput, ChangeFromResult } from '../lib/calendar/segments';
270
+ export type { SeriesSegment } from '../lib/schema';
271
+
272
+ /** Change the series from `fromKey` (an occurrence key) on: `input.start` is the new first date (wall-clock in the event's zone), `rrule` omitted = the rule in effect on the new weekday. */
273
+ export async function changeFromDate(repo: Repo, who: Who | typeof GOOGLE_SYNC, eventId: string, fromKey: string, input: import('../lib/calendar/segments').ChangeFromInput, context: WriteContext, opts: { quiet?: boolean } = {}): Promise<import('../lib/calendar/segments').ChangeFromResult> {
274
+ return (await segments()).changeFromDate(internal(repo), who, eventId, fromKey, input, { url: context.url, locals: context.locals }, opts);
275
+ }
276
+
277
+ /** Cancel every date from `fromKey` on: listed, struck through, holding no room; their RSVPs are cancelled (emailed). */
278
+ export async function cancelFromDate(repo: Repo, who: Who | typeof GOOGLE_SYNC, eventId: string, fromKey: string, context: WriteContext, opts: { quiet?: boolean } = {}): Promise<import('../lib/calendar/segments').ChangeFromResult> {
279
+ return (await segments()).cancelFromDate(internal(repo), who, eventId, fromKey, { url: context.url, locals: context.locals }, opts);
280
+ }
281
+
282
+ /** Undo cancelFromDate (from any date it covers). The dates are back; their RSVPs stay cancelled. */
283
+ export async function restoreFromDate(repo: Repo, who: Who | typeof GOOGLE_SYNC, eventId: string, fromKey: string, context: WriteContext, opts: { quiet?: boolean } = {}): Promise<import('../lib/calendar/segments').ChangeFromResult> {
284
+ return (await segments()).restoreFromDate(internal(repo), who, eventId, fromKey, { url: context.url, locals: context.locals }, opts);
285
+ }
286
+
287
+ /** Remove the change that took over at `from`: the rule before it carries on, RSVPs and dates following back. */
288
+ export async function revertFromDate(repo: Repo, who: Who | typeof GOOGLE_SYNC, eventId: string, from: string, context: WriteContext, opts: { quiet?: boolean } = {}): Promise<import('../lib/calendar/segments').ChangeFromResult> {
289
+ return (await segments()).revertFromDate(internal(repo), who, eventId, from, { url: context.url, locals: context.locals }, opts);
290
+ }
291
+
259
292
  // ---- rooms -----------------------------------------------------------------
260
293
  // Events hold rooms (CalEvent.roomHold) while they happen. These find where
261
294
  // two things want the same room. They don't check permissions, and their
@@ -361,7 +394,7 @@ export async function setHostedOccurrence(repo: Repo, who: Who, eventId: string,
361
394
  // that staff who can publish approve or decline; both sides are emailed.
362
395
 
363
396
  const hostRequests = () => import('../lib/calendar/host-requests');
364
- export type { HostMoveResult, MoveRequestData } from '../lib/calendar/host-requests';
397
+ export type { HostMoveResult, MoveRequestData, FromDateRequestData } from '../lib/calendar/host-requests';
365
398
 
366
399
  /** A host moves one date: `{ moved }` at once, or `{ request }` when moves need approval. Errors as moveOccurrence's, plus `code` room-not-allowed (a room the event doesn't use and nobody may request). */
367
400
  export async function moveHostedDate(repo: Repo, who: Who, eventId: string, occurrenceKey: string, input: import('../lib/calendar/moves').MoveInput, context: WriteContext): Promise<import('../lib/calendar/host-requests').HostMoveResult> {
@@ -383,6 +416,11 @@ export async function requestDateMove(repo: Repo, who: Who, eventId: string, occ
383
416
  return (await hostRequests()).requestDateMove(internal(repo), who, eventId, occurrenceKey, input, message, context);
384
417
  }
385
418
 
419
+ /** A host asks staff to change their event from one date on (checked like changeFromDate; rooms among the event's and the requestable ones). Staff are emailed. */
420
+ export async function requestFromDate(repo: Repo, who: Who, eventId: string, fromKey: string, input: Omit<import('../lib/calendar/segments').ChangeFromInput, 'location'>, message: string, context: WriteContext): Promise<import('../lib/schema').RequestRecord> {
421
+ return (await hostRequests()).requestFromDate(internal(repo), who, eventId, fromKey, input, message, context);
422
+ }
423
+
386
424
  /** A host's own date requests (waiting, approved, declined and withdrawn), newest first. */
387
425
  export async function listMyDateRequests(repo: Repo, who: Who, eventId?: string): Promise<import('../lib/schema').RequestRecord[]> {
388
426
  if (!who) return [];
@@ -394,8 +432,13 @@ export async function withdrawDateRequest(repo: Repo, who: Who, requestId: strin
394
432
  return (await hostRequests()).withdrawDateRequest(internal(repo), who, requestId);
395
433
  }
396
434
 
397
- /** Staff ("Publish content"): approve a host's date move. It runs as them (room clashes come back as warnings) and is recorded as the host's. Pass `seen`, the request's `updatedAt` you showed. */
398
- export async function approveDateRequest(repo: Repo, who: Who, requestId: string, context: WriteContext, seen?: number): Promise<{ request: import('../lib/schema').RequestRecord; moved: import('../lib/calendar/moves').MoveResult }> {
435
+ /**
436
+ * Staff ("Publish content"): approve a host's date request. A move runs as
437
+ * them (room clashes come back as warnings) and is recorded as the host's
438
+ * (`moved`); a change from a date on runs changeFromDate (`changed`). Pass
439
+ * `seen`, the request's `updatedAt` you showed.
440
+ */
441
+ export async function approveDateRequest(repo: Repo, who: Who, requestId: string, context: WriteContext, seen?: number): Promise<{ request: import('../lib/schema').RequestRecord; moved?: import('../lib/calendar/moves').MoveResult; changed?: import('../lib/calendar/segments').ChangeFromResult }> {
399
442
  return (await hostRequests()).approveDateRequest(internal(repo), who, requestId, context, seen);
400
443
  }
401
444
 
@@ -1346,4 +1346,6 @@ html.pe-evr-lock { overflow: hidden; }
1346
1346
  .pe-evd-rooms legend { font-size: 12px; color: var(--text-2); margin-bottom: 4px; padding: 0; }
1347
1347
  .pe-evd-room { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
1348
1348
  .pe-evd-formacts { display: flex; gap: 8px; }
1349
+ .pe-evd-segs { margin-bottom: 8px; padding-bottom: 4px; border-bottom: 1px solid var(--line); }
1350
+ .pe-evd-hint { margin: 0; font-size: 12.5px; color: var(--text-2); }
1349
1351
  .pe-ev-note.is-error { color: var(--red); }