Lalabase
티켓·문서·근무시간·고객대화를 한 곳에 모아 KI로 검색, 소프트웨어 기업 고객 프로젝트용, 오픈소스로 자체 서버·독일 클라우드에 배포
Team Calendar는 직원이 휴가와 근무 패턴을 한 곳에서 신청하고, Xero 급여와 공유 캘린더를 자동으로 최신 상태로 유지합니다
Team Calendar gives employees one easy place to request leave and working patterns, then keeps Xero Payroll and shared calendars current automatically.
비즈니스TypeScript · CSSGitHub @hiltonbrown온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Leave management and availability publishing platform for Xero Payroll. (AU, NZ, UK). Syncs leave, publishes ICS feeds.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
One accurate view of who is off, who is out, and who is in. Synced straight from Xero.README 전문 보기 ↗One accurate view of who is off, who is out, and who is in. Synced straight from Xero.
Team Calendar is the leave management and team availability platform for small businesses that run payroll in Xero. Staff request leave and flag working from home in one place; managers approve or decline, and approved leave writes back to Xero Payroll synchronously, so Xero stays the single source of truth for balances and accruals. Alongside approved leave, Team Calendar captures everyday out-of-office context (working from home, travelling, training, client site) and publishes one combined, privacy-controlled view to the calendars your team already uses: Outlook, Google Calendar, and Apple Calendar.
No more finding out someone is off when they do not show up. No more leave requested by text and lost in a thread. No more double entry between a calendar and your payroll system.
On a small team, leave admin holds together until it does not. Staff flag leave inconsistently: some use a form, some send a text, some just do not. Approved annual leave sits in payroll, while "working from home" and "on a client site" live in chat threads and ad hoc calendar invites. Managers approve leave in one place, then re-key it somewhere else. The result is guesswork about who is actually available, and a payroll system that drifts out of step with reality.
Team Calendar closes the gap:
Approved leave and manual entries are published through Team Calendar's feeds. Outlook, Google Calendar and Apple Calendar fetch updates on their own polling schedules, so changes may appear later in subscribed calendars.
Xero remains the source of truth for balances. Team Calendar never calculates accruals; it reads them from Xero and writes approved leave back synchronously.
| Direction | Mechanism | Scope |
|---|---|---|
| Inbound | Pull-first, scheduled Inngest jobs | Employees, leave records, leave balances. Xero provides no leave webhooks. |
| Outbound | Synchronous, user-triggered API write | AU submission stays local; approval creates scheduled leave. Imported requested leave uses approve/reject; supported remote withdrawal uses reject. No background queue. Failures surfaced inline. |
Outbound writes are synchronous; feed publication and cache rebuilds follow successful changes. Inbound Xero sync is pull-first and periodic, since Xero provides no leave webhooks. External calendar refresh timing is controlled by each calendar provider.
Xero consent requests exactly offline_access accounting.settings.read payroll.employees payroll.settings.read. One persisted full initial import covers people, leave and the complete balance roster. AU scheduled people/leave deltas use provider watermarks with a two-minute overlap; manual and nightly reconciliation are full reads. Balance polling remains provider-derived and rolling hourly.
Remote leave mutations reuse a durable UUID idempotency key for five minutes from first dispatch. Uncertain writes after that boundary require provider inspection and administrator recovery. Disconnect removes the selected remote connection before committing local teardown; failed or uncertain deletion preserves retryable state and sibling connections.
Xero calls use small shared, atomic Redis quota counters and expiring concurrency leases across serverless app, API and job workers. They enforce the five-request tenant concurrency limit and documented tenant/app quotas; lease expiry restores capacity after a worker crash. Keys initialise on first use, with no namespace bootstrap. Store failures deny calls as infrastructure failures; provider 429 responses remain separate and retain retry guidance even when their bodies are unreadable. Fixture namespaces isolate tests only.
The HTTP simplification follow-up records the retained-control requirements and current verification.
Team Calendar is built for the whole team, not only the people on the pay run. Employees on Xero Payroll get the two-way sync and leave balances. Contractors, directors, and advisors who never appear in payroll can still be added by hand, so their availability shows on the same calendar as everyone else, without touching a pay run. Every entry is labelled by where it came from: synced from Xero, or added manually.
Team Calendar is multi-tenant by design. A small business that grows into several payroll entities (each with its own Xero file) can run them under one Account, with strict isolation between entities and role-based access for owners, admins, managers, and viewers.
The tenancy boundary is the Clerk Organisation (the Account). Each payroll entity within it owns at most one Xero connection to an external payroll file. Canonical encrypted credentials belong to one verified Xero user/app authorisation, which may support several scoped connections. One Account maps to exactly one country code.
Team Calendar is a Turborepo monorepo built on modern serverless primitives:
@prisma/adapter-neonSlack and Teams notifications, HTML calendar views, and additional payroll connectors (MYOB, Zoho People, QuickBooks) are out of scope for the initial build. Team Calendar implements Xero directly. A future connector requires its own reviewed design; the current implementation does not introduce a multi-provider abstraction.
Team Calendar is under active development and pre-launch. Core infrastructure, Clerk multi-tenancy, the Prisma schema, and domain boundaries are established. Xero synchronisation, the leave submission and approval workflow with synchronous write-back, and the canonical ICS feed projection engine are implemented in their respective domain packages. Launch scope is AU-only, English-only, core loop. Current-candidate architecture, verification and review rulings are recorded in the final Xero audit and tasks/todo.md. Live Xero credentials are unavailable in this simplification session, so every live OAuth, import, write, refresh and disconnect journey remains NOT VERIFIED.
| URL | Description | Purpose |
|---|---|---|
https://app.teamcalendar.online/ | Authenticated product application. | Used by employees, managers, admins, and account owners to manage leave, manual availability, teams, feeds, Xero connections, reports, and account settings. |
https://api.teamcalendar.online/ | Public API service for server-side product operations. | Handles Xero OAuth callbacks, sync orchestration, secure ICS feed delivery, SSE notification streams, support submissions, health checks, cron routes, and Inngest handlers. |
https://teamcalendar.online/ | Public marketing website. | Explains Team Calendar to prospective customers, publishes product and integration information, and routes visitors into sign-in, sign-up, support, and documentation journeys. |
packageManager (also used by CI)enginesInstall dependencies:
bun install
Configure environment variables. Copy the example environment files for the apps you wish to run:
cp apps/api/.env.example apps/api/.env.local
cp apps/app/.env.example apps/app/.env.local
cp apps/web/.env.example apps/web/.env.local
Fill in DATABASE_URL, DATABASE_APP_URL, CLERK_SECRET_KEY, and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY as a minimum. app and api also need the same XERO_TOKEN_ENCRYPTION_KEY; generate one with openssl rand -base64 32. Copy packages/database/.env.example to packages/database/.env for migrations and seeding.
Optional GitHub support issue integration (apps/api/.env.local):
# Server-side only. Do not expose this token in the client.
GITHUB_TOKEN=github_pat_xxx
GITHUB_OWNER=your-github-owner
GITHUB_REPO=your-repository-name
GITHUB_TOKEN: the server-side GitHub token used by the API app to create support issuesGITHUB_OWNER: the repository owner or organisationGITHUB_REPO: the repository name without .gitIssues: write.Set up the database:
bun run migrate:deploy
This applies the committed migration chain. Use bun run migrate when developing a schema change; db:push is only for disposable development databases and is not release verification.
Seed development data (optional):
cd packages/database && bun run seed
This idempotently upserts a sample Australian Account: two payroll entities (organisations), their teams and locations, and a handful of people with a mix of Xero-sourced and manual records. Re-running creates no duplicates. The seed writes canonical data only; it never seeds Xero tokens or any other secret.
Team Calendar uses co-located tests and strict linting to maintain code quality:
bun run test
bunx vitest run packages/feeds
bun run check
bun run fix
Team Calendar deploys as three Vercel projects, one per deployable app. Each carries its own vercel.json. email is a development preview workspace. apps/docs retains the Mintlify starter and validation tooling; this repository does not establish a published documentation deployment. Customer help is implemented in apps/web at /help-centre.
| Vercel project | Root directory | Notes |
|---|---|---|
app | apps/app | Authenticated product UI |
api | apps/api | Xero OAuth, sync, feeds (/ical/:token.ics), SSE, Inngest handler. Runs a daily cron on /cron/keep-alive (see apps/api/vercel.json). |
web | apps/web | Public marketing site |
docs | no deployment configured | Retained Mintlify starter tooling |
email | not deployed | React Email dev preview only |
Set the Root Directory for each project to the relevant apps/* folder. Turborepo builds the dependent packages automatically.
Each project requires NEXT_PUBLIC_LAUNCH_MODE to be set explicitly to early_access or paid in production.
| Variable | Scope | early_access requirement | paid mode requirement |
|---|---|---|---|
NEXT_PUBLIC_LAUNCH_MODE | All apps | Required (early_access) | Required (paid) |
NEXT_PUBLIC_APP_URL | All apps | Required (URL) | Required (URL) |
NEXT_PUBLIC_WEB_URL | All apps | Required (URL) | Required (URL) |
NEXT_PUBLIC_API_URL | All apps | Required (URL) | Required (URL) |
NEXT_PUBLIC_SENTRY_DSN | All apps | Required (URL) | Required (URL) |
SENTRY_ORG / SENTRY_PROJECT / SENTRY_AUTH_TOKEN | All apps | Required for source-map upload | Required for source-map upload |
DATABASE_URL | app, api | Required | Required |
DATABASE_APP_URL | app, api | Required (restricted team_calendar_app URL) | Required (restricted team_calendar_app URL) |
XERO_TOKEN_ENCRYPTION_KEY | app, api | Required | Required |
XERO_CLIENT_ID / XERO_CLIENT_SECRET | app, api | Required | Required |
XERO_APP_TIER / XERO_REDIRECT_URI | app, api | Required commercial allowance / registered HTTPS callback | Required commercial allowance / registered HTTPS callback |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY / CLERK_SECRET_KEY | app, api | Required | Required |
CLERK_WEBHOOK_SECRET | api |
Before deploying any application, run the production preflight check:
bun run preflight app
bun run preflight api
bun run preflight web
The rows marked required in the matrix above are the canonical production
minimum enforced by runProductionPreflight. Copy each app's .env.example
for the full, annotated list. Optional variables that carry a format constraint
(a URL, an email, or a required prefix) are commented out in the examples: an
empty string fails validation, so leave them absent rather than set to "".
Run each command after securely pulling that project's production environment.
The optional mode argument is an assertion against NEXT_PUBLIC_LAUNCH_MODE;
it never supplies or overrides the deployed value. Run each project separately,
rather than combining all three projects' environments.
The API requires the runtime's RESEND_TOKEN name, a valid RESEND_FROM, and a
private EARLY_ACCESS_APPLICATION_RECIPIENT. Required credential pairs must be complete: setting only one of
KV_REST_API_URL/KV_REST_API_TOKEN or
INNGEST_EVENT_KEY/INNGEST_SIGNING_KEY fails preflight.
GitHub-backed support settings remain optional and API-only. The web project
does not need database, Clerk, Xero, Inngest, or KV variables. To publish live
service health, configure the optional Better Stack API key, status-page ID,
and HTTPS public status-page URL together. The Better Stack page must contain
exactly five public resources named App access, Xero connection and synchronisation, Calendar feed delivery, In-app notifications, and Email notifications. If the trio or a required resource is absent, /status reports
Unknown rather than assuming the service is operational.
For versioned encrypted envelopes, configure XERO_TOKEN_ENCRYPTION_ACTIVE_VERSION and the server-only XERO_TOKEN_ENCRYPTION_KEYS_JSON key map as specified in each app's example. Preserve every key still referenced by canonical authorisations. Never print the key map.
XERO_TOKEN_ENCRYPTION_KEY (32 bytes, base64-encoded) is validated on startup in packages/xero. An absent or malformed key prevents the application from starting rather than failing later at token access time.
bun run test:release runs the Playwright release journeys against three
explicit HTTPS candidate deployments. It requires TC_APP_CANDIDATE_URL,
TC_API_CANDIDATE_URL, TC_WEB_CANDIDATE_URL,
TC_DEPLOYED_CANDIDATE_SHA, and the D1 TC_RELEASE_MANIFEST. The manifest,
deployed SHA assertion, and local checkout must identify the same commit.
Provide Clerk test-instance keys plus TC_E2E_OWNER_EMAIL,
TC_E2E_ADMIN_EMAIL, TC_E2E_MANAGER_EMAIL, and TC_E2E_VIEWER_EMAIL for
isolated role sessions. Controlled fixture inputs are
TC_E2E_APPROVE_EMPLOYEE_NAME, TC_E2E_DECLINE_EMPLOYEE_NAME,
TC_E2E_FOREIGN_PERSON_ID, TC_E2E_FEED_ID,
TC_E2E_RECOVERY_PERSON_ID, TC_E2E_RETRY_PERSON_ID, and
TC_E2E_CALENDAR_EVENT_LABEL. Missing inputs fail the mandatory journey rather
than skipping it. The suite runs cleanup from the protected manifest even after
test failure and fails if owned residue remains.
Use Clerk development/test credentials and controlled Xero organisations only. The suite does not mock authentication or provider success.
PLAYWRIGHT_BROWSERS_PATH=/tmp/teamcalendar-playwright bun run test:release
If you enable GitHub-backed support submissions in deployed environments, configure GITHUB_TOKEN, GITHUB_OWNER, and GITHUB_REPO on the api project only. These values are server-side and must not be added to the app or web deployments. Set them in both Preview and Production if the feature should work in both, and redeploy the API app after changing them.
Xero requires every OAuth redirect URI to be pre-registered on the Xero app, and preview deployments get a fresh, unregistered URL. Team Calendar therefore registers a single production callback and disables Xero connect on preview deployments:
https://<your-api-domain>/api/xero/oauth/callback as the redirect URI on the Xero app and set XERO_REDIRECT_URI to that exact URL on the api (and app) projects for every environment. If XERO_REDIRECT_URI is unset, the callback is derived from NEXT_PUBLIC_API_URL instead.VERCEL_ENV=preview) the Xero connect flow and the callback route are gated off and return a clear message. Connect Xero from the production deployment.The public application, private-mailbox retention rule, and separate Clerk owner-admission runbook are documented in docs/early-access-admission.md. The API requires EARLY_ACCESS_APPLICATION_RECIPIENT and a private EARLY_ACCESS_APPLICATION_HMAC_SECRET of at least 32 characters, plus the configured KV pair used for abuse controls and retry receipts.
Every seeded row is scoped to a clerk_org_id. By default this is the
placeholder org_dev_teamcalendar. For the authenticated app to render the
seeded tenant, the rows must be scoped to a real Clerk Organisation id.
Set SEED_CLERK_ORG_ID to your Clerk org id (visible in the Clerk dashboard,
in the form org_...) before seeding:
SEED_CLERK_ORG_ID=org_yourrealid bun run seed
DATABASE_URL must be available, either in packages/database/.env or the
shell environment.
Start the development servers:
bun run dev
This starts all applications through Turbo and the local Inngest Dev Server.
Inngest discovers the API handler at http://localhost:3002/api/inngest and
exposes its local dashboard at http://localhost:8288. Keep
INNGEST_DEV="1" in apps/api/.env.local so locally dispatched sync events
are sent to that server instead of Inngest Cloud.
| Required |
| Required |
KV_REST_API_URL / KV_REST_API_TOKEN | app, api | Required pair | Required pair |
INNGEST_EVENT_KEY / INNGEST_SIGNING_KEY | api | Required pair | Required pair |
RESEND_TOKEN / RESEND_FROM | api | Required | Required |
EARLY_ACCESS_APPLICATION_RECIPIENT | api | Required private mailbox | Required private mailbox |
BETTERSTACK_API_KEY / BETTERSTACK_STATUS_PAGE_ID / BETTERSTACK_STATUS_PAGE_URL | web | Optional complete trio (status is Unknown when absent) | Optional complete trio (status is Unknown when absent) |
STRIPE_SECRET_KEY | app, api | Optional (disabled) | Required |
STRIPE_WEBHOOK_SECRET | api | Optional (disabled) | Required |
STRIPE_PRICE_BASIC / STRIPE_PRICE_PREMIUM | app, api | Optional (disabled) | Required |
STRIPE_PORTAL_RETURN_URL | app, api | Optional (disabled) | Required |