After Johan’s visit from Engestofte Gods, my teammate and I decided to build our elective exam project around the work he described. The aim is to give him and his staff a shared way to manage events, from the first inquiry through planning and delivery to the final invoice.
The table planner is still part of it, but the project has grown around a more basic problem: the same event information gets entered and updated in several places. If a couple changes their guest count, that change needs to reach the offer, the seating plan, and the kitchen. We want to enter it once and let the relevant parts of the system read from there.
This post is the plan for that application. We have the shared context and development tasks written down; the work ahead is turning them into a complete flow that we can demonstrate, and eventually put in front of Johan.
One event through the whole process#
We’re building around a common event workflow. A wedding, a birthday, and a company party need different details and pricing, but they all begin with an inquiry, go through a conversation and an offer, and eventually become work for the team.
An inquiry can come through the website or be entered by a member of staff after a phone call. Both should reach the same process. The manual form comes first: our project context identifies the phone as the most common channel, so the application needs to work well when Johan is simply taking down what someone tells him.
From there, he can check availability, develop an offer, and move the event towards confirmation. Confirmation generates preparation tasks from templates. Those categories follow the work Engestofte already organises: contact, table setup, decorations, accommodation, drinks, food, invoices, and staffing.
flowchart LR
I[Inquiry] --> D[In dialogue]
D --> O[Offer sent]
O --> C[Confirmed]
C --> P[Preparation]
P --> R[Ready]
R --> F[Completed]
F --> V[Invoiced]
Ready is a decision Johan makes himself. It corresponds to his green status in Trello: the plan is ready for the delivery team. The system can show outstanding work, but it shouldn’t mark an event as ready just because a checklist is empty.
We’re also planning an activity log from the start. If the guest count changes, staff should be able to see who changed it and when. Otherwise there’s no way to tell whether 85 or 78 is the right number.
Four modules, with Web in the middle#
The project is organised around Docs, Web, API, and CMS. Each has its own responsibility. Web is the orchestrator: it combines business data from the API and content from the CMS into the screens customers and staff use.
flowchart TB
Users[Customers and staff] --> Web[Web
Public site, dashboard, customer portal]
Web -->|Events, availability, offers and seating| API[API
Business rules and operational data]
Web -->|Pages, images and translations| CMS[CMS
Content and staff authentication]
CMS -->|Responses| Web
API -->|Responses| Web
Docs[Docs
Shared context and decisions] -.->|Guides development| Web
Docs -.->|Guides development| API
Docs -.->|Guides development| CMS
The solid arrows show application communication. The dotted arrows show how Docs supports development. Docs is something we use while building the system; it isn’t a running service the dashboard calls.
Web: what customers and staff see#
Web is a Next.js application with three parts: the public website, the staff dashboard, and a customer portal accessed through a magic link.
On a holiday-house page, for example, Web can get the description and photographs from the CMS and availability from the API. The visitor sees one page, but the text and photos are maintained in the CMS, while the bookings live in the API.
The dashboard brings inquiries, event details, resource bookings, and follow-ups into the staff’s daily view. The portal gives customers access to their offer, guest list, seating plan, and payment overview without making them navigate the staff interface.
Web coordinates those requests, but the API enforces the business rules, such as whether a resource can be booked or an offer superseded. Hiding a button in the interface doesn’t stop someone from sending the request anyway, so the check has to happen in the API.
API: the event and its rules#
The API owns the operational records: events, customers, contacts, resources, bookings, offers, payment plans, tasks, guests, and seating plans. Every change to the business data goes through it, and it can reject a change that breaks a rule.
That gives the website, dashboard, and portal the same answer to an availability question. It also gives them the same current offer and guest count.
CMS: content and languages#
The Payload CMS owns pages, media, descriptions, and translations. Danish is the source language, with English and German also in the MVP. AI can generate translation drafts there, and a person approves them.
The CMS also manages staff users and roles. Web reads the staff session on the server and forwards the token to the API, which checks it before allowing protected operations. Getting that login path working across the modules is a joint task for us.
Docs: shared terms and decisions#
Docs holds the shared context and architecture decisions. It defines terms such as inquiry, event, booking, and offer, along with the rules attached to them. Our code uses English names; the interface uses Danish.
The development workflow brings the context and decisions from the docs repository into the projects that need them. Generated TypeScript types from the API’s OpenAPI contract also help keep Web aligned with the backend. The documents describe what the data means, and the API contract describes the exact shape of requests and responses.
Rooms and holiday houses need one availability check#
One of the clearest problems from the interview was that a wedding can reserve both a venue and holiday houses, while those bookings are tracked separately today.
We’re modelling both as resources. Their type differs, but they share the booking rules. Two confirmed bookings must never overlap on the same resource. Tentative reservations can overlap one another, but they cannot overlap a confirmed booking. A holiday house must also be within the availability periods Johan has opened for it.
flowchart TD
Event[Event] --> Room[Venue booking]
Event --> Houses[Holiday-house bookings]
Room --> Check[API checks resource availability]
Houses --> Check
Stay[Standalone holiday-house inquiry] --> Check
Check --> Valid{Dates allowed and
no conflicting confirmed booking?}
Valid -->|Yes| Save[Accept booking]
Valid -->|No| Explain[Show the conflict]
The dashboard should make those dependencies visible. Staff need to see which houses are booked and which an event requires, without having to compare separate boards by hand. Online booking of holiday houses is outside this first version; customers begin with an inquiry.
Offers need a history, and payments need flexibility#
An offer changes as the customer makes decisions. We plan to keep versions, with only one sent offer current for an event. Sending a replacement supersedes the previous version, which remains read-only. The customer portal shows the current sent offer, and the payment plan records which offer it was based on.
The payment structure needs room for existing agreements too. Our context describes a change from a fixed deposit to a percentage-based deposit, while older agreements remain in effect. A single deposit field would struggle to represent both.
Instead, the payment plan consists of lines with an amount, a type, and a due date. Johan can choose an editable payment model to generate those lines, then adjust the actual plan. That keeps the agreement’s payment schedule explicit.
Payments will be recorded manually in the MVP. The e-conomic connection is an interface with a mock implementation for now. We can demonstrate the boundary without claiming that the project already creates real accounting entries.
The seating plan belongs to the event#
Customers will be able to import a guest list from CSV or Excel and map the columns in the interface. Guests belong to the event, so rearranging a seating plan does not create a second guest list.
The planner combines a visual editor with rules: guests who should sit together, guests who should sit apart, and people assigned to a particular table or seat. A conventional search algorithm will use those constraints to suggest a seating arrangement. The model will not place guests.
This also connects the plan to the kitchen. Allergies entered with the guests can be grouped by assigned table, giving the cook an overview without having to reconstruct it from emails. The target in our project context is a seating plan for eighty-five guests in under ten minutes. It’s a success criterion we still have to test.
AI helps at the points where people write#
The two operational AI features mirror each other. One turns an email into proposed form values; the other turns event data and a template into a reply or offer-text draft.
flowchart LR
Email[Pasted customer email] --> Extract[AI extracts proposed fields]
Extract --> Review[Staff review and correct the form]
Phone[Phone call or manual entry] --> Review
Review --> Save[Save through the API]
Save --> Data[Event data and template]
Data --> Draft[AI drafts a reply]
Draft --> Preview[Human reviews before sending]
The extraction feature sits above the ordinary form. It doesn’t save directly into the event. Johan reviews the proposed values, with uncertain fields highlighted, and the original text remains available for reference. The form still works without AI.
Reply drafting follows the same principle. The application uses known event data and a template, then shows a preview before sending. Availability, prices, payment schedules, reminders, and status changes remain ordinary application logic.
The CMS translation feature has the same draft-and-approval pattern, but belongs to the content workflow. The coding agents we use to build the project are a separate thing. When an agent helps us write a booking rule, the rule itself is still ordinary code.
Building it as a pair#
Our plan has two development tracks. One concentrates on the API and domain rules; the other covers Web and the CMS. Infrastructure and the shared login flow are joint work. The task order follows dependencies, so the frontend has a real inquiry endpoint to use and the portal has real offers and seating data to display.
The module boundaries make that split practical. We can work in parallel without constantly editing the same files, provided we agree on what crosses each boundary. If Web expects one response shape and the API returns another, it doesn’t help that the code lives in separate repositories. We have to agree on the contract and on what the data means.
We’re using Matt Pocock’s grilling, spec, and ticket workflow from the previous post on spec-driven development to do that planning. Grilling is where we challenge the idea together: what does confirmation mean, who owns a booking, and which changes need approval? Those decisions become the spec, then tickets with a clear scope, acceptance criteria, and dependencies. Both of us should understand the decisions before implementation starts.
Working with one ticket per agent context doesn’t mean only one ticket at a time for the whole project. To reach the demo, we need several tickets running in parallel. Once the shared event foundation is ready, offers, task generation, and seating can move forward independently. Web and CMS work can progress alongside the API wherever the agreed contracts and dependencies allow it. Each implementation stays focused on its own ticket, while we coordinate the work across them.
That only works if we keep talking to each other. We need to know who is working on what, flag a contract change before someone builds against the old version, and tell each other when a dependency is actually ready. A ticket being finished in one module doesn’t prove that the complete flow works. We need to connect the pieces regularly and check the customer journey together, well before demo day.
The milestones are a working demo on day twenty and hand-in on day thirty-five. For the demo, we want to follow an inquiry into an event, prepare an offer, generate tasks, and work with the seating plan. The remaining work includes the customer portal, AI assistance, and a full rehearsal of the process.
The measure I care about is whether Johan and his staff have less information to chase and copy. Can they check rooms and houses together? Can the kitchen get its table-by-table allergy overview? Can an inquiry become a usable plan without entering the same details again?
Those are the questions the build needs to answer. For me, the most exciting part is finding out whether we can take the problems Johan described in the interview and turn them into an application he can actually see himself using. I want to show him the demo and hear that it would help with something he and his staff deal with every day. That would make it more than an exam project to me.
This is part seven of my posts on this semester’s AI-driven applications elective. It describes the planned application at project start, based on our shared context and MVP development plan.
