Commit Graph
64 Commits
Author SHA1 Message Date
Jules 8a70822731 refactor(funnel): single source of truth for funnel form options
* refactor(web): dedupe funnel form options across the 3 forms

`skills`, `chatService` and `country` were byte-identical in all three
form definitions. They move to `packages/funnel/src/form-options.ts` and
reach the pages through `AFFORM_OPTIONS`; the form files now describe
rendering only.

Lists verified ordered-identical to the originals, entry for entry.

Closes #140

* refactor(civi-crm): derive notion id/label maps from shared funnel options

`SKILLS_MAP`, `CHAT_SERVICE_MAP` and `COUNTRY_MAP` were a fourth copy of
the funnel option lists. They are now `toLabelMap()` over the shared
arrays, the way `HEAR_ABOUT_MAP` already was, so an id cannot mean one
thing in the dropdown and another in Notion.

All 275 derived entries verified identical to the previous literals, so
the Notion and n8n writes are unchanged. The one exception is country
1054, normalised to `Côte d'Ivoire` -- the spelling Notion already stored
and the one the other apostrophised country names use. The dropdown label
and the Ghost member note now match it.
2026-08-26 00:35:44 +09:00
Jules 4a32420ddf fix(web): build through turbo instead of rebuilding shared deps
* fix(web): build through turbo instead of rebuilding shared deps

The app build re-ran the logos-ui build, whose `tsup --clean` wiped
packages/ui/dist while logos-crm compiled against it in parallel. The deploy
now runs the same turbo build as CI, so only the ui task writes that dist.
`out/**` joins the build outputs since turbo owns the export now.

* docs(web): build the app through turbo

* fix(web): build on Vercel through turbo

Vercel ran the app build directly, which no longer builds
@acid-info/logos-ui and failed to resolve it. vercel.json pins the deploy to
the same turbo command Jenkins runs.
2026-08-25 00:19:01 +09:00
Jules dfe8cf0451 fix(civi-crm): validate required funnel fields on afform-submit
* feat(funnel): add per-form required field list

Single source for which funnel answers are required, plus the check the
intake endpoint runs a submission against.

* refactor(web): build the funnel form schema from the shared list

Form definitions no longer carry a `required` flag, they describe
rendering only. The three pages pass REQUIRED_FIELDS_BY_FORM into
ConnectFormSection, which derives the zod schema and the required markers
from it.

* fix(civi-crm): validate required funnel fields on afform-submit

Only formName, the hear-about answer and the captcha were checked, so a
direct POST could create a record with no name, email or country. A
payload missing a required answer now gets a 400 naming the fields.

* docs(funnel): document the required-field validation
2026-08-24 23:24:13 +09:00
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
Jules cc621dcfb1 feat(web): forward submitted form data on newsletter signup
* chore(web): add a debug level to the logger

Logs in every build but tests, for output a deployed preview needs.

* feat(web): forward submitted form data on newsletter signup

Every field a form collects is sent to the subscribe endpoint, select
values as their labels rather than CiviCRM option ids. Logs the payload.

* docs(funnel): document the forwarded subscribe payload

* fix(web): build the funnel newsletter payload from answers only

Keeps the intake captcha token and field defs out of the newsletter
service whatever a caller puts in extraPayload.
2026-08-05 19:42:15 +09:00
Jules 96901178e0 feat(web): subscribe funnel opt-ins to newsletters
* refactor(funnel): move profile map to @repo/funnel

The profile label is now needed by apps/web too. Keeps it from drifting
from the Notion Profile select.

* feat(web): subscribe funnel opt-ins to newsletters

Each ticked checkbox subscribes the submitted email to its Ghost
newsletter and appends a note to the member profile. Fired after the
intake POST resolves, sequentially, and never surfaces as a form error.

* docs(funnel): document newsletter opt-ins
2026-07-30 21:31:50 +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
Jules dc0f7d55db refactor(civi-crm): write affiliated orgs to Mvmt Organization text field
* refactor(civi-crm): write affiliated orgs to Mvmt Organization text field

Redirect affiliatedOrgs from the Organization select to a new Mvmt
Organization rich_text property (clamped to 2000 chars, omitted when
empty). Drops the per-submit org-options lookup and the select
resolution helper.

* docs(funnel): document Mvmt Organization mapping
2026-06-30 23:28:31 +09:00
jinhojang6 78619e28dc feat(field-guide): add Logos Field Guide at /field-guide
Port the 16-chapter Logos Field Guide from the standalone reference site
into logos.co as content-driven pages, discoverable from the footer and
the global nav.

- content/field-guide/en: manifest (TOC) + chapter Markdown bodies
- @repo/content: fieldGuide schema + loaders, readText fs helper
- /field-guide index + /field-guide/[slug] routes (static export)
- FieldGuideShell (sidebar, pager, keyboard nav) + FieldGuideContent
  (react-markdown to design tokens); light theme, site header retained
- Footer "Brand Guidelines" group and nav Explore > Resources links
- sitemap, routes constant, i18n metadata, loader/integrity tests

Closes #65
2026-06-30 02:29:00 +09:00
Jules bcf6c7a878 chore(civi-crm): add Notion evaluation template
* chore: add Notion evaluation template

Use the evaluation template for intake-created Notion pages and document the helper module.

* ci: authenticate build-time GitHub API calls

Pass the Actions GITHUB_TOKEN to the web build (via turbo passThroughEnv)
so RFP fetches in generateStaticParams() avoid the unauthenticated
60 req/hr limit that intermittently 403s and fails the static export.
2026-06-26 19:28:41 +09:00
Jules 79cfba374b feat(civi-crm): split funnel websites into discrete Notion columns
* feat: write each funnel website to its own Notion column

Spread website[] across Website, Website 2..Website 5 instead of joining with ' | '. Empty entries dropped, columns fill contiguously, capped at 5.

* feat: cap funnel website field at 5 rows

Hide 'Add another' once 5 website rows exist, matching the five Notion website columns.

* docs: document per-column website mapping in funnel
2026-06-26 19:22:03 +09:00
jinhojang6 0c9f3a934e fix: update site title and description for clarity and branding 2026-06-23 00:12:22 +09:00
jinhojang6 e6ff43022e fix: update descriptions for consistency and clarity across various components 2026-06-18 21:37:12 +09:00
jinhojang6 d40ee16ce1 chore: remove monorepo test coverage implementation plan document 2026-06-18 01:49:38 +09:00
Jinho Jang 760c5a1eb2 feat(web): add Manifesto page with routing and content (#42)
* feat(manifesto): add manifesto page with content and routing details

* Refactor code structure for improved readability and maintainability
2026-06-11 17:17:45 +09:00
JulesFILIOT a7b0850954 docs: add responsive image frame fitting guide
Six reusable techniques (fill, aspect-ratio, object-fit, derived heights,
sibling sync, sizes) with a decision tree and a worked example tied to
the homepage Basecamp card. Registered in root and web AGENTS.md.
2026-06-05 15:58:32 +09:00
JulesFILIOT 1cb44ff0cd feat(web): add Repo/Docs links to networking feature cards
Buttons pin above each card image, matching messaging page style.
2026-06-03 20:41:52 +09:00
JulesFILIOT 2fa3f00829 docs: mark networking hero copy as applied in pending doc
Record implemented hero fields and keep feature/builder CTA decisions open.
2026-06-03 20:24:05 +09:00
Jules 51345d4012 feat(funnel): migrate /connect to afform-submit flow
* feat(funnel): route connect through afform-submit

Send /connect submissions to the shared afform-submit endpoint and allowlist afformCircleContactForm.

* fix(civi-crm): make connect civi-only intake

Gate Notion to movement forms, add connect case defaults, and allow missing Notion profile mappings for non-Notion intake forms.

* chore(civi-crm): remove legacy contact endpoint

Drop the old public contact submission route after connect migrated to afform-submit.

* test(civi-crm): cover connect afform-submit path

Assert connect case defaults, Note joins, and CiviCRM-required behaviour when Notion is skipped.

* docs(funnel): add connect to intake flow

Document the fourth form and its CiviCRM-only routing contract in the funnel intake guide.

* fix(web): update afform circle contact form question field

* feat(notion): add scorecard page content to funnel form submissions

heading_2, four bulleted_list_item blocks, and a paragraph for the
overall fit line using proper Notion block types.

* refactor(civi-crm): drop wildcard CORS from afform-submit

Rely on the public-cors middleware for origin allowlisting instead of
emitting Access-Control-Allow-Origin: * and a redundant OPTIONS handler.

* feat(web): rename /connect route to /contact

Updates the ROUTES constant and moves the app directory from
[locale]/connect to [locale]/contact.

* chore(web): exclude /contact from sitemap

Page currently redirects to the homepage, so it should not be indexed.

* docs(funnel): note /connect renamed to /contact and form removed
2026-06-02 16:50:28 +09:00
JulesandFelicio 77d83d2f1e feat: route all three intake forms to Notion & CiviCRM
* add forms for CRM funnels

* a

* feat(civi-crm): add notion-coalition-partner API route

Accepts coalition-partner form submissions and writes them to a
Notion DB. Resolves numeric IDs for country, skills and chat service
to human-readable labels. Same hCaptcha guard pattern as afform-submit.
Includes CORS headers for cross-origin requests from apps/web.

* feat(web): wire coalition-partner form to Notion API route

* fix(web): checkbox default value and broken checked visual state

Checkboxes were initialising to true instead of false, causing unchecked
submissions to send true to the API. peer-checked: on the Check icon also
never fired because it is not a direct sibling of the peer input -- replaced
with group-has-[input:checked]: which works via the label's group class.

* chore(civir-crm-api): suppress unused variable warnings in coalition-partner route

* fix(civi-crm): set CORS headers for public API routes via next.config

* 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.

* chore(civi-crm): lint:fix

* docs(funnel): document Notion intake CRM setup

Add funnel docs covering goals, property mapping, completed schema DDL,
Hide-when-empty limitations, manual UI checklist, and implementation roadmap.

* feat(civi-crm): add Notion lib for funnel intake submissions

Self-contained maps, property builder, and submitToNotion with
Organization option matching and Notion-Version 2026-03-11.

* docs(funnel): add implementation guide and sync status

Document phased steps, Notion lib layout, and refresh README/roadmap
for completed schema and submitToNotion work still pending wiring.

* docs(funnel): expand implementation steps and Notion lib notes

Document goals per phase, legacy route vs src/lib/notion/, and link
schema mapping to the code.

* refactor(civi-crm): extract Afform submit into submit-afform lib

Decouple CiviCRM writes from the afform-submit route for upcoming
Notion + CiviCRM orchestration.

* feat(civi-crm): add funnel intake submit disable flags

FUNNEL_INTAKE_NOTION_DISABLED and FUNNEL_INTAKE_CIVICRM_DISABLED opt out
per destination; unset means submit (default).

* feat(civi-crm): orchestrate afform-submit with Notion and CiviCRM

Verify hCaptcha once; Notion required when enabled, CiviCRM best-effort.
Point coalition partner at the shared endpoint; add CORS for public web.

* feat(civi-crm): set Notion BU to Movement on funnel intake

* chore(civi-crm): remove notion coalition partner route

* docs(civi-crm): document funnel intake endpoint and env

* docs(funnel): document orchestrator, env flags, and BU default

* docs(funnel): document cleanup completion and deploy checklist

Record removal of notion-coalition-partner, single afform-submit path,
and non-local Notion env requirements for operators.

* docs(funnel): replace per-phase docs with consolidated AGENTS.md

Merge README, implementation, and notion-database into a single
agent-targeted reference covering request flow, code layout, env vars,
and the full IFT BD CRM schema (all 32 properties, funnel relevance
annotated).

* refactor(civi-crm): rename NOTION_COALITION_PARTNER_DB_ID to NOTION_DB_ID

* docs: rename NOTION_COALITION_PARTNER_DB_ID to NOTION_DB_ID

* feat(notion): add scorecard page content to funnel form submissions

heading_2, four bulleted_list_item blocks, and a paragraph for the
overall fit line using proper Notion block types.

* refactor(civi-crm): drop wildcard CORS from afform-submit

Rely on the public-cors middleware for origin allowlisting instead of
emitting Access-Control-Allow-Origin: * and a redundant OPTIONS handler.

---------

Co-authored-by: Felicio <felicio@users.noreply.github.com>
2026-06-02 16:09:03 +09:00
jinhojang6 a4b8d07c7c feat: apply blockchain page copy and trim copywriting docs to pending-only
- Update /technology-stack/blockchain copy: meta title/description, hero body
  and key repositories (Nomos -> Logos), testnet status, overview, and feature
  card copy (Mantle Channels -> Blend Network)
- Add docs/copywriting/technology-stack-blockchain.md recording held decisions
- Trim copywriting docs to list only pending/to-do items
2026-06-02 02:28:02 +09:00
jinhojang6 dcf13a3c96 feat: update storage technology-stack page copy
Apply confirmed before/after copy to the Storage page: meta title/description, hero body, module repository description, testnet CTA label, overview paragraphs, and the two module repo links (codex-storage placeholder -> correct logos repos). Record remaining open decisions (per-module docs links vs link policy, hero CTA, builder/basecamp routing, lorem-ipsum sections) in docs/copywriting/technology-stack-storage.md.
2026-06-02 02:26:14 +09:00
jinhojang6 a9785b44d9 docs: apply networking page copy update and log pending decisions
Apply straight before/after copy swaps on existing fields in the
networking technology-stack page (meta title/description/heading, hero
headline+body, Overview description). Record the remaining structural
and link decisions (hero Key Repositories + status block, feature
section restructure, builder CTA links) in docs/copywriting for later.
2026-06-02 02:25:37 +09:00
jinhojang6 44f7ba2df1 feat: update messaging page copy from latest copy doc
- Meta title and description
- Current Status body and Install CTA label (drop v0.1)
- Delivery and Chat descriptions (drop layer-prefix)
- Add messaging pending-copy backup; trim 'already applied' notes from copy docs
2026-06-02 02:25:11 +09:00
jinhojang6 565b389d84 content: update technology-stack page copy
Apply the technology-stack copy refresh to content/pages/en/technology-stack.json:
meta, hero, architecture overview, diagram header, basecamp chip, pillars
(incl. new blockchain LEZ/PoS details), networking + foundation rows, the
Logos Basecamp section, how-it-compares body, open-source repos, and use-case
cards 1-4. Track remaining open decisions in docs/copywriting/technology-stack.md.
2026-06-02 02:23:17 +09:00
jinhojang6 1a9bfe28aa feat: update homepage copy from latest copy doc
Apply before/after copy changes to homepage messages and PageCopy:
- Hero kicker + mobileBody typo fix
- WHY section: intro, Debt/Corruption/Stagnation bodies, 3-paragraph closing
- Basecamp: description fixes, title, feature labels
- Choose your path: Build/Operate bodies, CTA labels (Start Building, Join the Movement)
- Start Building: title/body/mobileBody
- Tech Stack module bodies, Networking 'Mixnet', Foundation 'Logos Runtime'
- Parallel Society CTA to external ps.logos.co

Back up pending/structural copy decisions to docs/copywriting.
2026-06-02 02:18:59 +09:00
Jinho Jang f7d45d861c Homepage copy updates (#26)
* feat: update content and descriptions across various sections to enhance clarity and alignment with new messaging

* feat: enhance SupportCard with button icon and update home section media labels for clarity
2026-06-01 17:02:04 +09:00
Jules 128abb857f feat(civi-crm): implement server actions, page shells, and AppNav
* feat(civi-crm): add debug HTTP logging to CiviCRM client

Extracts a private request() method in CiviCRMClient that wraps all
fetch calls. When LOG_LEVEL=DEBUG, logs each outgoing request (URL +
body) and response (status + body + latency) via console.debug.

* test(civi-crm): cover debug logging in CiviCRM client

* chore(civi-crm): trim response log in http client

* chore(civi-crm): add test case for trimmed http client logs

* fix(civi-crm): send api4 params in query string

Route all CiviCRM actions through URL-encoded params and enforce object-shaped orderBy to avoid sort-direction parsing errors. Update civi-crm internal docs to match the new request/route behavior.

* test(civi-crm): consolidate client transport coverage

Merge duplicated client tests into __tests__/client.test.ts and update assertions for query-param transport and orderBy map semantics.

* test(civi-crm): fix cases tests for query params

Update case query assertions to read URL-encoded params from request URLs now that the CiviCRM client no longer sends JSON bodies.

* fix(civi-crm): chain contact email creation

Use APIv4 chaining to create Contact and primary Email together, and extend civiClient.create with optional chain options while preserving existing call signatures.

* docs(civi-crm): document api4 chaining usage

Update CiviCRM API and architecture docs to reflect resolveOrCreateContactByEmail usage and the Contact+Email chaining pattern.

* feat(civi-crm): add AppNav component and server-url helper

Add getBaseUrl() for absolute-URL internal fetches from Server Components,
and an AppNav that renders the active view label in the root layout.

* feat(civi-crm): implement cases list/detail pages and server actions

Pages use a Suspense + inner async component pattern for PPR compatibility.
Both pages fetch data via their respective API routes using absolute URLs.
Server actions call PATCH routes with auth header forwarding and revalidate
affected paths. Client component stubs are typed with correct props.

* fix(civi-crm): correct caseTypeName in movementView config

* fix(civi-crm): scope coordinator relationship lookup to current case type

CiviRelationship.case_id can be null for non-case relationships. When those
nulls leaked into the id IN clause, CiviCRM ignored the case_type_id:name
filter and returned cases of all types. Fix by adding IS NOT NULL and a
case_id.case_type_id:name filter to the relationship query so only IDs of
the correct case type ever reach the Case query.
2026-06-01 15:08:01 +09:00
Jinho Jang 1a37cf1f22 fix: use British English for public copy and paths (#5) 2026-05-26 19:22:57 +09:00
jinhojang6 6463494e96 feat: Add Basecamp page and update links throughout the site
- Introduced new Basecamp page at `/basecamp` with detailed sections on its features, capabilities, and installation.
- Updated multiple references across the site to link to the new Basecamp page instead of the old technology stack anchor.
- Added new images for Basecamp to the public images directory.
- Modified existing JSON files to reflect the new structure and content for Basecamp.
- Enhanced the navigation to direct users to the Basecamp installation page.
2026-05-23 00:04:32 +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 932711206b Update SEO documentation to reflect changes in article page URLs and JSON-LD structure 2026-05-21 17:14:20 +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 dbdca73925 fix: hide deferred about and circle detail surfaces 2026-05-20 23:48:26 +09:00
jinhojang6 94baf77713 chore: pin node 24 and pnpm 11 2026-05-19 17:57:00 +09:00
jinhojang6 45a6d535c2 feat: enhance GitHub App private key handling and add schema push automation tests 2026-05-18 18:50:32 +09:00
jinhojang6 8f5777ac5b docs: plan monorepo test coverage 2026-05-18 14:17:03 +09:00
jinhojang6 95bb87b975 docs: align cms workflow notes with save flow 2026-05-18 03:15:40 +09:00
jinhojang6 d59d4689ec fix: update Logos forum links 2026-05-15 20:38:47 +09:00
jinhojang6 e319fdc696 feat: publish Logos packages to npm 2026-05-15 20:01:59 +09:00
jinhojang6 989acefdce feat: Update content descriptions and improve Press Engine integration for articles 2026-05-14 21:30:57 +09:00
jinhojang6 d090c1c3ee feat: Refactor press article handling by removing static content and updating loaders to fetch from Logos Press Engine 2026-05-14 21:12:59 +09:00
jinhojang6 9bf5f1bc6d feat: enforce GitHub App authentication and remove personal access token support 2026-05-12 19:27:17 +09:00
jinhojang6 a1c7dd5bbe feat: add database connection and query timeout configurations to deployment guide 2026-05-11 16:12:04 +09:00
jinhojang6 944252da9f feat: update environment variables and enhance Recent PR banner functionality 2026-05-11 15:44:35 +09:00
jinhojang6 9c97802363 feat: implement GitHub App authentication for CMS pull requests and add delete functionality for content types 2026-05-11 15:19:24 +09:00
jinhojang6 c4f0120a7f feat: add functionality to create and save CircleEvent and CircleInitiative as pull requests 2026-05-09 19:05:41 +09:00
jinhojang6 674740c180 refactor: simplify Figma references in documentation by removing URLs 2026-05-09 16:29:47 +09:00
jinhojang6 71a6ac6891 feat: update environment variable handling and improve deployment documentation 2026-05-07 20:07:42 +09:00