mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:01:08 +02:00
feat(calendar): M4.a — events adopt the unified visibility system
Third consumer of @mana/shared-privacy. Calendar events now carry a VisibilityLevel the owner flips from the EventDetailModal via <VisibilityPicker>; a new calendar.events embed source lets the user drop a moduleEmbed block on their website that pulls their public events in. This unblocks concrete use-cases the Website-Builder audit surfaced: band tour dates, public workshops, public rehearsals on a team-space website, meeting-with-the-host pages. Changes: - calendar/types: visibility + unlistedToken + visibilityChangedAt + visibilityChangedBy on LocalEvent; CalendarEvent (UI type) requires visibility. timeBlockToCalendarEvent forwards the field; cross-module TimeBlocks (tasks, habits, time entries) without an owning LocalEvent fall back to 'space' so they stay off the public embed - calendar/stores/events: createEvent stamps defaultVisibilityFor(activeSpace.type); createDraftEvent seeds a 'private' draft until the user explicitly opts in; new setVisibility(id, level) mints/clears the unlisted token on the transition boundary and emits cross-module VisibilityChanged - calendar/components/EventDetailModal: <VisibilityPicker compact> sits in the modal-actions row left of copy/edit/delete website embed: - website-blocks/moduleEmbed/schema: EmbedSourceSchema adds 'calendar.events'; the filter shape gains optional `upcomingDays` (1-365) and `tagIds` (up to 16). Old filters (isFavorite/status/kind) remain — each source uses only its own subset - website/embeds: resolveCalendarEvents gates hard on canEmbedOnWebsite(event.visibility ?? 'private'), joins each event to its LocalTimeBlock for the real start/end, applies the optional upcomingDays window and tag-id AND-filter, sorts upcoming-first with id as stable tiebreaker Redaction is whitelist-per-design (plan §2): the inlined snapshot carries only title, formatted date range, and location — NOT description, reminders, tag labels, or the guest list. Fields that typically hold private context stay out of the public blob regardless of the visibility toggle. Verified: - pnpm check (web): 7450 files, 0 errors - pnpm test calendar + website: 26/26 - pnpm run validate:all green Next: M4.b — Todo, M4.c — Goals. Same pattern; split out because goals lives under $lib/companion/goals/ with its own structure and Todo has a complex view-column/filter surface that warrants its own PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0e9f574dfb
commit
ac44d51363
5 changed files with 177 additions and 3 deletions
|
|
@ -26,7 +26,7 @@ export const EmbedResolvedSchema = z.object({
|
|||
* Supported embed sources. Add new sources here + a matching provider
|
||||
* in the editor's publish resolver.
|
||||
*/
|
||||
export const EmbedSourceSchema = z.enum(['picture.board', 'library.entries']);
|
||||
export const EmbedSourceSchema = z.enum(['picture.board', 'library.entries', 'calendar.events']);
|
||||
export type EmbedSource = z.infer<typeof EmbedSourceSchema>;
|
||||
|
||||
export const ModuleEmbedSchema = z.object({
|
||||
|
|
@ -38,14 +38,20 @@ export const ModuleEmbedSchema = z.object({
|
|||
layout: z.enum(['grid', 'list']).default('grid'),
|
||||
maxItems: z.number().int().min(1).max(48).default(12),
|
||||
/**
|
||||
* Optional filters depending on source. Library uses { isFavorite?,
|
||||
* status?, kind? }; picture ignores them in M4.
|
||||
* Optional filters depending on source.
|
||||
* library.entries: { isFavorite?, status?, kind? }
|
||||
* picture.board: ignored (board is the source)
|
||||
* calendar.events: { upcomingDays?, tagIds? } — omit upcomingDays
|
||||
* to include past events; tagIds AND-filter on
|
||||
* event tag assignments
|
||||
*/
|
||||
filter: z
|
||||
.object({
|
||||
isFavorite: z.boolean().optional(),
|
||||
status: z.string().max(32).optional(),
|
||||
kind: z.string().max(32).optional(),
|
||||
upcomingDays: z.number().int().min(1).max(365).optional(),
|
||||
tagIds: z.array(z.string().max(64)).max(16).optional(),
|
||||
})
|
||||
.default({}),
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue