the package is now served from npm; removed copy-latest related code
This commit is contained in:
155
README.md
155
README.md
@@ -1,15 +1,17 @@
|
||||
# PHX Frontend Plugin Demo
|
||||
|
||||
> [Deutsche Version](README.DE.md)
|
||||
|
||||
Example project showing how to build a **PHX ERP frontend plugin** as an Angular web component, using `@phx-erp/shared` and `@phx-erp/shared-ui`.
|
||||
|
||||
**Starting a new plugin?** Use [@phx-erp/create-phx-frontend-plugin](https://www.npmjs.com/package/@phx-erp/create-phx-frontend-plugin) to scaffold a project from the official template. This repository is a full demo you can explore, clone, or use as a reference.
|
||||
|
||||
The same codebase supports two workflows:
|
||||
|
||||
| Mode | Purpose | Typical command |
|
||||
|------|---------|-----------------|
|
||||
| **Plugin host** | Run inside PHX as a custom element | `yarn run plugin` |
|
||||
| **Standalone client** | Develop locally like a normal Angular app | `yarn client` |
|
||||
|
||||
| Mode | Purpose | Typical command |
|
||||
| --------------------- | ----------------------------------------- | ----------------- |
|
||||
| **Plugin host** | Run inside PHX as a custom element | `yarn run plugin` |
|
||||
| **Standalone client** | Develop locally like a normal Angular app | `yarn client` |
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -22,6 +24,7 @@ The same codebase supports two workflows:
|
||||
- [Development modes](#development-modes)
|
||||
- [Project structure](#project-structure)
|
||||
- [Create your own plugin](#create-your-own-plugin)
|
||||
- [Scaffold CLI (recommended)](#scaffold-cli-recommended)
|
||||
- [1. Basic setup](#1-basic-setup)
|
||||
- [2. Tailwind CSS](#2-tailwind-css)
|
||||
- [3. GraphQL Codegen](#3-graphql-codegen)
|
||||
@@ -77,26 +80,21 @@ For local standalone development you also need either:
|
||||
|
||||
## Quick start
|
||||
|
||||
These steps apply to **this demo repository**. To create your own plugin from scratch, use the scaffold CLI — see [Create your own plugin](#create-your-own-plugin).
|
||||
|
||||
1. **Install dependencies:**
|
||||
|
||||
```bash
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
```
|
||||
2. **Create a local development environment** (optional, for standalone mode):
|
||||
|
||||
```bash
|
||||
```bash
|
||||
cp src/environments/environment.example.ts src/environments/environment.development.ts
|
||||
```
|
||||
|
||||
```
|
||||
Adjust `apiUrl`, `wsUrl`, and optionally `apiKey` in that file. The file is gitignored.
|
||||
|
||||
3. **Generate GraphQL types** (requires PHX admin API at the schema URL configured in `codegen.ts`):
|
||||
|
||||
```bash
|
||||
```bash
|
||||
yarn codegen
|
||||
```
|
||||
|
||||
```
|
||||
4. **Run in one of the development modes** below.
|
||||
|
||||
---
|
||||
@@ -106,10 +104,10 @@ For local standalone development you also need either:
|
||||
To use this demo in your own instance, add a custom element in **Admin → Custom Elements** (`https://your.phx.instance/admin/customElements`) with this manifest URL:
|
||||
|
||||
```
|
||||
https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/manifest.json
|
||||
https://unpkg.com/@phx-erp/phx-frontend-plugin-demo/manifest.json
|
||||
```
|
||||
|
||||
The manifest resolves to the compiled `main.js` in the `latest/` directory of this repository.
|
||||
The manifest's `path` field points at the package root; unpkg serves `main.js` via the `"."` export. For local development with `yarn run plugin`, register `http://localhost:3223/local.manifest.json` in Admin → Custom Elements instead.
|
||||
|
||||
---
|
||||
|
||||
@@ -124,7 +122,7 @@ yarn client
|
||||
# equivalent: ng serve --port 4201 --watch --configuration development
|
||||
```
|
||||
|
||||
Open **http://localhost:4201/**.
|
||||
Open **[http://localhost:4201/](http://localhost:4201/)**.
|
||||
|
||||
### Plugin host mode (inside PHX)
|
||||
|
||||
@@ -136,18 +134,20 @@ yarn run plugin
|
||||
|
||||
This runs `yarn build` and `yarn serve` concurrently:
|
||||
|
||||
| Script | What it does |
|
||||
|--------|----------------|
|
||||
| `yarn build` | Watches production build (in this repo, also syncs output to `latest/` for the hosted demo) |
|
||||
| `yarn serve` | Serves `dist/.../browser/` at **http://localhost:3223/** |
|
||||
|
||||
| Script | What it does |
|
||||
| ------------ | ---------------------------------------------------------------------------------- |
|
||||
| `yarn build` | Watches production build |
|
||||
| `yarn serve` | Serves `dist/.../browser/` at **[http://localhost:3223/](http://localhost:3223/)** |
|
||||
|
||||
|
||||
Point your PHX instance at the local manifest:
|
||||
|
||||
```
|
||||
http://localhost:3223/manifest.json
|
||||
http://localhost:3223/local.manifest.json
|
||||
```
|
||||
|
||||
(`public/manifest.json` is copied into the served output.)
|
||||
(`public/local.manifest.json` is copied into the served output.)
|
||||
|
||||
> **Note:** Use `yarn run plugin`, not `yarn plugin` — Yarn treats `plugin` as a built-in command.
|
||||
|
||||
@@ -166,16 +166,15 @@ yarn codegen # Regenerate GraphQL types
|
||||
|
||||
```
|
||||
phx-frontend-plugin-demo/
|
||||
├── latest/ # Published build output (main.js, manifest.json)
|
||||
├── public/
|
||||
│ └── manifest.json # Local manifest (path → localhost:3223)
|
||||
│ └── local.manifest.json # Local dev manifest (copied into dist)
|
||||
├── manifest.json # Published manifest (npm/unpkg)
|
||||
├── scripts/
|
||||
│ ├── copy-latest.mjs # (this repo only) Sync dist/*.js → latest/ for hosted demo
|
||||
│ └── serve-dist.mjs # Static server for plugin-host dev
|
||||
├── src/
|
||||
│ ├── app/
|
||||
│ │ ├── components/ # Demo pages (hello-world, product-view, …)
|
||||
│ │ ├── login.ts # Auth callback route (standalone development)
|
||||
│ │ ├── login.ts # Auth callback route — redirect flow (standalone development)
|
||||
│ │ ├── services/
|
||||
│ │ │ ├── apollo.service.ts
|
||||
│ │ │ └── phoenix-host-bridge.service.ts
|
||||
@@ -198,7 +197,23 @@ phx-frontend-plugin-demo/
|
||||
|
||||
## Create your own plugin
|
||||
|
||||
The steps below walk through creating a project similar to this demo, using [Yarn v4](https://yarnpkg.com/getting-started/install), [Angular 20](https://angular.dev/), [PrimeNG 20](https://primeng.org/), and [Tailwind CSS](https://tailwindcss.com/).
|
||||
### Scaffold CLI (recommended)
|
||||
|
||||
The fastest way to start is the official scaffold CLI, published as [@phx-erp/create-phx-frontend-plugin](https://www.npmjs.com/package/@phx-erp/create-phx-frontend-plugin):
|
||||
|
||||
```bash
|
||||
# Yarn Berry — one-shot, no global install
|
||||
yarn dlx @phx-erp/create-phx-frontend-plugin my-orders-plugin
|
||||
|
||||
# npm
|
||||
npx @phx-erp/create-phx-frontend-plugin@latest my-orders-plugin
|
||||
```
|
||||
|
||||
This copies the official plugin template, replaces placeholders (project name, custom element tag, API URLs), creates `environment.development.ts`, and can run `yarn install`. See the [package page](https://www.npmjs.com/package/@phx-erp/create-phx-frontend-plugin) for non-interactive flags (`--yes`, `--install`, `--api-url`, `--ui-url`, `--api-key`, etc.).
|
||||
|
||||
### Manual setup (step by step)
|
||||
|
||||
If you prefer to understand each piece or customize from scratch, the steps below walk through creating a project similar to this demo, using [Yarn v4](https://yarnpkg.com/getting-started/install), [Angular 20](https://angular.dev/), [PrimeNG 20](https://primeng.org/), and [Tailwind CSS](https://tailwindcss.com/).
|
||||
|
||||
Replace placeholders such as `*PROJECT-NAME*`, `*YOUR-TAG*`, and `*YOUR-TOKEN*` with your values.
|
||||
|
||||
@@ -307,7 +322,7 @@ This generates `src/app/schema-types.ts` and `src/app/generated.ts`.
|
||||
|
||||
Use separate environments so the same build runs as a PHX plugin (production) or as a standalone dev app (development).
|
||||
|
||||
**`src/environments/environment.interface.ts`**
|
||||
`**src/environments/environment.interface.ts**`
|
||||
|
||||
```ts
|
||||
export abstract class Environment {
|
||||
@@ -319,7 +334,7 @@ export abstract class Environment {
|
||||
}
|
||||
```
|
||||
|
||||
**`src/environments/environment.ts`** (production — used when embedded in PHX)
|
||||
`**src/environments/environment.ts**` (production — used when embedded in PHX)
|
||||
|
||||
```ts
|
||||
import { Environment } from './environment.interface';
|
||||
@@ -333,7 +348,7 @@ export const environment: Environment = {
|
||||
};
|
||||
```
|
||||
|
||||
**`src/environments/environment.development.ts`** (local development — add to `.gitignore`)
|
||||
`**src/environments/environment.development.ts**` (local development — add to `.gitignore`)
|
||||
|
||||
```ts
|
||||
import { Environment } from './environment.interface';
|
||||
@@ -488,8 +503,8 @@ bootstrapPhoenixPluginCustomElement(App, '*YOUR-TAG*', appConfig).then((app) =>
|
||||
});
|
||||
```
|
||||
|
||||
- **`bootstrapPhoenixPluginCustomElement`** — creates the Angular application and registers the custom element for PHX.
|
||||
- **`app.bootstrap(App)` in development** — additionally mounts the root component so `ng serve` / standalone mode works with routing.
|
||||
- `bootstrapPhoenixPluginCustomElement` — creates the Angular application and registers the custom element for PHX.
|
||||
- `app.bootstrap(App)` in development — additionally mounts the root component so `ng serve` / standalone mode works with routing.
|
||||
|
||||
The custom element tag must be **lowercase with hyphens** (e.g. `my-company-orders`). Use the same tag in your manifest.
|
||||
|
||||
@@ -510,12 +525,14 @@ PHX loads a JSON manifest that points to your entry script and declares the cust
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| `path` | Absolute URL to `main.js` (and base for chunk resolution) |
|
||||
| `items[].tagName` | Custom element tag registered in `main.ts` |
|
||||
|
||||
**Local development example** (`public/manifest.json` in this repo):
|
||||
| Field | Description |
|
||||
| ----------------- | --------------------------------------------------------- |
|
||||
| `path` | Absolute URL to `main.js` (and base for chunk resolution) |
|
||||
| `items[].tagName` | Custom element tag registered in `main.ts` |
|
||||
|
||||
|
||||
**Local development example** (`public/local.manifest.json` in this repo):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -524,11 +541,11 @@ PHX loads a JSON manifest that points to your entry script and declares the cust
|
||||
}
|
||||
```
|
||||
|
||||
**Hosted example** (`latest/manifest.json` in this repo):
|
||||
**Published example** (root `manifest.json`, served from npm via unpkg):
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/main.js",
|
||||
"path": "https://unpkg.com/@phx-erp/phx-frontend-plugin-demo",
|
||||
"items": [{ "tagName": "frontend-plugin-demo" }]
|
||||
}
|
||||
```
|
||||
@@ -538,7 +555,7 @@ PHX loads a JSON manifest that points to your entry script and declares the cust
|
||||
1. Host `main.js` (and any sibling chunks) at a URL reachable from your users' browsers — same network rules as your PHX instance.
|
||||
2. Publish a manifest JSON at a stable URL.
|
||||
3. In PHX: **Admin → Custom Elements** → add the manifest URL and choose a mount path or tag.
|
||||
4. Log out and back in. The plugin is available at `https://your.phx.instance/customElements/*PATH*`.
|
||||
4. Log out and back in. The plugin is available at `https://your.phx.instance/customElements/*PATH`*.
|
||||
|
||||
For local plugin-host development, see [Serving the plugin locally](#serving-the-plugin-locally).
|
||||
|
||||
@@ -554,7 +571,7 @@ Install the static server:
|
||||
yarn add -D serve
|
||||
```
|
||||
|
||||
**`scripts/serve-dist.mjs`**
|
||||
`**scripts/serve-dist.mjs**`
|
||||
|
||||
```js
|
||||
import { spawn } from 'node:child_process';
|
||||
@@ -575,7 +592,7 @@ const child = spawn(serveBin, ['-l', port, '--cors', '--no-etag'], {
|
||||
child.on('exit', (code) => process.exit(code ?? 0));
|
||||
```
|
||||
|
||||
**`serve.json`** (replace `*PROJECT-NAME*` with your Angular project name from `angular.json`)
|
||||
`**serve.json**` (replace `*PROJECT-NAME*` with your Angular project name from `angular.json`)
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -601,8 +618,6 @@ Add to `package.json`:
|
||||
|
||||
Use a custom port: `PORT=8080 yarn serve`.
|
||||
|
||||
> **This repository only:** `scripts/copy-latest.mjs` copies built JS files into `latest/` so the [live demo](#live-demo) manifest can point at a stable path in git. You do not need this for local plugin-host development.
|
||||
|
||||
### Apollo, auth guard, and login
|
||||
|
||||
In **production** (embedded in PHX), authentication is handled by the host. Use the Apollo client from `IPluginServices` via `PhoenixHostBridgeService` — you do not need a separate login flow.
|
||||
@@ -650,9 +665,9 @@ Apply the same pattern for other host services (e.g. notifications) when you nee
|
||||
|
||||
#### Login callback route
|
||||
|
||||
For development without a preset API key, add a `/login` route that acts as an **auth callback** — not a login form. When the user is unauthenticated, the auth guard sends them to the PHX login page (`environment.serverUrl`). After successful login, PHX redirects back to your plugin's `/login` route with an `authToken` query parameter and a base64-encoded `redirectTo` target.
|
||||
For development without a preset API key, you need a way to obtain and store an API token. This demo uses a **redirect-based auth callback** on `/login` rather than an embedded login form, but either approach is valid — you can also build a login form in your plugin, call the GraphQL `login` mutation, and handle the token yourself.
|
||||
|
||||
The callback component stores the token in `localStorage` and navigates back to the original page:
|
||||
With the redirect flow, when the user is unauthenticated, the auth guard sends them to the PHX login page (`environment.serverUrl`). After successful login, PHX redirects back to your plugin's `/login` route with an `authToken` query parameter and a base64-encoded `redirectTo` target. The callback component stores the token in `localStorage` and navigates back to the original page:
|
||||
|
||||
```ts
|
||||
@Component({
|
||||
@@ -730,32 +745,34 @@ Recommended scripts after following this guide:
|
||||
}
|
||||
```
|
||||
|
||||
| Script | Description |
|
||||
|--------|-------------|
|
||||
| `build` | Production watch build for PHX |
|
||||
| `serve` | Serves compiled assets for PHX (default port 3223) |
|
||||
| `plugin` | Runs `build` + `serve` together — use `yarn run plugin` |
|
||||
| `client` | Standalone Angular dev server on port 4201 |
|
||||
| `codegen` | Regenerates GraphQL TypeScript types |
|
||||
|
||||
This repository additionally runs `copy-latest.mjs` alongside `build` to publish artifacts into `latest/` for the hosted demo — that is not required for your own plugin.
|
||||
| Script | Description |
|
||||
| --------- | ------------------------------------------------------- |
|
||||
| `build` | Production watch build for PHX |
|
||||
| `serve` | Serves compiled assets for PHX (default port 3223) |
|
||||
| `plugin` | Runs `build` + `serve` together — use `yarn run plugin` |
|
||||
| `client` | Standalone Angular dev server on port 4201 |
|
||||
| `codegen` | Regenerates GraphQL TypeScript types |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Things to check |
|
||||
|---------|-----------------|
|
||||
| PHX shows an old version of the plugin | Hard-refresh; confirm `serve.json` sets `Cache-Control: no-store` for JS; restart `yarn run plugin` |
|
||||
|
||||
| Symptom | Things to check |
|
||||
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||
| PHX shows an old version of the plugin | Hard-refresh; confirm `serve.json` sets `Cache-Control: no-store` for JS; restart `yarn run plugin` |
|
||||
| `401` / GraphQL auth errors in standalone mode | Set `apiKey` in `environment.development.ts`, or ensure `serverUrl` points to your PHX instance so the login redirect works |
|
||||
| `yarn add @phx-erp/shared` fails | Check network access to the public npm registry; retry `yarn install` |
|
||||
| Routing broken inside PHX | Add route segments to `stripTrailingSegments` in `providePhoenixPluginWithPrimeNG` |
|
||||
| Tailwind classes missing in a component | Add `@tailwind` directives to that component's `styles` |
|
||||
| `yarn codegen` fails | Ensure PHX is running and the schema URL in `codegen.ts` is reachable |
|
||||
| Custom element not found | Tag in manifest must exactly match `customElements.define` / `bootstrapPhoenixPluginCustomElement` |
|
||||
| `yarn add @phx-erp/shared` fails | Check network access to the public npm registry; retry `yarn install` |
|
||||
| Routing broken inside PHX | Add route segments to `stripTrailingSegments` in `providePhoenixPluginWithPrimeNG` |
|
||||
| Tailwind classes missing in a component | Add `@tailwind` directives to that component's `styles` |
|
||||
| `yarn codegen` fails | Ensure PHX is running and the schema URL in `codegen.ts` is reachable |
|
||||
| Custom element not found | Tag in manifest must exactly match `customElements.define` / `bootstrapPhoenixPluginCustomElement` |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For integration questions or PHX-specific APIs, contact your PHX partner.
|
||||
For integration questions or PHX-specific APIs, contact your PHX partner.
|
||||
Reference in New Issue
Block a user