# Help Center

Public knowledge base and ticket flows


Run a public knowledge base and ticket portal that reads all of its content and configuration from erxes at request time.

For the domain modules behind it, see [Frontline](https://erxes.io/docs/plugin-frontline). For the portal credential it uses, see [Client Portal](https://erxes.io/docs/app-client-portal).

## The app

`apps/help-center` is a standalone Next.js 16.3 (React 19, Tailwind v4) portal (not an Nx project) with Apollo Client 4 for GraphQL. `npm run dev` serves it on port **3900**.

It renders a support-portal landing page (ticket actions, announcements, knowledge base), article browsing and search, ticket submission/tracking, and portal-user sign-in. Every piece of content is read live from the gateway:

| Area | GraphQL source |
| --- | --- |
| Portal identity, toggles, theme, header/footer | `helpCenterGetConfigByDomain` (Frontline helpcenter module, no login required) |
| Knowledge base | `cpKnowledgeBaseTopicDetail` (Frontline knowledgebase) |
| Announcements | `cpPostList` / `cpPost` (CMS, portal scope) |
| Tickets | `cpCreateTicket`, `cpGetTickets`, `cpGetTicket`, `cpTicketGetNotes`, `cpTicketCreateNote` (Frontline tickets) |
| Forms | `cpForms`, `cpFormDetail` (Frontline forms) |
| Visitor session | `clientPortalUserRegister`, `clientPortalUserLoginWithCredentials`, `clientPortalLogout` |

## One variable, config-driven

The only setting the app itself needs is `NEXT_PUBLIC_ERXES_API_URL`, the gateway base URL with no trailing slash. Everything else (portal name, app token, knowledge-base topic, ticket pipeline, labels, colors) comes from a **help center** record the backend looks up by the host each request arrives on.

Create the record under **Frontline → Help Center** and set its **Website** to the address the portal is served from (`http://localhost:3900` for local work). The Website field is a Client Portal picker: choosing a portal also stores that portal's token on the config as `erxesAppToken`, which the app then sends as `x-app-token` for the `cp*` queries. Until a help center claims the request host, every page renders a "no help center configured" message naming the domain it looked for. The config's editable header fields (wordmark, section labels, search placeholder) and footer fields (logo, description, copyright, link columns) are exposed in the `helpCenterGetConfigByDomain` payload and edited from the Help Center drawer in Frontline UI. See [Knowledge Base, Help Center & Reports](https://erxes.io/docs/frontline-knowledge-base).

| Variable | Purpose |
| --- | --- |
| `NEXT_PUBLIC_ERXES_API_URL` | Gateway URL, e.g. `https://officenext.erxes.io/gateway` |
| `NEXT_PUBLIC_APP_VERSION` | `OS` (default) or `SAAS`. On `SAAS`, a `<subdomain>` placeholder in the API URL is filled from the request host; one container serves a help center per tenant |

```dotenv
NEXT_PUBLIC_ERXES_API_URL=https://officenext.erxes.io/gateway
NEXT_PUBLIC_APP_VERSION=OS
```

## Runtime configuration and Docker

`NEXT_PUBLIC_*` values are inlined into the client bundle at `next build`, which would tie an image to one gateway. To avoid that, [`docker-entrypoint.sh`](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/apps/help-center/docker-entrypoint.sh) writes `public/js/env.js` from the container environment before the server starts, the root layout loads that file first, and [`modules/apollo/utils/env.ts`](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/apps/help-center/modules/apollo/utils/env.ts) reads `window.env` at run time. `next dev`/`next start` read `.env.local` the usual way.

Build from the **repository root** (the app compiles form controls from the `erxes-ui` source tree):

```bash
docker build -f apps/help-center/Dockerfile -t erxes/help-center .

docker run -p 3900:3900 \
  -e NEXT_PUBLIC_ERXES_API_URL=https://officenext.erxes.io/gateway \
  erxes/help-center
```

The image uses Next's `standalone` output; `PORT` (default `3900`) and `HOSTNAME` (default `0.0.0.0`) are read at start. Behind a proxy, forward the original host; that is what the help center lookup matches on:

```nginx
proxy_set_header X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Proto $scheme;
```

On a SaaS install, combine `NEXT_PUBLIC_APP_VERSION=SAAS` with a placeholder gateway like `https://<subdomain>.api.erxes.io/gateway`; a request to `acme.help.example.com` then talks to `acme`'s gateway.

## Routes

| Route | Backing data |
| --- | --- |
| `/` | Portal landing: ticket actions, announcements, KB sections |
| `/knowledge-base`, `/knowledge-base/category/[id]`, `/knowledge-base/article/[id]` | `cpKnowledgeBaseTopicDetail` |
| `/search?q=` | KB articles + CMS announcements, labelled by type |
| `/tickets`, `/tickets/new`, `/tickets/track`, `/tickets/[id]` | `cpGetTickets`, `cpCreateTicket`, `cpGetTicket`, `cpTicketGetNotes` |
| `/announcements`, `/announcements/[slug]` | `cpPostList`, `cpPost` |
| `/account`, `/sign-in`, `/sign-up` | `clientPortalUser*` mutations |

The whole app is rendered per request. `app/layout.tsx` exports `dynamic = 'force-dynamic'`, so config, KB content, and announcements are fetched fresh on every hit rather than statically cached. When the app runs against an older gateway, `modules/config/api.ts` retries the config query without the header/footer block and then with a `domain` argument, so a newer portal image still works against older gateways ([query](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/apps/help-center/modules/config/graphql/queries/helpCenterConfig.ts)).

## Check it works

1. Point the app at your gateway, open `/`, and confirm the portal name and sections come from your help center record, not from env.
2. Change the help center's Website to a different domain and confirm requests on that host find the other config.
3. Submit a ticket (`/tickets/new`) and confirm it lands in the configured pipeline; track it from `/tickets/track` and as a signed-in portal user from `/tickets`.
4. In the container, unset `NEXT_PUBLIC_ERXES_API_URL` and confirm pages fail fast rather than silently hitting a stale build-time URL.

## Troubleshooting

- **"No help center" page**: the request host does not match any help center's Website field, or the proxy dropped `X-Forwarded-Host`.
- **KB renders but tickets/announcements 401**: the config's `erxesAppToken` is stale; re-pick the portal in the Website field to re-store a current token.
- **Wrong tenant's content on SaaS**: `NEXT_PUBLIC_APP_VERSION` is not `SAAS`, or the API URL lacks the `<subdomain>` placeholder.
- **Config errors after upgrading the image only**: the gateway predates the header/footer fields; the app retries automatically, so upgrade the backend rather than pinning the image.

