Imported from ronanrodrigo/agenda-assis (
AGENTS.md). Install upstream withnpx skills add ronanrodrigo/agenda-assis. Copyright stays with the author.
AGENTS.md
Custom campaign-agenda kiosk. A Next.js (App Router) app that reads a public Google Calendar via the Calendar API through a serverless proxy (API key kept server-side), and renders a themed agenda page. Replaces the old static index.html iframe embed.
What this repo is
- App (Next.js 14, TypeScript strict, skillfold architecture):
src/app/page.tsx— kiosk page (client). Fetches/api/calendarand renders: top "Hoje" band grouped by Manhã/Tarde/Noite, left column = current month (from today onward, one card per day), right column = next month. On mobile (<768px) hides the right column + "Hoje" band and shows a full-width "Abrir no Google Agenda" button.src/app/api/calendar/route.ts— serverless proxy. Reads env, calls the service, returnsAgendaResponse. API key never reaches the client.src/app/globals.css— design tokens (bege/cream neutral palette, single accent--accentused only on the hero; gold star). Mobile + kiosk layouts.src/app/layout.tsx— root layout, loadsglobals.css.
- Domain / application / infrastructure (skillfold layers):
src/domain/calendar/event.ts— pure types/transforms.src/application/gateways/calendar-gateway.ts— capability port (CalendarGateway).src/application/services/calendar-service.ts— use-case logic (grouping, month split, period split).src/application/app-container.ts— composition root (wires adapter + env).src/infrastructure/google/google-calendar-gateway.ts— Google Calendar API adapter (holds fetch + key).src/infrastructure/sample/in-memory-calendar-gateway.ts— deterministic adapter for tests.src/interface-adapters/http/calendar-response.ts—AgendaResponsecontract (today,currentMonth,nextMonth).
agenda-prototype.html— standalone static prototype (self-contained fixtures) used to iterate on the visual design before touching the Next.js app. Open in the preview pane to react to changes; port approved changes intopage.tsx/globals.css.index.html— legacy redirector served by GitHub Pages atronanrodrigo.dev/agenda-assis. It only redirects to the live site (https://assis.ronanrodrigo.dev). Do not put app logic here. The canonical project now lives in the private repoassis; this public repo stays only as the GitHub Pages redirector.kiosk.sh— Linux fullscreen launcher. Not runnable on macOS (usesxset,unclutter,xdotool,chromium-browser). Only relevant to the physical kiosk box..freebuff/— gitignored tool state. Never commit it..hermes/plans/— implementation plans..hermes/plans/STATUS.mdtracks each plan's status (✅ done / 🟡 partial / ⬜ pending / ❌ obsoleto) with commit/PR/deploy evidence. Check it before starting or claiming any planned work — it is the source of truth for what's already built vs. pending..hermes/LESSONS.md— learned lessons / footguns already hit in this repo (e.g. the America/Sao_Paulo timezone bug). Read it before touching date/time logic, the calendar fetch, or deploy.
Architecture (skillfold)
Dependency direction points inward — domain depends on nothing; outer layers may import inner ones, never the reverse.
flowchart TD
subgraph app["app/transport"]
page["page.tsx<br/>(kiosk UI)"]
route["api/calendar/route.ts<br/>(serverless proxy)"]
end
subgraph iface["interface-adapters"]
resp["http/calendar-response.ts<br/>(AgendaResponse)"]
end
subgraph app_layer["application"]
svc["services/calendar-service.ts<br/>(BuildAgendaService)"]
gw["gateways/calendar-gateway.ts<br/>(CalendarGateway port)"]
ioc["app-container.ts<br/>(composition root)"]
end
subgraph domain["domain"]
event["calendar/event.ts<br/>(types + sp* TZ helpers)"]
end
subgraph infra["infrastructure"]
gcal["google/google-calendar-gateway.ts<br/>(fetch + API key)"]
mem["sample/in-memory-calendar-gateway.ts<br/>(tests)"]
end
page -->|fetch /api/calendar| route
route --> resp
route --> svc
svc --> gw
svc --> event
ioc -->|wires| gcal
ioc -->|wires| svc
gw -.implemented by.-> gcal
gw -.implemented by.-> mem
gcal -->|"Google Calendar API<br/>(timeZone=America/Sao_Paulo)"| cal[("Google Calendar<br/>assis.capim@gmail.com")]
classDef inward fill:#f4f1ea,stroke:#8c2f2c,color:#2b2118;
classDef secret fill:#fff4d6,stroke:#8c2f2c,color:#2b2118;
class event,gw,svc,resp,page,route inward;
class gcal,cal secret;
Rules:
domainhas no framework/provider/infra imports.application/gatewaysare capability ports (interfaces), named by capability not vendor.infrastructure/googleis the only place that holds the API key + fetch.- Secrets live only in environment config (Vercel env /
.env.local), never in source or client.
Gotchas an agent would likely miss
- Calendar data lives in Google Calendar, not here. The repo only renders what the API returns. Event edits happen in the calendar itself.
- Public calendar + read-only ⇒ any GCP API key works. No OAuth / owner token needed. The key is
GOOGLE_CALENDAR_API_KEY(restrict it to the Calendar API in GCP).GOOGLE_CALENDAR_ID = assis.capim@gmail.com. - API key is server-side only.
src/app/api/calendar/route.tsreads env and proxies; the client never sees the key. Do not move the fetch to the client or hardcode the key. index.htmlis NOT the app. It's a GitHub Pages redirect to the Vercel site. Editing it does not change the agenda — changesrc/app/page.tsxinstead.- Current month column shows only from today onward (
page.tsxfilterse.day >= new Date().getDate()). Past days are intentionally dropped. - Day card header:
DD/Mêsleft,Dia da Semanaright (same size/weight; weekday softened withvar(--muted)), separated byspace-between. No hyphen. - No CI on git push for the app itself beyond Vercel's build. Vercel auto-deploys preview on PR and production on push to
main. GitHub Pages rebuilds frommainfor the redirector only. .env.localis gitignored (holds the real key)..env.exampleis the template. Never commit.env.local.- Vercel Deployment Protection was disabled on this project (public kiosk). If you re-enable it, the live API/page will 403 for unauthenticated visitors.
/api/calendarcaches server-side via the adapter'smaxDurationwindow; the page also refreshes every 5 min client-side.- Timezone is
America/Sao_Paulo— never rely on the runtime (UTC) clock. All "today" / day-bucketing / time formatting must go through thesp*helpers inevent.tsand the API must be called withtimeZone=America/Sao_Paulo. Usingnew Date().getDate()/getMonth()/toLocaleTimeString()(runtime-local) silently shifts "Hoje" to the wrong civil day and times ~3h off on Vercel. Details + how to avoid:.hermes/LESSONS.md.
Verification
npm install
npm run verify:pr # lint (incl. skillfold layer guard) + typecheck + vitest
npm run dev # local dev (needs .env.local with GOOGLE_CALENDAR_API_KEY + GOOGLE_CALENDAR_ID)
- Tests:
vitest(domain + service; 14 tests). Adapter is covered indirectly; in-memory adapter used in unit tests (no network). - Typecheck:
tsc --noEmit(strict). - Layer guard: ESLint
no-restricted-importspreventsdomain/applicationfrom importing infra/provider code.
Deploy
- Vercel is the real host (project
rohones/agenda-assis), now served under the canonical domainhttps://assis.ronanrodrigo.dev.- Env vars (Secret):
GOOGLE_CALENDAR_API_KEY,GOOGLE_CALENDAR_ID. vercel deploy→ preview;vercel deploy --prod→ production. Pushingmainalso triggers a production build.
- Env vars (Secret):
- GitHub Pages serves only
index.html(redirect to the canonical site) atronanrodrigo.dev/agenda-assis. Keep the redirect pointing at the canonical domainhttps://assis.ronanrodrigo.dev(not the per-deploy hash URL, nor the oldagenda-assis.vercel.app).
Style conventions
- UI text is pt-BR. Chrome/Chromium kiosk app name is "Agenda Campanha".
- Neutral bege/cream palette (warm paper tones), single accent
--accent:#8c2f2cused only on the hero. Gold star#f2c200. No campaign-red branding elsewhere, no gradients, no glass. - Cohesive type scale via CSS vars (
--fs-*/--w-*). Day card = one card per day with a clean internal list (not one card per event). - Kiosk layout is designed for a vertical monitor; large-screen default = top "Hoje" band (~20% height, full width) + left/right month columns (50/50). Mobile = current month only + button.