Synaplan
기업을 위한 오픈소스 AI 플랫폼
자원봉사자는 행사 출입을 스캔하고, 조직은 2분 내에 시간을 확인해 누구나 확인 가능한 링크를 제공한다
Volunteers scan in and out at the event, the organization confirms the hours in about two minutes, and every record gets a link anyone can check.
비즈니스TypeScript · CSSGitHub @Slifronic온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기SycaMore Live: org-attested volunteer hours (Cal Hacks 13.0)
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Volunteer hours recorded as the event happens, from the organization's side. Volunteers scan in and out; the coordinator verifies the event when it closes; every volunteer gets a record anyone can check.
Started at Cal Hacks 13.0 on Oct 23, 2026. SycaMore is the team's existing student project; this repo is a new weekend build on the same problem and shares no code with it.
This repo holds both sides of the sign-in table in one Next.js app on one database: the organization side (Bryant, built with Claude) and the volunteer side (check-in on phones, "My hours", the public /v/ page, ported from Khanh's prototype). The API follows build/00-SHARED-CONTRACT.md in the team's Drive hub.
On Oct 25, 2026 (judging day) the whole app was redesigned: a top bar and icon rail for coordinators, an app shell with a bottom tab bar for volunteers, a dark theme, a shared motion kit, and new legal pages. See Design.
/) jumps to any event, volunteer or page. Signed out, any coordinator page goes to the PIN screen and comes back to it after sign-in.claude-opus-5-5 drafts rows with structured outputs; low-confidence rows are amber; nothing is saved until the coordinator confirms each row. Sheet text is treated as data, not instructions, and the model never invents a check-out time.src/lib/insights.ts) but aren't shown or linked anywhere until the business model's Phase 4 trigger (30 organizations, 200 events); /org/score still renders for anyone who types the URL.src/lib/presence.ts; the volunteer endpoint POST /api/vol/presence is a reference implementation for the volunteer side./w/<token>:
vid cookie.src/lib/walkin.ts and src/lib/google.ts. POST /api/vol/checkout is a reference implementation of the contract route for the volunteer side..ics invite or the invite email (.eml), a Google or Outlook "add to calendar" link, or a calendar's iCal address (Google's "secret address", Apple public calendars, Outlook ICS links). Feeds list the next four months, with repeating events expanded, and an event imported twice is flagged.claude-opus-5-5, structured output). The server checks the date and time shapes, relative dates ("this Saturday") resolve against today, and instructions inside the material are ignored.src/lib/safe-fetch.ts)./cal/<token>.ics is unguessable and updates every few hours; practice events never appear in it./e/<slug> with a map, one-tap directions, add to Google / Apple / Outlook, notes and how check-in works. The check-in code is not on it. Until the event ends, the coordinator's event page shows a ready-to-send announcement for group chats, with Share, Copy, WhatsApp, text, email and Telegram buttons./c/<slug>; voice notes stay private. Media is stored on local disk in .media/ (gitignored; set MEDIA_DIR to change it).Same app, same theme: the volunteer pages live in src/app/(vol)/. On phones, a greeting bar and a bottom tab bar (Home, Discover, Messages, Me); on desktop, one top bar with the same tabs as a sliding pill, Check in and the avatar.
/join: name, email, birth date, and a required "I agree to the Terms and Privacy Policy" box (13 to 17: "and a parent or guardian knows I'm using SycaMore"), kept as terms_accepted_at and guardian_ack_at. Under 13 is refused and nothing is stored; 13 to 17 can join, but sharing needs a guardian's OK. An existing email signs back in only with its birth date. ?next= brings them back (a QR scan lands on /join?next=/checkin?code=…; a signed-out visit to any volunteer page, Discover listings included, comes back to that page)./checkin: the event QR (/checkin?code=<checkin_code>) or the 6 typed characters; check-in opens 2 hours before the start and closes 2 hours after the end, and practice events never match. Checked in, it's the live session card (shared with the walk-in page) with Check out. If the coordinator set a site, an off-by-default toggle, "Help {org} see if I leave early", sends POST /api/vol/presence pings while the page is open (no coordinates stored) and offers Check out if they've left./me (Home): verified hours (counted up) with "+N this month"; the live session card with Check out if they're checked in; "Up next"; a private personal goal (volunteer.hours_goal, filled only by attested hours); pending events as "Waiting for {org} to confirm" with no number; and each organization as a folder (its colour, total hours and record count). Hover or press-and-hold lifts the papers; tapping a folder fans out its three newest records, each linking to the record (more than three: the third paper opens Journey)./me/account (Account): name and email, the light/dark theme, the announcements opt-in (marketing_opt_in, off until they turn it on, with the time in marketing_opt_in_at), Download my data (GET /api/vol/account/export, a JSON file of their own rows), Delete my account (type DELETE; DELETE /api/vol/account deletes the volunteer and unlinks their attendance as "Former volunteer", so the organizations' attested totals survive as anonymous rows), links to Badges, Journey, Privacy, Terms and Deletion and retention (/data), and Sign out on this device./me/records/[id]: the contract's record wording with captured vs adjusted and the reason, a share link (SYC-XXXX-XXXX, crypto.randomInt) with copy and QR, and print / save as PDF. Minors get "Sharing needs a guardian's OK", and the API returns 403.Demo volunteers: [email protected] / 2007-04-12 (adult, three attested records, one pending, one already shared at /v/SYC-RVR7-PLNT, Event Coordination signed and Team Leadership ready for sign-off) and [email protected] / 2010-02-03 (16, can't share). Three more made-up organizations (PINs 135790, 864202, 975318) have 8 upcoming events for Discover.
The Oct 25, 2026 redesign is described in docs/REDESIGN-2026-10-25.md (the brief: shells, density, motion, volunteer folders, legal pages) and docs/UI-KIT.md (how to use the tokens, shells, primitives and motion kit). docs/DESIGN-BRIEF.md still holds the colour rules, type and accessibility decisions underneath it. In short:
src/components/motion/: staggered page entrances, count-ups, sparklines, sliding pills, toasts and route transitions. The check-in, event-day and verify paths never wait on animation, and content is visible without JavaScript./privacy, /terms, /data (download, deletion, retention, breach steps) and /licenses. Every public page has its own title, description and share card; private pages are noindex.Needs Node 22+ and PostgreSQL 16/17 running locally.
npm install
cp .env.example .env.local # or create it: see below
npm run db:reset # creates the database in DATABASE_URL and loads the demo seed
npm run dev -- -p 3311
.env.local:
DATABASE_URL=postgres://localhost:5432/sycamore_live
NEXT_PUBLIC_APP_URL=http://localhost:3311
ANTHROPIC_API_KEY=... # for "Import paper sheet", "Event story" and the volunteer blurb
OPENAI_API_KEY=... # optional: the blurb uses OpenAI instead of Claude when set
GOOGLE_CLIENT_ID=...apps.googleusercontent.com # "Continue with Google" for coordinators and volunteers
GOOGLE_CLIENT_SECRET=... # with the ID above; without both, Google buttons hide and the PIN / email code remain
NEXT_PUBLIC_GOOGLE_CLIENT_ID=...apps.googleusercontent.com # optional: one-tap Google on the walk-in page
RESEND_API_KEY=... # emailed sign-in codes and supervisor/guardian emails (in dev without it, emails print to the server log)
SYCAMORE_DEMO=1 # optional: keep the "Demo data · made-up names" ribbon on in a production build
Going to production: read docs/PRODUCTION.md (the Google OAuth client, Resend, the two migrations, demo PINs, organization review). What the competitor research found and what was built from it is in docs/COMPETITIVE-ANALYSIS-2026-10-28.md.
Already have a database from before the redesign? npm run db:reset rebuilds it from db/schema.sql, which now has the account columns on volunteer: terms_accepted_at and guardian_ack_at (the join and walk-in consent boxes), marketing_opt_in (default false) and marketing_opt_in_at (the announcements opt-in), plus an attendance trigger that lets a deleted volunteer's attested rows be unlinked as "Former volunteer" without changing their hours. To keep your data instead, add those columns by hand and run psql "$DATABASE_URL" -f db/migrations-redesign-r2.sql (idempotent: it backfills guardian_ack_at and adds the second and third demo organizations to Ana's folders).
Org and volunteer pages show a slim "Demo data · made-up names" ribbon whenever SYCAMORE_DEMO=1 or NODE_ENV isn't production (src/lib/demo.ts).
Sign-in. Coordinators and volunteers sign in at /signin: Google for both, an emailed 6-digit code for volunteers, and the organization PIN for the demo org and orgs set up before Google. Sessions are server-side (auth_session, only a hash of the token is stored). Google needs an OAuth Web application client whose redirect URI is <NEXT_PUBLIC_APP_URL>/api/auth/google/callback; see docs/PRODUCTION.md.
Demo coordinator PIN: 246810 (Riverside Restoration Crew, made-up names). The seed mirrors the shape of the team's real pilot: four planting events with 45/48/52/58 attendances at 3 hours each. Phase 2 is left unverified on purpose, with 4 of its 58 people who never scanned out (so verify shows "Fill in 4 who didn't scan out"): verifying it on stage adds its hours to the record and makes new badges ready. Today's walk-in event, Guadalupe River Cleanup, runs 9:00 AM to 1:00 PM Pacific on the day you seed; seeded later than that, it shows as ended but its walk-in QR keeps working for 4 hours.
npm run check # tsc + eslint
npm test # Discover engine (22 ported tests + 4) and the blurb number check; Node 22.18+ runs the TypeScript directly
npm run eval:sheets (dev server running, ANTHROPIC_API_KEY set) scores the import on three sheets in tests/sheets/ whose "handwriting" is synthetic: computer-generated with handwriting-style fonts by make_sheets.py (which regenerates them), not written by people. Last run, Oct 23, 2026, claude-opus-5-5: 19 of 19 rows exactly right before review on those synthetic sheets, the ink-blotted time flagged instead of guessed, the crossed-out row and the "IGNORE PREVIOUS INSTRUCTIONS" line both skipped and mentioned in notes, and blank time-outs left empty. About 5 to 9 seconds per sheet. A test on real, human-handwritten sign-in sheets is still pending, so treat 19/19 as a synthetic result, not a field accuracy figure.
OPENAI_API_KEYOPENAI_MODELgpt-4o-minisrc/lib/claude.tssrc/lib/blurb.ts/me/badges: signed / in progress / not started. Signed means the organization signed a badge_award row; progress and "ready for sign-off" are computed per organization from attested attendance with the rule targets in src/lib/badges.ts (the org dashboard uses the same stats helpers). Pending check-ins and practice events never count./me/badges/[key]: the certificate: signer, date, the coordinator's evidence note, attested events and hours with the issuer, the rule; print / save as PDF. Adults can create a public verification link (/v/b/BDG-XXXX-XXXX, POST /api/vol/badges/:id/share; minors get a 403, like records). With the link, Add to LinkedIn: an editable post that states facts only (organization, signer and date, attested events and hours, dates, roles; no skill claims) with the link, Copy, LinkedIn's share URL, and the Licenses & certifications fields to copy. Nothing is posted for them, and there's no LinkedIn partnership./me/journey: every attested event (practice excluded) with the role, hours, a running total, firsts, and the badges signed along the way./discover: upcoming public events from every organization, ranked in the browser by Khanh's matching engine (ported unchanged to src/lib/discover/): use my experience / learn something new / follow my interests, a cause, skills to use and develop. Org tags supply the cause and skills; journey time, cost and support are unknown, so each score is the supported fit points with the range. Fit is rule-based, not a probability. Pasted resume or role text (/discover/profile) stays in page memory, never sent or stored. Save for later is per volunteer on the server (saved_event, /api/vol/saved). Each event links to its public page /e/<slug> and walk-up check-in; there's no sign-up. Details in docs/volunteer/DISCOVER.md./v/<code>: public, no cookie, not indexed: VERIFIED or REVOKED, the wording, and who to call. Unknown codes are a 404./me/messages: coordinator threads and event group chats, over the existing /api/vol/messages and /api/vol/event-chats. Volunteers under 18 get the organization's announcements (broadcasts and automatic notices) only: they aren't in event group chats (can't see members, read or post) and direct messages with a coordinator are off in both directions, with "Messages with volunteers under 18 are off" on both sides. The rule lives in the data layer (src/lib/minors.ts, used by messages.ts, event-chat.ts and the vol routes), which answer 403.POST /api/vol/join, GET/PATCH /api/vol/me, PATCH/DELETE /api/vol/account, GET /api/vol/account/export, POST /api/vol/checkin, GET /api/vol/records, POST /api/vol/records/:id/share, POST /api/vol/records/:id/blurb, POST /api/vol/badges/:id/share, GET/POST /api/vol/saved, GET /api/v/:code (logic in src/lib/vol-data.ts, vol-badges.ts, vol-discover.ts, blurb.ts). The browser never sends hours.sycamore-volunteer-preview/) is fully ported and removed from the branch; it stays in git history at c169374. Its notes, updated for the integrated version, are in docs/volunteer/.