30 Commits
Author SHA1 Message Date
Jules bc30c4cede refactor(civi-crm/web): drop CiviCRM integration
* refactor(civi-crm): remove case management pages and API

The Circle Case UI, its API routes and the CiviCRM query layer they used
are gone. Keeps the app shell out entirely: no pages remain, so the
layout, styles and UI dependencies go with them.

* refactor(civi-crm): drop CiviCRM write from the intake endpoint

Submissions now go to Notion and, for the steward form, n8n. The legacy
afformCircleContactForm name was CiviCRM-only and is no longer accepted;
the fields[] payload is ignored.

* chore(web): remove CiviCRM afform generator scripts

They fetched form definitions from the CiviCRM API, which is gone. The
generated files under lib/civicrm are hand-maintained from now on.

* refactor(web): trim funnel form definitions and move to lib/funnel-forms

Fields keep only what renders the form: formKey, label, inputType,
required, repeatable. The CiviCRM entity/join/fieldName plumbing and the
fields[] POST payload are gone, along with the unused circle contact
form.

country was marked required but exempt from validation through its
CiviCRM Address join, so it is now enforced like every other required
select.

* docs: rewrite civi-crm and funnel docs for the intake-only app

Drops the CiviCRM API guide and the case management architecture, and
updates the funnel reference to the Notion + n8n flow.
2026-08-13 22:44:23 +09:00
jinhojang6 cc4c287393 fix: address logos crm review feedback 2026-08-10 20:41:36 +09:00
jinhojang6 b3602a0801 fix: improve CRM navigation and usability 2026-08-10 20:41:36 +09:00
jinhojang6 9f2e79f689 feat: add Logos CRM demo 2026-08-10 20:41:36 +09:00
Jules a7e5eedf35 feat: add "How did you first hear about Logos?" field to funnel forms
* feat: add @repo/funnel package for shared hear-about constants

Single source of truth for the "How did you first hear about Logos?"
question, options, and id -> label map used by apps/web and apps/civi-crm.

* feat: map hear-about answer to a Notion select on intake

Unknown option ids are dropped so tampering cannot create new select
options; field defs without a CiviCRM fieldName are skipped in
buildAfformValues so the Afform payload is unchanged.

* feat: add required hear-about field to the three funnel forms

Injected below the chat name/service row on coalition-partner,
activist-builder and activist-leader-steward. Web/Notion-only: defs
without a fieldName are filtered out of the CiviCRM fields payload.

* docs: document hear-about field in funnel reference

* refactor: extract resolveHearAboutLabel from Notion builder

Share the option-id -> label resolution so the route can reuse the
builder's exact accept/write predicate.

* fix: enforce required hear-about field server-side

Reject funnel submissions whose hear-about answer is missing or not a
known option id, instead of relying on client-side validation alone.
2026-07-08 19:39:42 +09:00
Jinho Jang 96be50d8d0 refactor: make @repo/content the single source for home, get-started & movement page copy (#62)
* docs: add page-copy single-source spec and implementation plan

* feat(content): add typed home section schemas and migrate home copy into content

Add 7 bespoke home* section schemas (+ techStack ctas) to the page section
union and populate content/pages/en/home.json with home copy moved verbatim
from messages.

* refactor(web): source home page copy from @repo/content; drop home namespace from messages

Feed every home section from content via findSection/data props (replacing
next-intl getTranslations), remove the home namespace from messages/en.json,
and add a regression guard plus content-based tests. Render output unchanged.

* docs: add get-started/movement page-copy dedup spec and plan

* feat(content): add get-started & movement section schemas and content docs

Move get-started/movement page copy verbatim into content/pages so it is
single-sourced in @repo/content.

* refactor(web): source get-started/movement copy from content; drop those namespaces from messages

Feed both pages from content via findSection/data props (replacing next-intl),
switch them to content-based metadata, delete pages.getStarted/movement from
messages, and add content-based tests + ownership guard. Render unchanged.

* docs: add navbar-pages CMS copy spec and plan

* feat(content): add navbar page section schemas + Pages docs (book, brand-kit, research, node-programme, lambda-prize, manifesto, media, podcast, broadcast, tech-stack explorer)

* refactor(web): source navbar pages copy from content; drop those namespaces from messages

Migrate book, brand-kit, research (incl. rich-text via a tagged-text renderer),
node-programme, lambda-prize, manifesto, media, podcast, logos-broadcast-network,
and the tech-stack explorer to read copy from content via findSection/data props
and content-based metadata; remove the migrated pages.* namespaces from messages;
update guards + contracts. Render output unchanged.

* feat(web): source activist/coalition pages copy from content (CMS title/description)

Migrate activistBuilder, activistLeaderSteward, and coalitionPartner page
metadata from next-intl messages.pages.* into @repo/content PageCopy JSON,
following the established bookCopy pattern.

- Add activistBuilderCopySectionSchema, activistLeaderStewardCopySectionSchema,
  coalitionPartnerCopySectionSchema to packages/content/src/schemas/pages.ts
  and wire them into the pageSectionSchema discriminated union
- Create content/pages/en/activist-builder.json,
  activist-leader-steward.json, coalition-partner.json with verbatim
  title/description/heading values from the old messages namespace
- Switch all three route pages from createTranslatedPageMetadata to
  createPageMetadata(ROUTE), removing the NAMESPACE constant
- Remove activistBuilder, activistLeaderSteward, coalitionPartner from
  apps/web/messages/en.json pages object
- Add schema parse tests, renderToStaticMarkup page tests, messages ownership
  guards, and content-route contract entries for all three pages

* chore(web): remove dead faq (pages.faq + unused FaqSection components)

* fix: remove stale faq links

* fix: pass payload env through turbo build

* fix: preserve lambda prize support labels

* fix: preserve live copy after master rebase

* fix: address page copy review feedback

* refactor: move design guide copy to content

* feat: add schemas for home and page copy sections

- Introduced home section schemas including homeSocialProof, homeChoosePath, homeDecide, homeStartBuilding, homeAbout, homeUseCases, and homeBuilderPortal.
- Added page copy section schemas such as getStartedCopy, movementCopy, bookCopy, designGuideCopy, activistBuilderCopy, activistLeaderStewardCopy, coalitionPartnerCopy, researchCopy, nodeProgrammeCopy, lambdaPrizeCopy, mediaCopy, podcastCopy, broadcastCopy, and manifestoCopy.
- Created shared schema for section keys to maintain consistency across sections.

* chore(web): remove dead pages.faq from messages

* fix: resolve page copy migration conflicts

* fix: source connect form page copy from content

* fix: remove duplicated afform page copy exports

* fix: stop generating afform page copy exports

* fix(cms): require only server actions key

* fix(cms): load env files before build validation

* fix(cms): make env loader available during build

* fix(cms): load next env via require

* test(cms): isolate missing env validation

* feat: add homepage testnet highlight

* fix: restore lambda prize support links
2026-07-06 19:09:02 +09:00
jinhojang6 a45401568e feat(field-guide): match reference site layout, fonts, and dark mode
Port the reference guide's chrome verbatim so /field-guide is visually
identical to logos-brand-protocol.vercel.app, including dark mode.

- field-guide.css: reference tokens + styles scoped under .fg-shell,
  with the Paper/Ink dark theme on .fg-shell[data-theme="dark"]
- FieldGuideShell: reference DOM (lambda header, page reference, theme
  toggle, GitHub, print, chapter sidebar, prev/next pager, mobile menu)
- render chapter Markdown into .fg-article via rehype-raw so headerless
  tables, <kbd>, and <strong> render faithfully
- hide the global site header/footer on /field-guide so the guide owns
  the full viewport (SiteHeaderGate + new SiteFooterGate)
- Public Sans / Fira Code via next/font; Times serif headings
2026-06-30 02:29:00 +09:00
Jinho Jang 97589cb5c5 Update Builders Hub, Get Started, legal pages, and RFP sourcing (#36)
* fix(sitemap): remove FAQ route from static indexable routes

* feat(web): markdown-managed legal pages + Testnet FAQ & Terms

Introduce a markdown-based system for legal/info documents and add the
two testnet pages on the same docs route pattern.

- Add getLegalDoc loader (gray-matter frontmatter + zod validation) and
  LegalMarkdown renderer (react-markdown + remark-gfm) mapped to Logos
  design tokens; render legal pages as a server component.
- Refactor docs-page-factory to source content from content/legal/<slug>.md
  instead of next-intl namespaces; migrate terms, privacy, and security
  from messages/en.json to markdown.
- Add /testnet-v01-faqs and /testnet-terms-and-conditions pages with
  content mirrored from logos.co; link the FAQ's testnet-terms references
  to the internal route.
- Add Testnet FAQ and Testnet Terms to the docs ToC (remove the dead FAQ
  entry); register both routes in the sitemap.
- Align docs content to the standard page gutter by removing the
  redundant px-3 on the docs page shell.

* chore: optimise page imagery

* feat(web): link Logos Devs X account in community section

* chore(web): hide 'What you can build today' section on get-started and builders-hub

* refactor: reuse builders hub start sections

* feat(builders-hub): source RFPs from GitHub API at build time

Fetch and parse RFPs from the logos-co/rfp repo (markdown + YAML
frontmatter) at build time instead of local content, so the /rfps
listing and [slug] detail pages render from a single live source.

- add rfps-github lib: GitHub contents fetch, gray-matter frontmatter
  parse, stable slug from filename, draft/template exclusion
- detail page renders frontmatter-stripped markdown via LegalMarkdown,
  with Apply + View on GitHub CTAs
- widen RFP card/row/listing props to RfpListItem so GitHub data
  (no reward/image) and local Rfp both satisfy the UI
- add optional markdown body to BuildersHubDetailLayout

* refactor: share get started sections
2026-06-04 18:34:29 +09:00
jinhojang6 43b18f3232 feat: implement newsletter signup functionality with react-query; refactor footer components to integrate new newsletter form 2026-06-02 22:44:34 +09:00
FelicioandJulesFILIOT ee04d72683 add forms for CRM funnels
* add forms for CRM funnels

* a

* set profile and lead source

- https://civicrm.logos.co/civicrm/admin/afform/submissions#/?name=afformActivistBuilder
- https://civicrm.logos.co/civicrm/contact/view?reset=1&cid=1000
- https://civicrm.logos.co/civicrm/contact/view?reset=1&cid=1000
- https://civicrm.logos.co/civicrm/contact/view/case?reset=1&id=388&cid=1000&action=view&context=case&selectedChild=case&key=CRMCaseFormSearch3vjNUK027Z0YMdmpnTbHyNePydwogixSnFN3Z2YluYYR4_8939

* default checkboxes to true

* remove titles

* u

* add policy

* t

* fix(civi-crm): avoid readonly env writes in CORS test

Use a keyed env helper in the public CORS test so cleanup and setup no longer rely on delete or readonly typed env properties.

* fix(web): align node programme route keys

Rename node program route/message keys to node programme and update links, metadata, sitemap entries, and contract tests.

---------

Co-authored-by: JulesFILIOT <dev@julesfiliot.com>
2026-06-01 17:59:37 +09:00
Jules fdd51a7965 feat(civi-crm): implement API route handlers
* feat(civi-crm-cms): add project architecture documentation

* chore(civi-crm-cms): register app in mono-repo

Add civi-crm-cms env vars to turbo.json build.env. Update root AGENTS.md
with app entry (port 3002, arch doc link) and dev command. Update lockfile.

* chore(civi-crm-cms): scaffold app config and tooling

Next.js 16, Tailwind v4, Vitest, ESLint, PostCSS — matching apps/web
conventions. Path alias @/* → ./src/*. Port 3002. AGENTS.md links to
the architecture doc and documents doc-update rules and code conventions.

* feat(civi-crm-cms): add lib layer, view config, and domain type stubs

- views.ts: full ViewConfig types + circles_onboarding config + getActiveView()
- auth.ts: getCurrentUserEmail() — DEV_USER_EMAIL_MOCK override + Keycloak header
- civicrm/client.ts: CiviCRMClient skeleton with typed interface
- civicrm/scorecard.ts: computeScorecard() pure function (tested, 4 passing)
- civicrm/{cases,contacts,activities,relationships,coordinators}.ts: stubs
- activity-logger.ts: logActivity() stub
- constants/pagination.ts: PAGE_SIZE = 20
- types/{case,contact}.ts: placeholder stubs

* feat(civi-crm-cms): add app router skeleton

Root layout + redirect to /cases. Page stubs for list and detail views.
Server Actions stub (updateCase, updateContact, swapCoordinator).
API route stubs returning 501 for all planned endpoints:
GET /api/cases, GET+PATCH /api/cases/[id], GET /api/cases/[id]/activities,
PATCH /api/cases/[id]/coordinator, PATCH /api/contacts/[contactId],
GET /api/coordinators.

* feat(civi-crm-cms): add component stubs

AppNav, CasesTable, CasesFilters, CaseDetailShell, CaseFields,
ContactFields, Scorecard, ActivityLog — each returns null with a
TODO comment describing its role per the architecture doc.

* fix(civi-crm-cms): add assignedTo FieldDef backed by Relationship entity

Adds a missing FieldDef for the `assignedTo` listColumn so that
`getFieldDef()` no longer throws when rendering or building select[] for
that column. The field is `readonly` with a dedicated `Relationship`
UpdateTarget, reflecting that coordinator assignment goes through
CiviCRM's Relationship entity rather than a direct Case/Contact PATCH.

* chore(civi-crm-cms): update architecture docs

Remove mention of repo pending node & pnpm upgrade.

* docs(civi-crm-cms): align ActivityLog Server/Client boundary description

The table incorrectly said ActivityLog had no client-side fetching, but
the stub and the dedicated GET /api/cases/[id]/activities?page= route
both indicate it fetches on page change. Corrected to reflect that
ActivityLog is a client-fetching component, with a note that the Server
Component shell can pre-fetch page 1 to avoid a first-render waterfall.

* docs(civi-crm-cms): fix globals.css snippet to use Tailwind v4 import syntax

The snippet used `@tailwind utilities;` which is a v3 directive. Updated
to match the actual app file and apps/web pattern: `@import 'tailwindcss'`
plus the `@source` directive for the shared UI package. Added a note
clarifying that the v3 directives must not be used.

* fix(civi-crm-cms): guard DEV_USER_EMAIL_MOCK against production

* fix(civi-crm-cms): strengthen views and client stubs

- Add skipSelect flag to FieldDef; mark assignedTo so select[] builders
  have a typed signal to exclude it rather than relying on comments
- Add assertViewIntegrity() called from getActiveView() to catch
  scorecardFieldKeys referencing unknown or non-scorecard field keys at startup
- Add constructor to CiviCRMClient wiring CIVICRM_BASE_URL / CIVICRM_API_KEY
- Remove spurious blank lines in update() params
- Document null as a valid CiviWhere value for IS NULL / IS NOT NULL operators

* docs(civi-crm-cms): sync architecture doc with code fixes

- Fix /case/[id] → /cases/[id] route path in §1
- Add Relationship to UpdateTarget union in §5.2
- Add skipSelect to FieldDef snippet in §5.2
- Update auth.ts snippet in §8 to show NODE_ENV guard with explanation
- Add null to CiviWhere type in §6.1 with IS NULL / IS NOT NULL note

* refactor(civi-crm-cms): rename circles_onboarding view to movement_view

Renames the view ID, variable, label, and caseTypeName throughout:
- src/lib/views.ts: const + registry key + ACTIVE_VIEW default
- .env.local.example: ACTIVE_VIEW default value
- docs/civi-crm-cms-architecture.md: §5.1, §5.3, §13 snippets

* feat(civi-crm-cms): implement types and CiviCRM HTTP client

Define raw CiviCRM APIv4 response types, domain types for Case/Contact,
and implement the four CiviCRMClient methods (get, create, update, delete)
with X-Civi-Auth header and CiviCRMError on non-2xx responses.

* test(civi-crm-cms): add CiviCRMClient unit tests

* feat(civi-crm-cms): extend CiviCRM client with IN support, orderBy, and count()

- Widen CiviWhere third element to accept (string | number)[] for IN / NOT IN
- Add optional orderBy to CiviParams, passed through in get() body
- Add count() method that returns data.count without fetching full rows

* feat(civi-crm-cms): implement query layer

Implement all CiviCRM entity query functions:
- cases: listCases (2-round with assignee pre-filter), getCaseById (3+2 parallel rounds), updateCase
- contacts: getContactById, updateContact, updateEmail, resolveOrCreateContactByEmail
- activities: listActivities (paginated, date-desc), createActivity
- relationships: getCoordinatorRelationship, deleteRelationship, createCoordinatorRelationship
- coordinators: listCoordinators (deduplicated by contact)

* feat(civi-crm-cms): implement activity logger

- buildSubject: single change includes from/to if under 128 chars, strips
  values if too long; multiple changes list field names only
- buildDetails: full context -- editor name + email, date, entity, per-field
  before/after values; always written regardless of subject truncation
- logActivity: resolves contact by email, uses display_name as actor,
  writes both subject and details fields; swallows errors to never fail mutations

* refactor(civi-crm-cms): move unit tests to __tests__ directories

* test(civi-crm-cms): add query layer and activity logger tests

- cases: buildCaseSelect exclusions, listCases WHERE/filter/pagination/merge
  behaviour, getCaseById round structure and field mapping, updateCase safety
- activity-logger: buildSubject truncation logic, buildDetails completeness,
  logActivity integration (display_name fallback, details vs subject invariant)

* chore(civi-crm-cms): add check-all convenience scripts

* chore(civi-crm): update directory name

* chore: update lock file

* chore(civi-crm): update directory name

* feat(civi-crm): enable cacheComponents

* feat(civi-crm): implement API route handlers

Replace 501 stubs with working implementations for all six routes.
Fan-out writes use Promise.allSettled with 207 on partial failure.
Scoring fields require all 6 in payload; scorecard is auto-computed.
Coordinator swap is sequential with per-step 500 error messages.
Caching uses the use cache directive with cacheTag/cacheLife;
revalidateTag invalidates on successful mutations.

* test(civi-crm): add route handler tests

32 new tests across GET /api/cases, GET+PATCH /api/cases/[id],
PATCH /api/cases/[id]/coordinator, and PATCH /api/contacts/[contactId].
Covers scorecard injection, 207 on partial failure, logActivity
suppression on failure, coordinator swap ordering, and entity fan-out.
Expands vitest include to src/app/**/*.test.ts.

* chore(ci): add CIVICRM_BASE_URL environment variable to GH test workflow

* fix(ci): read CIVICRM_BASE_URL from GitHub secret

* fix(civi-crm): opt /api/coordinators out of static prerendering

Next.js prerenders parameterless GET handlers at build time, triggering
a live CiviCRM call that fails in CI. export const dynamic is incompatible
with nextConfig.cacheComponents, so use connection() from next/server
instead -- it signals the route requires a live connection without
conflicting with the use cache model.

* ci: remove CIVICRM_BASE_URL not required

* refactor(civi-crm): centralize CONTACT_ID_PREFIX and add getContactFieldValues

- Add CONTACT_ID_PREFIX to civiConfig as the single source of truth
- Add getContactFieldValues(contactId, view) to contacts.ts -- fetches
  current contact field values keyed by FieldDef.key by stripping the
  contact_id. CaseContact join prefix and querying Contact directly

* fix(civi-crm): harden PATCH handlers and fix activity log gaps

- Use civiConfig.CONTACT_ID_PREFIX in cases and contacts routes
- Call getContactFieldValues before writes in PATCH /api/contacts/[contactId]
  so activity log "from" values reflect actual previous state
- Wrap logActivity in try/catch in all three PATCH handlers -- a logger
  failure no longer fails an otherwise successful mutation
- Add CiviCRMError 404 handling to GET /api/cases/[id]/activities
- Add tests: "from" value propagation and logActivity failure resilience

* chore(civi-crm): remove outdated doc
2026-05-22 21:13:12 +09:00
Jules 15aea96184 feat(civi-crm): implement query layer and activity logger
* feat(civi-crm-cms): add project architecture documentation

* chore(civi-crm-cms): register app in mono-repo

Add civi-crm-cms env vars to turbo.json build.env. Update root AGENTS.md
with app entry (port 3002, arch doc link) and dev command. Update lockfile.

* chore(civi-crm-cms): scaffold app config and tooling

Next.js 16, Tailwind v4, Vitest, ESLint, PostCSS — matching apps/web
conventions. Path alias @/* → ./src/*. Port 3002. AGENTS.md links to
the architecture doc and documents doc-update rules and code conventions.

* feat(civi-crm-cms): add lib layer, view config, and domain type stubs

- views.ts: full ViewConfig types + circles_onboarding config + getActiveView()
- auth.ts: getCurrentUserEmail() — DEV_USER_EMAIL_MOCK override + Keycloak header
- civicrm/client.ts: CiviCRMClient skeleton with typed interface
- civicrm/scorecard.ts: computeScorecard() pure function (tested, 4 passing)
- civicrm/{cases,contacts,activities,relationships,coordinators}.ts: stubs
- activity-logger.ts: logActivity() stub
- constants/pagination.ts: PAGE_SIZE = 20
- types/{case,contact}.ts: placeholder stubs

* feat(civi-crm-cms): add app router skeleton

Root layout + redirect to /cases. Page stubs for list and detail views.
Server Actions stub (updateCase, updateContact, swapCoordinator).
API route stubs returning 501 for all planned endpoints:
GET /api/cases, GET+PATCH /api/cases/[id], GET /api/cases/[id]/activities,
PATCH /api/cases/[id]/coordinator, PATCH /api/contacts/[contactId],
GET /api/coordinators.

* feat(civi-crm-cms): add component stubs

AppNav, CasesTable, CasesFilters, CaseDetailShell, CaseFields,
ContactFields, Scorecard, ActivityLog — each returns null with a
TODO comment describing its role per the architecture doc.

* fix(civi-crm-cms): add assignedTo FieldDef backed by Relationship entity

Adds a missing FieldDef for the `assignedTo` listColumn so that
`getFieldDef()` no longer throws when rendering or building select[] for
that column. The field is `readonly` with a dedicated `Relationship`
UpdateTarget, reflecting that coordinator assignment goes through
CiviCRM's Relationship entity rather than a direct Case/Contact PATCH.

* chore(civi-crm-cms): update architecture docs

Remove mention of repo pending node & pnpm upgrade.

* docs(civi-crm-cms): align ActivityLog Server/Client boundary description

The table incorrectly said ActivityLog had no client-side fetching, but
the stub and the dedicated GET /api/cases/[id]/activities?page= route
both indicate it fetches on page change. Corrected to reflect that
ActivityLog is a client-fetching component, with a note that the Server
Component shell can pre-fetch page 1 to avoid a first-render waterfall.

* docs(civi-crm-cms): fix globals.css snippet to use Tailwind v4 import syntax

The snippet used `@tailwind utilities;` which is a v3 directive. Updated
to match the actual app file and apps/web pattern: `@import 'tailwindcss'`
plus the `@source` directive for the shared UI package. Added a note
clarifying that the v3 directives must not be used.

* fix(civi-crm-cms): guard DEV_USER_EMAIL_MOCK against production

* fix(civi-crm-cms): strengthen views and client stubs

- Add skipSelect flag to FieldDef; mark assignedTo so select[] builders
  have a typed signal to exclude it rather than relying on comments
- Add assertViewIntegrity() called from getActiveView() to catch
  scorecardFieldKeys referencing unknown or non-scorecard field keys at startup
- Add constructor to CiviCRMClient wiring CIVICRM_BASE_URL / CIVICRM_API_KEY
- Remove spurious blank lines in update() params
- Document null as a valid CiviWhere value for IS NULL / IS NOT NULL operators

* docs(civi-crm-cms): sync architecture doc with code fixes

- Fix /case/[id] → /cases/[id] route path in §1
- Add Relationship to UpdateTarget union in §5.2
- Add skipSelect to FieldDef snippet in §5.2
- Update auth.ts snippet in §8 to show NODE_ENV guard with explanation
- Add null to CiviWhere type in §6.1 with IS NULL / IS NOT NULL note

* refactor(civi-crm-cms): rename circles_onboarding view to movement_view

Renames the view ID, variable, label, and caseTypeName throughout:
- src/lib/views.ts: const + registry key + ACTIVE_VIEW default
- .env.local.example: ACTIVE_VIEW default value
- docs/civi-crm-cms-architecture.md: §5.1, §5.3, §13 snippets

* feat(civi-crm-cms): implement types and CiviCRM HTTP client

Define raw CiviCRM APIv4 response types, domain types for Case/Contact,
and implement the four CiviCRMClient methods (get, create, update, delete)
with X-Civi-Auth header and CiviCRMError on non-2xx responses.

* test(civi-crm-cms): add CiviCRMClient unit tests

* feat(civi-crm-cms): extend CiviCRM client with IN support, orderBy, and count()

- Widen CiviWhere third element to accept (string | number)[] for IN / NOT IN
- Add optional orderBy to CiviParams, passed through in get() body
- Add count() method that returns data.count without fetching full rows

* feat(civi-crm-cms): implement query layer

Implement all CiviCRM entity query functions:
- cases: listCases (2-round with assignee pre-filter), getCaseById (3+2 parallel rounds), updateCase
- contacts: getContactById, updateContact, updateEmail, resolveOrCreateContactByEmail
- activities: listActivities (paginated, date-desc), createActivity
- relationships: getCoordinatorRelationship, deleteRelationship, createCoordinatorRelationship
- coordinators: listCoordinators (deduplicated by contact)

* feat(civi-crm-cms): implement activity logger

- buildSubject: single change includes from/to if under 128 chars, strips
  values if too long; multiple changes list field names only
- buildDetails: full context -- editor name + email, date, entity, per-field
  before/after values; always written regardless of subject truncation
- logActivity: resolves contact by email, uses display_name as actor,
  writes both subject and details fields; swallows errors to never fail mutations

* refactor(civi-crm-cms): move unit tests to __tests__ directories

* test(civi-crm-cms): add query layer and activity logger tests

- cases: buildCaseSelect exclusions, listCases WHERE/filter/pagination/merge
  behaviour, getCaseById round structure and field mapping, updateCase safety
- activity-logger: buildSubject truncation logic, buildDetails completeness,
  logActivity integration (display_name fallback, details vs subject invariant)

* chore(civi-crm-cms): add check-all convenience scripts

* chore(civi-crm): update directory name

* chore: update lock file

* chore(civi-crm): update directory name

* refactor(civi-crm): tighten CiviCRMClient and expand tests

- Extract repeated auth headers into a private getter
- Simplify get() body to JSON.stringify(params) -- new CiviParams fields no longer need manual wiring
- Guard create() against empty values response; throws CiviCRMError(200) instead of returning undefined
- Assert request body in create and update tests
- Add test for empty-values guard in create

* docs: rename civi-crm-cms to civi-crm in architecture doc

* chore: update lock file

* refactor(civi-crm): tidy count() and add tests

Deduplicate auth headers by reusing this.authHeaders and add an
explanatory comment for the limit:0 / select:['id'] counting pattern.
Add four tests covering URL, body shape, return value and error propagation.

* fix(civi-crm): guard missing lead contact in getCaseById

Throw CiviCRMError(404) when CaseContact is absent rather than silently
querying with contact_id=0. Also documents why Circle_Case.Scorecard is
stripped in updateCase, and removes an unused variable from the test.

* refactor(civi-crm): centralise instance-specific IDs in civiConfig

Extract three numeric IDs and the coordinator relationship type name into
a single config.ts. Update all consumers to reference civiConfig rather
than scattered local constants, making environment differences a one-file
change. Also caps listCoordinators at 2500 rows to bound the unbounded fetch.

* chore(civi-crm): remove outdated doc file

* fix(civi-crm): drop invalid CaseContact.role field

civicrm_case_contact has only id/case_id/contact_id columns; role is not
a valid API field and was never consumed by the list-building logic.

* perf(civi-crm): replace count() with row_count aggregate

The previous implementation fetched every matching row (select id, limit 0)
to derive a count from data.count. Using select: ['row_count'] returns a
single aggregate record without transferring entity rows, which is both
correct per the APIv4 spec and avoids full-table scans on large datasets.

* fix(civi-crm): normalize is_primary to boolean in Email where clause

* docs: add CiviCRM API v4 usage guide

* chore: move civi-crm docs into docs/civi-crm/ and update references

* docs(civi-crm): update client interface in architecture doc and link to api.md
2026-05-22 16:38:39 +09:00
Jules 7ff0d9dc19 feat(civi-crm): types and CiviCRM HTTP client
* feat(civi-crm-cms): add project architecture documentation

* chore(civi-crm-cms): register app in mono-repo

Add civi-crm-cms env vars to turbo.json build.env. Update root AGENTS.md
with app entry (port 3002, arch doc link) and dev command. Update lockfile.

* chore(civi-crm-cms): scaffold app config and tooling

Next.js 16, Tailwind v4, Vitest, ESLint, PostCSS — matching apps/web
conventions. Path alias @/* → ./src/*. Port 3002. AGENTS.md links to
the architecture doc and documents doc-update rules and code conventions.

* feat(civi-crm-cms): add lib layer, view config, and domain type stubs

- views.ts: full ViewConfig types + circles_onboarding config + getActiveView()
- auth.ts: getCurrentUserEmail() — DEV_USER_EMAIL_MOCK override + Keycloak header
- civicrm/client.ts: CiviCRMClient skeleton with typed interface
- civicrm/scorecard.ts: computeScorecard() pure function (tested, 4 passing)
- civicrm/{cases,contacts,activities,relationships,coordinators}.ts: stubs
- activity-logger.ts: logActivity() stub
- constants/pagination.ts: PAGE_SIZE = 20
- types/{case,contact}.ts: placeholder stubs

* feat(civi-crm-cms): add app router skeleton

Root layout + redirect to /cases. Page stubs for list and detail views.
Server Actions stub (updateCase, updateContact, swapCoordinator).
API route stubs returning 501 for all planned endpoints:
GET /api/cases, GET+PATCH /api/cases/[id], GET /api/cases/[id]/activities,
PATCH /api/cases/[id]/coordinator, PATCH /api/contacts/[contactId],
GET /api/coordinators.

* feat(civi-crm-cms): add component stubs

AppNav, CasesTable, CasesFilters, CaseDetailShell, CaseFields,
ContactFields, Scorecard, ActivityLog — each returns null with a
TODO comment describing its role per the architecture doc.

* fix(civi-crm-cms): add assignedTo FieldDef backed by Relationship entity

Adds a missing FieldDef for the `assignedTo` listColumn so that
`getFieldDef()` no longer throws when rendering or building select[] for
that column. The field is `readonly` with a dedicated `Relationship`
UpdateTarget, reflecting that coordinator assignment goes through
CiviCRM's Relationship entity rather than a direct Case/Contact PATCH.

* chore(civi-crm-cms): update architecture docs

Remove mention of repo pending node & pnpm upgrade.

* docs(civi-crm-cms): align ActivityLog Server/Client boundary description

The table incorrectly said ActivityLog had no client-side fetching, but
the stub and the dedicated GET /api/cases/[id]/activities?page= route
both indicate it fetches on page change. Corrected to reflect that
ActivityLog is a client-fetching component, with a note that the Server
Component shell can pre-fetch page 1 to avoid a first-render waterfall.

* docs(civi-crm-cms): fix globals.css snippet to use Tailwind v4 import syntax

The snippet used `@tailwind utilities;` which is a v3 directive. Updated
to match the actual app file and apps/web pattern: `@import 'tailwindcss'`
plus the `@source` directive for the shared UI package. Added a note
clarifying that the v3 directives must not be used.

* fix(civi-crm-cms): guard DEV_USER_EMAIL_MOCK against production

* fix(civi-crm-cms): strengthen views and client stubs

- Add skipSelect flag to FieldDef; mark assignedTo so select[] builders
  have a typed signal to exclude it rather than relying on comments
- Add assertViewIntegrity() called from getActiveView() to catch
  scorecardFieldKeys referencing unknown or non-scorecard field keys at startup
- Add constructor to CiviCRMClient wiring CIVICRM_BASE_URL / CIVICRM_API_KEY
- Remove spurious blank lines in update() params
- Document null as a valid CiviWhere value for IS NULL / IS NOT NULL operators

* docs(civi-crm-cms): sync architecture doc with code fixes

- Fix /case/[id] → /cases/[id] route path in §1
- Add Relationship to UpdateTarget union in §5.2
- Add skipSelect to FieldDef snippet in §5.2
- Update auth.ts snippet in §8 to show NODE_ENV guard with explanation
- Add null to CiviWhere type in §6.1 with IS NULL / IS NOT NULL note

* refactor(civi-crm-cms): rename circles_onboarding view to movement_view

Renames the view ID, variable, label, and caseTypeName throughout:
- src/lib/views.ts: const + registry key + ACTIVE_VIEW default
- .env.local.example: ACTIVE_VIEW default value
- docs/civi-crm-cms-architecture.md: §5.1, §5.3, §13 snippets

* feat(civi-crm-cms): implement types and CiviCRM HTTP client

Define raw CiviCRM APIv4 response types, domain types for Case/Contact,
and implement the four CiviCRMClient methods (get, create, update, delete)
with X-Civi-Auth header and CiviCRMError on non-2xx responses.

* test(civi-crm-cms): add CiviCRMClient unit tests

* chore(civi-crm): update directory name

* chore: update lock file

* refactor(civi-crm): tighten CiviCRMClient and expand tests

- Extract repeated auth headers into a private getter
- Simplify get() body to JSON.stringify(params) -- new CiviParams fields no longer need manual wiring
- Guard create() against empty values response; throws CiviCRMError(200) instead of returning undefined
- Assert request body in create and update tests
- Add test for empty-values guard in create

* docs: rename civi-crm-cms to civi-crm in architecture doc
2026-05-22 13:09:02 +09:00
jinhojang6 113902fc3a fix(ci): get pnpm test:release fully green end-to-end
- apps/cms: auto-formatted prettier warnings in scripts and tests
  (schema-push-prompts.test.ts, dev.ts, schema-push-prompts.ts).
- apps/web: auto-formatted prettier warnings in lib/press-engine.ts.
- apps/web/lib/__tests__/link-policy.test.ts: drop docs.logos.co
  from the blocked-URL list — the live Get Started page now uses it
  as the canonical docs entry point via EXTERNAL_URLS.docs.
- apps/cms/.../save-site-content-as-pr.test.ts: footerSchema now
  requires a newsletter object; add it to the test fixture so the
  builder passes Zod parsing.
- apps/web/package.json: declare tsx as an explicit devDependency so
  test:perf / test:static find it under apps/web/node_modules/.bin
  after the pnpm 11 lockfile regeneration changed hoisting.
- apps/web/test/performance/budgets.ts: bump maxHtmlBytes 325k -> 430k
  to cover homepage growth from recent motion polish and Get Started.
- pnpm-lock.yaml: refresh after adding tsx to apps/web.

Verified: pnpm test:release passes locally end-to-end.
2026-05-21 20:24:54 +09:00
jinhojang6 ebfcd999cb fix(ci): unbreak Vercel install and prevent pnpm/lockfile drift
- Remove pinned pnpm 10.9.0 from pnpm/action-setup so it honors
  packageManager (pnpm@11.1.0) — fixes ERR_PNPM_BAD_PM_VERSION.
- Regenerate pnpm-lock.yaml to restore the vitest peer-dep snapshot
  resolved against @types/node@25.6.0 (civi-crm) — fixes
  ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY on Vercel.
- Add scripts/__tests__/repo-health.test.mjs guarding against both
  classes of failure: workflow pnpm version must match packageManager,
  and pnpm-lock.yaml must satisfy --frozen-lockfile.
- Wire pnpm test:repo-health into test:release as the first step.
2026-05-21 19:37:11 +09:00
Jules a162388bfa feat(civi-crm-cms): architecture doc and project skeleton
* feat(civi-crm-cms): add project architecture documentation

* chore(civi-crm-cms): register app in mono-repo

Add civi-crm-cms env vars to turbo.json build.env. Update root AGENTS.md
with app entry (port 3002, arch doc link) and dev command. Update lockfile.

* chore(civi-crm-cms): scaffold app config and tooling

Next.js 16, Tailwind v4, Vitest, ESLint, PostCSS — matching apps/web
conventions. Path alias @/* → ./src/*. Port 3002. AGENTS.md links to
the architecture doc and documents doc-update rules and code conventions.

* feat(civi-crm-cms): add lib layer, view config, and domain type stubs

- views.ts: full ViewConfig types + circles_onboarding config + getActiveView()
- auth.ts: getCurrentUserEmail() — DEV_USER_EMAIL_MOCK override + Keycloak header
- civicrm/client.ts: CiviCRMClient skeleton with typed interface
- civicrm/scorecard.ts: computeScorecard() pure function (tested, 4 passing)
- civicrm/{cases,contacts,activities,relationships,coordinators}.ts: stubs
- activity-logger.ts: logActivity() stub
- constants/pagination.ts: PAGE_SIZE = 20
- types/{case,contact}.ts: placeholder stubs

* feat(civi-crm-cms): add app router skeleton

Root layout + redirect to /cases. Page stubs for list and detail views.
Server Actions stub (updateCase, updateContact, swapCoordinator).
API route stubs returning 501 for all planned endpoints:
GET /api/cases, GET+PATCH /api/cases/[id], GET /api/cases/[id]/activities,
PATCH /api/cases/[id]/coordinator, PATCH /api/contacts/[contactId],
GET /api/coordinators.

* feat(civi-crm-cms): add component stubs

AppNav, CasesTable, CasesFilters, CaseDetailShell, CaseFields,
ContactFields, Scorecard, ActivityLog — each returns null with a
TODO comment describing its role per the architecture doc.

* fix(civi-crm-cms): add assignedTo FieldDef backed by Relationship entity

Adds a missing FieldDef for the `assignedTo` listColumn so that
`getFieldDef()` no longer throws when rendering or building select[] for
that column. The field is `readonly` with a dedicated `Relationship`
UpdateTarget, reflecting that coordinator assignment goes through
CiviCRM's Relationship entity rather than a direct Case/Contact PATCH.

* chore(civi-crm-cms): update architecture docs

Remove mention of repo pending node & pnpm upgrade.

* docs(civi-crm-cms): align ActivityLog Server/Client boundary description

The table incorrectly said ActivityLog had no client-side fetching, but
the stub and the dedicated GET /api/cases/[id]/activities?page= route
both indicate it fetches on page change. Corrected to reflect that
ActivityLog is a client-fetching component, with a note that the Server
Component shell can pre-fetch page 1 to avoid a first-render waterfall.

* docs(civi-crm-cms): fix globals.css snippet to use Tailwind v4 import syntax

The snippet used `@tailwind utilities;` which is a v3 directive. Updated
to match the actual app file and apps/web pattern: `@import 'tailwindcss'`
plus the `@source` directive for the shared UI package. Added a note
clarifying that the v3 directives must not be used.

* fix(civi-crm-cms): guard DEV_USER_EMAIL_MOCK against production

* fix(civi-crm-cms): strengthen views and client stubs

- Add skipSelect flag to FieldDef; mark assignedTo so select[] builders
  have a typed signal to exclude it rather than relying on comments
- Add assertViewIntegrity() called from getActiveView() to catch
  scorecardFieldKeys referencing unknown or non-scorecard field keys at startup
- Add constructor to CiviCRMClient wiring CIVICRM_BASE_URL / CIVICRM_API_KEY
- Remove spurious blank lines in update() params
- Document null as a valid CiviWhere value for IS NULL / IS NOT NULL operators

* docs(civi-crm-cms): sync architecture doc with code fixes

- Fix /case/[id] → /cases/[id] route path in §1
- Add Relationship to UpdateTarget union in §5.2
- Add skipSelect to FieldDef snippet in §5.2
- Update auth.ts snippet in §8 to show NODE_ENV guard with explanation
- Add null to CiviWhere type in §6.1 with IS NULL / IS NOT NULL note

* refactor(civi-crm-cms): rename circles_onboarding view to movement_view

Renames the view ID, variable, label, and caseTypeName throughout:
- src/lib/views.ts: const + registry key + ACTIVE_VIEW default
- .env.local.example: ACTIVE_VIEW default value
- docs/civi-crm-cms-architecture.md: §5.1, §5.3, §13 snippets

* refactor(civi-crm): rename civi-crm-cms app

Align app naming, paths, and docs with its role as a CiviCRM web layer.
2026-05-21 14:47:04 +09:00
jinhojang6 ca887f85ae chore: downgrade @types/node to ^24.12.4 across multiple packages 2026-05-20 14:42:29 +09:00
jinhojang6 e319fdc696 feat: publish Logos packages to npm 2026-05-15 20:01:59 +09:00
jinhojang6 9aeb1993b2 Refactor code structure for improved readability and maintainability 2026-05-07 09:57:22 +09:00
jinhojang6 8f3da4c607 feat: integrate circles map and events functionality with dynamic loading 2026-04-29 22:06:36 +09:00
jinhojang6 0615503046 feat: add RFP and Idea management features 2026-04-28 17:01:02 +09:00
jinhojang6 a573bbf969 feat: add Builders Hub implementation plan and Figma specs for home page 2026-04-28 16:25:32 +09:00
jinhojang6 a51b6cff8d feat: add GitHub smoke test suite and mutation primitives 2026-04-28 11:01:29 +09:00
jinhojang6 a3ead09137 feat: update README and add deployment documentation for Vercel and Turso integration 2026-04-28 01:12:12 +09:00
jinhojang6 08606373b1 feat: add schemas for circles, press articles, and site settings 2026-04-28 00:45:24 +09:00
jinhojang6 cc000b6aa0 feat: add blockchain and networking sections with related articles and features 2026-04-25 02:24:34 +09:00
jinhojang6 0618023ad2 feat: integrate tailwind-merge for improved class management in Button component 2026-04-24 03:07:54 +09:00
jinhojang6 ee30a72f1c feat: add footer and navigation overlay components with icons 2026-04-22 00:29:40 +09:00
jinhojang6 856ea90979 feat: add design tokens for colors and typography 2026-04-20 23:42:00 +09:00
Jinho Jang c0cb0ff0ed Initial commit 2026-04-20 22:24:39 +09:00