Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "card",
"attributes": {
"name": "Table Seating Planner",
"summary": "The TableSeatingPlanner is a card definition designed to organize and manage seating arrangements for events. It captures key event details such as the event title, date, venue, and hosts. The planner links to guests, tables, and fixtures involved in the event setup. It includes a floor plan with configurable positioning, dimensions, opacity, and lock status to visualize seating arrangements. Additionally, it supports messaging features like invitation and seating messages, and facilitates poster creation with customizable poster images, prompts, and aspect ratios. The primary purpose is to assist event planners in designing, visualizing, and organizing seating plans for successful gatherings.",
"summary": "Seating charts are the most stressful part of planning an event — dozens of guests, family politics, and a floor plan that keeps changing right up to the big day. Table Seating Planner takes the pain out of it: lay out your venue with drag-and-drop tables and fixtures (or import your own floor plan), then seat everyone with a simple drag.\n\nOpen the demo to see it shine: ask the AI assistant to arrange the seats and watch every guest find a place, step through the guided setup wizard to start an event of your own, then carry your theme through to matching invitations and posters for the big day.",
"cardInfo": {
"name": "Table Seating Planner",
"notes": null,
Expand Down Expand Up @@ -170,17 +170,27 @@
},
"images.0": {
"links": {
"self": "../Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-b6ba6f10.png"
"self": "../Screenshots/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8-81ceb0df.png"
}
},
"images.1": {
"links": {
"self": "../Screenshots/8b596412-e2ed-4d38-877b-40d12c1876dc-940f80d5.png"
"self": "../Screenshots/setup-wizard-c0a3fa49.png"
}
},
"images.2": {
"links": {
"self": "../Screenshots/36305cb1-39a5-4a95-b594-ab4af32fa883-e6851a31.png"
"self": "../Screenshots/add-element-d340bad2.png"
}
},
"images.3": {
"links": {
"self": "../Screenshots/seating-print-05985ab6.png"
}
},
"images.4": {
"links": {
"self": "../Screenshots/ai-poster-9d32df2e.png"
}
},
"skills.0": {
Expand All @@ -193,6 +203,26 @@
"self": null
}
},
"categories.0": {
"links": {
"self": "../../Category/creative-projects"
}
},
"categories.1": {
"links": {
"self": "../../Category/ai-automation"
}
},
"cardInfo.theme": {
"links": {
"self": null
}
},
"cardInfo.cardThumbnail": {
"links": {
"self": "../ListingThumbnails/table-seating-b62d23ba.png"
}
},
"examples.0": {
"links": {
"self": "../TableSeatingPlanner/85c8a56e-d0ee-414d-8d7f-ec94c7aff8c8"
Expand All @@ -208,101 +238,81 @@
"self": "../TableSeatingPlanner/36305cb1-39a5-4a95-b594-ab4af32fa883"
}
},
"examples.3": {
"supportingCards.0": {
"links": {
"self": "../Guest/andy"
}
},
"examples.4": {
"supportingCards.1": {
"links": {
"self": "../Guest/cheche"
}
},
"examples.5": {
"supportingCards.2": {
"links": {
"self": "../Guest/chuan"
}
},
"examples.6": {
"supportingCards.3": {
"links": {
"self": "../Guest/justin"
}
},
"examples.7": {
"supportingCards.4": {
"links": {
"self": "../Guest/nana"
}
},
"examples.8": {
"supportingCards.5": {
"links": {
"self": "../Guest/richy"
}
},
"examples.9": {
"supportingCards.6": {
"links": {
"self": "../Guest/robin"
}
},
"examples.10": {
"supportingCards.7": {
"links": {
"self": "../Guest/yvonne"
}
},
"examples.11": {
"supportingCards.8": {
"links": {
"self": "../Host/amy"
}
},
"examples.12": {
"supportingCards.9": {
"links": {
"self": "../Host/lucas"
}
},
"examples.13": {
"supportingCards.10": {
"links": {
"self": "../LayoutTemplate/ceremony"
}
},
"examples.14": {
"supportingCards.11": {
"links": {
"self": "../LayoutTemplate/reception"
}
},
"examples.15": {
"supportingCards.12": {
"links": {
"self": "../Theme/double-happiness-banquet"
}
},
"examples.16": {
"supportingCards.13": {
"links": {
"self": "../Theme/italian-cathedral"
}
},
"examples.17": {
"links": {
"self": "../../index"
}
},
"categories.0": {
"links": {
"self": "../../Category/creative-projects"
}
},
"categories.1": {
"supportingCards.14": {
"links": {
"self": "../../Category/ai-automation"
}
},
"cardInfo.theme": {
"links": {
"self": null
}
},
"cardInfo.cardThumbnail": {
"links": {
"self": "../ListingThumbnails/table-seating-b62d23ba.png"
"self": "../Theme/parisian"
}
}
}
}
}
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# SetupWizard\n\n## Summary\n\nFirst-run setup wizard that the isolated planner shows over the canvas until the plan has content. It is a thin wrapper composing the shared catalog `<Stepper>` component (`@cardstack/catalog/aef6db-stepper/stepper`), which supplies the modal scrim, vertical step rail, heading, and Back/Next navigation; this file owns only the four domain steps — event details (name, date, venue), hosts (the couple), guests, and finally starting from a layout template (previewed with LayoutPreview) or a blank canvas — plus the gold corner ornament, expressed as `StepperStep` descriptors with per-step `isComplete` checks. It talks to the host planner only through args: every mutation happens via the callbacks you pass in, and the planner's `hosts` / `guests` linksToMany field components are passed as `@hostsField` / `@guestsField` so newly added people render as fitted cards inside the wizard. Every step is mandatory — Next stays disabled until the step is complete (all three event fields, at least one host, at least one guest) and there is no Skip; the ✕ close (and the final \"Start blank\" finish action, both wired to `@onSkip`) is the one escape hatch, and dismissal isn't persisted, so the wizard reopens the next time an incomplete planner is opened.\n\n## Import\n\n```js\nimport SetupWizard from '../components/setup-wizard';\n```\n\n## Template Usage\n\nAs invoked from the isolated planner view:\n\n```gjs\n<SetupWizard\n @hostsField={{@fields.hosts}}\n @guestsField={{@fields.guests}}\n @eventTitle={{@model.eventTitle}}\n @venue={{@model.venue}}\n @eventDate={{this.eventDateInput}}\n @guestCount={{this.totalGuests}}\n @hostCount={{this.hostsCount}}\n @templates={{this.templates}}\n @templatesLoading={{this.templatesLoading}}\n @onEventTitle={{this.setEventTitle}}\n @onVenue={{this.setVenue}}\n @onEventDate={{this.setEventDate}}\n @onAddHosts={{this.addHosts}}\n @onAddGuests={{this.addGuests}}\n @onLoadTemplates={{this.loadTemplates}}\n @onApplyTemplate={{this.applyTemplateFromWizard}}\n @onSkip={{this.skipWizard}}\n/>\n```",
"ref": {
"module": "../components/setup-wizard",
"name": "default"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# ApplySeatingPlanCommand\n\n## Summary\n\nApplies a complete seating arrangement to a Table Seating Planner card: it loads the planner with `GetCardCommand`, expands each party-head guest id into the full party (companions linked via `parentGuest`), and writes `seatedGuests` on every table — tables omitted from the plan are emptied. The plan is validated deterministically: out-of-range table indexes and unknown party ids are skipped and reported, per-table capacity is enforced (overflow is trimmed, and `section` grids are regrown to fit), and leftover parties are auto-seated by VIP status and category priority while honouring `reservedCategories`. Input is an `ApplySeatingPlanInput` card (`plannerCardId`, `assignments` of `TableAssignmentField`, optional `summary`); output is an `ApplySeatingPlanResult` (`seatedCount`, `message`). It makes no AI call itself — it is the deterministic half of the AI seating flow.\n\n## Import\n\n```js\nimport {\n ApplySeatingPlanCommand,\n TableAssignmentField,\n} from '../commands/apply-seating-plan-command';\n```\n\n## Usage\n\nIn this package the command is not called from a component. It is registered on the Seat Arranger skill card (`Skill/seat-arranger.json`), and the AI assistant executes it after reasoning out the arrangement — the planner opens that room with `UseAiAssistantCommand` (see `components/tsp.gts`):\n\n```json\n{\n \"codeRef\": {\n \"name\": \"ApplySeatingPlanCommand\",\n \"module\": \"../commands/apply-seating-plan-command\"\n },\n \"requiresApproval\": false\n}\n```\n\nInvoked directly, the input shape is:\n\n```js\nlet ctx = this.args.context?.commandContext;\nlet result = await new ApplySeatingPlanCommand(ctx).execute({\n plannerCardId: planner.id,\n assignments: [\n new TableAssignmentField({ tableIndex: 0, partyIds: [avaId, noahId] }),\n new TableAssignmentField({ tableIndex: 1, partyIds: [miaId] }),\n ],\n summary: 'Family up front, friends by the dance floor.',\n});\n```\n",
"ref": {
"name": "ApplySeatingPlanCommand",
"module": "../commands/apply-seating-plan-command"
Expand Down Expand Up @@ -42,4 +42,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# PlaceCardView\n\n## Summary\n\nRenders a wedding place card: an optional uppercase event-title kicker, the guest name in large serif type, an accent rule, then the table name and an optional italic message, with an optional `@logoUrl` event logo as a faint centered watermark behind the content. It is the planner's stationery visual — the print sheet renders one per seated guest, so what prints matches this component exactly. Colors and fonts flow from the theme's semantic tokens with Parisian fallbacks. Sizing is container-query driven, and the guest name auto-fits its width by character count (the print sheet is hidden on screen, so real measurement isn't possible), so the same component reads well as a large isolated card, a small fitted tile, or a print-sheet cell.\n\n## Import\n\n```js\nimport { PlaceCardView } from '../components/card-views';\n```\n\n## Template Usage\n\nAs rendered on the planner's print sheet (all args optional; the name falls back to \"Guest Name\", and `@message` adds an italic closing line):\n\n```gjs\n<PlaceCardView\n @eventTitle={{@model.eventTitle}}\n @guestName={{g.fullName}}\n @tableName={{row.table.name}}\n @logoUrl={{this.eventLogoURL}}\n/>\n```",
"ref": {
"module": "../components/card-views",
"name": "PlaceCardView"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# Guest\n\n## Summary\nGuest models one invitee on the wedding guest list. It extends Person, so it inherits `fullName`, `photo`, and the computed `photoURL`, and adds a `category` enum (Bride's Family, Groom's Family, Close Friend, Friends, College, Colleagues, Others), a `vip` boolean, and a `parentGuest` link that is set on a +1 to point back at the guest who brings them. In the Table Seating Planner package, Guest instances are the people you drag onto tables: the planner links to them via `guests = linksToMany(Guest)`, and each Table field tracks its own `seatedGuests`.\n\n## Import\n```js\nimport { Guest } from '../guest';\n```\n\n## Usage\nGuest is a card, so consuming cards link to it rather than containing it — the same wiring the planner uses:\n\n```js\nimport { CardDef, field, linksToMany } from 'https://cardstack.com/base/card-api';\nimport { Guest } from '../guest';\n\nexport class SeatingChart extends CardDef {\n @field guests = linksToMany(() => Guest);\n}\n```\n\n## Template Usage\n```handlebars\n<@fields.guests @format='embedded' />\n```\nRenders each linked guest as an avatar row with name, category dot, VIP badge, and +1 attribution.",
"ref": {
"module": "../guest",
"name": "Guest"
Expand All @@ -19,9 +19,44 @@
}
},
"relationships": {
"linkedExamples": {
"linkedExamples.0": {
"links": {
"self": null
"self": "../Guest/andy"
}
},
"linkedExamples.1": {
"links": {
"self": "../Guest/cheche"
}
},
"linkedExamples.2": {
"links": {
"self": "../Guest/chuan"
}
},
"linkedExamples.3": {
"links": {
"self": "../Guest/justin"
}
},
"linkedExamples.4": {
"links": {
"self": "../Guest/nana"
}
},
"linkedExamples.5": {
"links": {
"self": "../Guest/richy"
}
},
"linkedExamples.6": {
"links": {
"self": "../Guest/robin"
}
},
"linkedExamples.7": {
"links": {
"self": "../Guest/yvonne"
}
},
"cardInfo.cardThumbnail": {
Expand All @@ -42,4 +77,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# categoryLabel\n\n## Summary\n\nReturns the display label for a guest-category value (e.g. `brides-family` → \"Bride's Family\") from the `GUEST_CATEGORIES` list, or an empty string when the value is missing or unknown. The planner uses it wherever a guest's category is shown as text — the category chips in the Guest card views and the guest roster, legend, and hover popover in the main planner component (`components/tsp.gts`).\n\n## Import\n\n```js\nimport { categoryLabel } from '../utils/index';\n```\n\n## Usage\n\n```js\ncategoryLabel(value: string | null | undefined): string\n\ncategoryLabel('brides-family'); // \"Bride's Family\"\n```",
"ref": {
"module": "../utils/index",
"name": "categoryLabel"
Expand Down Expand Up @@ -42,4 +42,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# FixtureGlyph\n\n## Summary\n\nAn SVG glyph for a floor-plan fixture, chosen by `@kind`: plant, tree, balloon, stage, red-carpet, dance-floor, arch, projector, cake, bar, curved-wall, rect-decor, round-decor, or circle-cluster, with a dashed placeholder box for unknown kinds. `@color` tints the drawing and `@pattern` ('solid' | 'outline' | 'soft') controls the fill — outline removes it, soft renders it at 45% opacity. Strokes use `vector-effect: non-scaling-stroke`, so line weight stays crisp whether it is stretched across the canvas, shrunk into LayoutPreview, or shown as a picker swatch.\n\n## Import\n\n```js\nimport FixtureGlyph from '../components/fixture-glyph';\n```\n\n## Template Usage\n\n```gjs\n<FixtureGlyph\n @kind={{f.kind}}\n @color={{f.color}}\n @pattern={{f.pattern}}\n/>\n```",
"ref": {
"module": "../components/fixture-glyph",
"name": "default"
Expand Down Expand Up @@ -42,4 +42,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# TableCardView\n\n## Summary\n\nRenders a table-number card: an optional uppercase event-title kicker, the table name in large serif type flanked by stationery rules, and an optional italic `@accent` line, with an optional `@logoUrl` event logo as a faint centered watermark. Like PlaceCardView it is a planner stationery visual — the print sheet renders one as the header cell for each table, so print output matches this component exactly, and its colors and fonts flow from the theme's semantic tokens with Parisian fallbacks. Container-query sizing plus character-count auto-fitting of the name let it scale from an isolated card down to a print-sheet cell.\n\n## Import\n\n```js\nimport { TableCardView } from '../components/card-views';\n```\n\n## Template Usage\n\nAs rendered on the planner's print sheet (all args optional; the name falls back to \"Table\"):\n\n```gjs\n<TableCardView\n @eventTitle={{@model.eventTitle}}\n @tableName={{row.table.name}}\n @logoUrl={{this.eventLogoURL}}\n/>\n```",
"ref": {
"module": "../components/card-views",
"name": "TableCardView"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"data": {
"type": "card",
"attributes": {
"readMe": null,
"readMe": "# TableSeatingPlannerFitted\n\n## Summary\n\nThe compact card formats for the Table Seating Planner. One component renders all the fitted breakpoints, switched by container queries: a badge (✦ mark, event title, guest count), a strip (title, venue or table count, and a seated tag), a tile (a LayoutPreview floor-plan hero above the title and stats), and a wide card (title, date, venue, and stat pills beside a floor-plan panel). The title falls back to \"Seating Plan\", and the seated count is de-duplicated across tables. When the planner has no linked theme (`cardInfo.theme`), a `.tsp-default-theme` class pins the semantic tokens to the package's Parisian palette so app-level defaults can't restyle the card; with a linked theme the class is omitted and the theme's tokens flow through.\n\n## Import\n\n```js\nimport { TableSeatingPlannerFitted } from '../components/tsp';\n```\n\n## Template Usage\n\nNot typically invoked directly — the card definition assigns it as both the fitted and embedded formats in `table-seating-planner.gts`:\n\n```js\nTableSeatingPlanner.embedded = TableSeatingPlannerFitted;\nTableSeatingPlanner.fitted = TableSeatingPlannerFitted;\n```",
"ref": {
"module": "../components/tsp",
"name": "TableSeatingPlannerFitted"
Expand Down Expand Up @@ -42,4 +42,4 @@
}
}
}
}
}
Loading
Loading