Compare commits
1 Commits
public-pac
...
7dd99548c8
| Author | SHA1 | Date | |
|---|---|---|---|
| 7dd99548c8 |
159
README.DE.md
159
README.DE.md
@@ -4,16 +4,12 @@
|
|||||||
|
|
||||||
Beispielprojekt, das zeigt, wie ein **PHX-ERP-Frontend-Plugin** als Angular-Web-Component mit `@phx-erp/shared` und `@phx-erp/shared-ui` erstellt wird.
|
Beispielprojekt, das zeigt, wie ein **PHX-ERP-Frontend-Plugin** als Angular-Web-Component mit `@phx-erp/shared` und `@phx-erp/shared-ui` erstellt wird.
|
||||||
|
|
||||||
**Neues Plugin starten?** Nutzen Sie [@phx-erp/create-phx-frontend-plugin](https://www.npmjs.com/package/@phx-erp/create-phx-frontend-plugin), um ein Projekt aus dem offiziellen Template zu erzeugen. Dieses Repository ist eine vollständige Demo zum Erkunden, Klonen oder als Referenz.
|
|
||||||
|
|
||||||
Dieselbe Codebasis unterstützt zwei Arbeitsweisen:
|
Dieselbe Codebasis unterstützt zwei Arbeitsweisen:
|
||||||
|
|
||||||
|
| Modus | Zweck | Typischer Befehl |
|
||||||
| Modus | Zweck | Typischer Befehl |
|
|-------|-------|------------------|
|
||||||
| --------------------- | ----------------------------------------------- | ----------------- |
|
| **Plugin host** | Ausführung in PHX als Custom Element | `yarn run plugin` |
|
||||||
| **Plugin host** | Ausführung in PHX als Custom Element | `yarn run plugin` |
|
| **Standalone client** | Lokale Entwicklung wie eine normale Angular-App | `yarn client` |
|
||||||
| **Standalone client** | Lokale Entwicklung wie eine normale Angular-App | `yarn client` |
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,7 +22,6 @@ Dieselbe Codebasis unterstützt zwei Arbeitsweisen:
|
|||||||
- [Entwicklungsmodi](#entwicklungsmodi)
|
- [Entwicklungsmodi](#entwicklungsmodi)
|
||||||
- [Projektstruktur](#projektstruktur)
|
- [Projektstruktur](#projektstruktur)
|
||||||
- [Eigenes Plugin erstellen](#eigenes-plugin-erstellen)
|
- [Eigenes Plugin erstellen](#eigenes-plugin-erstellen)
|
||||||
- [Scaffold-CLI (empfohlen)](#scaffold-cli-empfohlen)
|
|
||||||
- [1. Grundsetup](#1-grundsetup)
|
- [1. Grundsetup](#1-grundsetup)
|
||||||
- [2. Tailwind CSS](#2-tailwind-css)
|
- [2. Tailwind CSS](#2-tailwind-css)
|
||||||
- [3. GraphQL Codegen](#3-graphql-codegen)
|
- [3. GraphQL Codegen](#3-graphql-codegen)
|
||||||
@@ -47,10 +42,10 @@ Dieselbe Codebasis unterstützt zwei Arbeitsweisen:
|
|||||||
Ein PHX-Frontend-Plugin ist eine Angular-Anwendung, die als **Custom Element** (Web Component) verpackt wird. PHX lädt es über ein **Manifest**, das auf die kompilierte `main.js` verweist und den Element-Tag-Namen deklariert.
|
Ein PHX-Frontend-Plugin ist eine Angular-Anwendung, die als **Custom Element** (Web Component) verpackt wird. PHX lädt es über ein **Manifest**, das auf die kompilierte `main.js` verweist und den Element-Tag-Namen deklariert.
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────┐ manifest.json ┌───────────────────┐
|
┌─────────────────┐ manifest.json ┌──────────────────┐
|
||||||
│ PHX host │ ─────────────────────► │ main.js (+ deps) │
|
│ PHX host │ ─────────────────────► │ main.js (+ deps) │
|
||||||
│ (ERP shell) │ loads & registers │ custom element │
|
│ (ERP shell) │ loads & registers │ custom element │
|
||||||
└────────┬────────┘ └────────┬──────────┘
|
└────────┬────────┘ └────────┬─────────┘
|
||||||
│ │
|
│ │
|
||||||
│ pluginServices (Apollo, notifications…) │
|
│ pluginServices (Apollo, notifications…) │
|
||||||
│ hostInjector │
|
│ hostInjector │
|
||||||
@@ -82,21 +77,26 @@ Für die lokale eigenständige Entwicklung benötigen Sie außerdem eines der fo
|
|||||||
|
|
||||||
## Schnellstart
|
## Schnellstart
|
||||||
|
|
||||||
Diese Schritte gelten für **dieses Demo-Repository**. Um ein eigenes Plugin von Grund auf zu erstellen, nutzen Sie die Scaffold-CLI — siehe [Eigenes Plugin erstellen](#eigenes-plugin-erstellen).
|
|
||||||
|
|
||||||
1. **Abhängigkeiten installieren:**
|
1. **Abhängigkeiten installieren:**
|
||||||
```bash
|
|
||||||
|
```bash
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Lokale Development-Umgebung anlegen** (optional, für den Standalone-Modus):
|
2. **Lokale Development-Umgebung anlegen** (optional, für den Standalone-Modus):
|
||||||
```bash
|
|
||||||
|
```bash
|
||||||
cp src/environments/environment.example.ts src/environments/environment.development.ts
|
cp src/environments/environment.example.ts src/environments/environment.development.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
Passen Sie `apiUrl`, `wsUrl` und optional `apiKey` in dieser Datei an. Die Datei ist gitignored.
|
Passen Sie `apiUrl`, `wsUrl` und optional `apiKey` in dieser Datei an. Die Datei ist gitignored.
|
||||||
|
|
||||||
3. **GraphQL-Typen generieren** (erfordert PHX Admin API unter der in `codegen.ts` konfigurierten Schema-URL):
|
3. **GraphQL-Typen generieren** (erfordert PHX Admin API unter der in `codegen.ts` konfigurierten Schema-URL):
|
||||||
```bash
|
|
||||||
|
```bash
|
||||||
yarn codegen
|
yarn codegen
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **In einem der Entwicklungsmodi** unten starten.
|
4. **In einem der Entwicklungsmodi** unten starten.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -106,10 +106,10 @@ Diese Schritte gelten für **dieses Demo-Repository**. Um ein eigenes Plugin von
|
|||||||
Um diese Demo in Ihrer eigenen Instanz zu nutzen, fügen Sie unter **Admin → Custom Elements** (`https://your.phx.instance/admin/customElements`) ein Custom Element mit folgender Manifest-URL hinzu:
|
Um diese Demo in Ihrer eigenen Instanz zu nutzen, fügen Sie unter **Admin → Custom Elements** (`https://your.phx.instance/admin/customElements`) ein Custom Element mit folgender Manifest-URL hinzu:
|
||||||
|
|
||||||
```
|
```
|
||||||
https://unpkg.com/@phx-erp/phx-frontend-plugin-demo/manifest.json
|
https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/manifest.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Das `path`-Feld des Manifests verweist auf die Paketwurzel; unpkg liefert `main.js` über den `"."`-Export. Für die lokale Entwicklung mit `yarn run plugin` registrieren Sie stattdessen `http://localhost:3223/local.manifest.json` unter Admin → Custom Elements.
|
Das Manifest verweist auf die kompilierte `main.js` im Verzeichnis `latest/` dieses Repositorys.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ yarn client
|
|||||||
# equivalent: ng serve --port 4201 --watch --configuration development
|
# equivalent: ng serve --port 4201 --watch --configuration development
|
||||||
```
|
```
|
||||||
|
|
||||||
Öffnen Sie **[http://localhost:4201/](http://localhost:4201/)**.
|
Öffnen Sie **http://localhost:4201/**.
|
||||||
|
|
||||||
### Plugin-Host-Modus (innerhalb von PHX)
|
### Plugin-Host-Modus (innerhalb von PHX)
|
||||||
|
|
||||||
@@ -136,20 +136,18 @@ yarn run plugin
|
|||||||
|
|
||||||
Dies führt `yarn build` und `yarn serve` parallel aus:
|
Dies führt `yarn build` und `yarn serve` parallel aus:
|
||||||
|
|
||||||
|
| Skript | Funktion |
|
||||||
| Skript | Funktion |
|
|--------|----------|
|
||||||
| ------------ | -------------------------------------------------------------------------------------------- |
|
| `yarn build` | Production-Watch-Build (in diesem Repo zusätzlich Sync nach `latest/` für die gehostete Demo) |
|
||||||
| `yarn build` | Production-Watch-Build |
|
| `yarn serve` | Stellt `dist/.../browser/` unter **http://localhost:3223/** bereit |
|
||||||
| `yarn serve` | Stellt `dist/.../browser/` unter **[http://localhost:3223/](http://localhost:3223/)** bereit |
|
|
||||||
|
|
||||||
|
|
||||||
Verweisen Sie Ihre PHX-Instanz auf das lokale Manifest:
|
Verweisen Sie Ihre PHX-Instanz auf das lokale Manifest:
|
||||||
|
|
||||||
```
|
```
|
||||||
http://localhost:3223/local.manifest.json
|
http://localhost:3223/manifest.json
|
||||||
```
|
```
|
||||||
|
|
||||||
(`public/local.manifest.json` wird in die bereitgestellte Ausgabe kopiert.)
|
(`public/manifest.json` wird in die bereitgestellte Ausgabe kopiert.)
|
||||||
|
|
||||||
> **Hinweis:** Verwenden Sie `yarn run plugin`, nicht `yarn plugin` — Yarn behandelt `plugin` als eingebauten Befehl.
|
> **Hinweis:** Verwenden Sie `yarn run plugin`, nicht `yarn plugin` — Yarn behandelt `plugin` als eingebauten Befehl.
|
||||||
|
|
||||||
@@ -168,15 +166,16 @@ yarn codegen # Regenerate GraphQL types
|
|||||||
|
|
||||||
```
|
```
|
||||||
phx-frontend-plugin-demo/
|
phx-frontend-plugin-demo/
|
||||||
|
├── latest/ # Published build output (main.js, manifest.json)
|
||||||
├── public/
|
├── public/
|
||||||
│ └── local.manifest.json # Lokales Dev-Manifest (in dist kopiert)
|
│ └── manifest.json # Local manifest (path → localhost:3223)
|
||||||
├── manifest.json # Veröffentlichtes Manifest (npm/unpkg)
|
|
||||||
├── scripts/
|
├── scripts/
|
||||||
|
│ ├── copy-latest.mjs # (this repo only) Sync dist/*.js → latest/ for hosted demo
|
||||||
│ └── serve-dist.mjs # Static server for plugin-host dev
|
│ └── serve-dist.mjs # Static server for plugin-host dev
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── app/
|
│ ├── app/
|
||||||
│ │ ├── components/ # Demo pages (hello-world, product-view, …)
|
│ │ ├── components/ # Demo pages (hello-world, product-view, …)
|
||||||
│ │ ├── login.ts # Auth-Callback-Route — Weiterleitungsflow (Standalone-Entwicklung)
|
│ │ ├── login.ts # Auth-Callback-Route (Standalone-Entwicklung)
|
||||||
│ │ ├── services/
|
│ │ ├── services/
|
||||||
│ │ │ ├── apollo.service.ts
|
│ │ │ ├── apollo.service.ts
|
||||||
│ │ │ └── phoenix-host-bridge.service.ts
|
│ │ │ └── phoenix-host-bridge.service.ts
|
||||||
@@ -199,23 +198,7 @@ phx-frontend-plugin-demo/
|
|||||||
|
|
||||||
## Eigenes Plugin erstellen
|
## Eigenes Plugin erstellen
|
||||||
|
|
||||||
### Scaffold-CLI (empfohlen)
|
Die folgenden Schritte führen durch die Erstellung eines Projekts, das dieser Demo ähnelt, mit [Yarn v4](https://yarnpkg.com/getting-started/install), [Angular 20](https://angular.dev/), [PrimeNG 20](https://primeng.org/) und [Tailwind CSS](https://tailwindcss.com/).
|
||||||
|
|
||||||
Der schnellste Weg ist die offizielle Scaffold-CLI, veröffentlicht als [@phx-erp/create-phx-frontend-plugin](https://www.npmjs.com/package/@phx-erp/create-phx-frontend-plugin):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Yarn Berry — einmalig, ohne globale Installation
|
|
||||||
yarn dlx @phx-erp/create-phx-frontend-plugin
|
|
||||||
|
|
||||||
# npm
|
|
||||||
npx @phx-erp/create-phx-frontend-plugin@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
Dabei wird das offizielle Plugin-Template kopiert, Platzhalter ersetzt (Projektname, Custom-Element-Tag, API-URLs), `environment.development.ts` angelegt und optional `yarn install` ausgeführt. Nicht-interaktive Flags (`--yes`, `--install`, `--api-url`, `--ui-url`, `--api-key` usw.) sind auf der [Paketseite](https://www.npmjs.com/package/@phx-erp/create-phx-frontend-plugin) dokumentiert.
|
|
||||||
|
|
||||||
### Manuelles Setup (Schritt für Schritt)
|
|
||||||
|
|
||||||
Wenn Sie jeden Schritt nachvollziehen oder von Grund auf anpassen möchten, führen die folgenden Abschnitte durch die Erstellung eines Projekts, das dieser Demo ähnelt, mit [Yarn v4](https://yarnpkg.com/getting-started/install), [Angular 20](https://angular.dev/), [PrimeNG 20](https://primeng.org/) und [Tailwind CSS](https://tailwindcss.com/).
|
|
||||||
|
|
||||||
Ersetzen Sie Platzhalter wie `*PROJECT-NAME*`, `*YOUR-TAG*` und `*YOUR-TOKEN*` durch Ihre Werte.
|
Ersetzen Sie Platzhalter wie `*PROJECT-NAME*`, `*YOUR-TAG*` und `*YOUR-TOKEN*` durch Ihre Werte.
|
||||||
|
|
||||||
@@ -263,7 +246,7 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Aufgrund eines bekannten Problems müssen diese Direktiven in den `**styles` jeder Komponente** (und in `src/styles.scss` für die lokale Entwicklung) ergänzt werden:
|
Aufgrund eines bekannten Problems müssen diese Direktiven in den **`styles` jeder Komponente** (und in `src/styles.scss` für die lokale Entwicklung) ergänzt werden:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@@ -324,7 +307,7 @@ Dies erzeugt `src/app/schema-types.ts` und `src/app/generated.ts`.
|
|||||||
|
|
||||||
Verwenden Sie getrennte Environments, damit derselbe Build als PHX-Plugin (Production) oder als eigenständige Dev-App (Development) läuft.
|
Verwenden Sie getrennte Environments, damit derselbe Build als PHX-Plugin (Production) oder als eigenständige Dev-App (Development) läuft.
|
||||||
|
|
||||||
`**src/environments/environment.interface.ts**`
|
**`src/environments/environment.interface.ts`**
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export abstract class Environment {
|
export abstract class Environment {
|
||||||
@@ -336,7 +319,7 @@ export abstract class Environment {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`**src/environments/environment.ts**` (Production — verwendet bei Einbettung in PHX)
|
**`src/environments/environment.ts`** (Production — verwendet bei Einbettung in PHX)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Environment } from './environment.interface';
|
import { Environment } from './environment.interface';
|
||||||
@@ -350,7 +333,7 @@ export const environment: Environment = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
`**src/environments/environment.development.ts**` (lokale Entwicklung — in `.gitignore` aufnehmen)
|
**`src/environments/environment.development.ts`** (lokale Entwicklung — in `.gitignore` aufnehmen)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { Environment } from './environment.interface';
|
import { Environment } from './environment.interface';
|
||||||
@@ -505,8 +488,8 @@ bootstrapPhoenixPluginCustomElement(App, '*YOUR-TAG*', appConfig).then((app) =>
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- `bootstrapPhoenixPluginCustomElement` — erstellt die Angular-Anwendung und registriert das Custom Element für PHX.
|
- **`bootstrapPhoenixPluginCustomElement`** — erstellt die Angular-Anwendung und registriert das Custom Element für PHX.
|
||||||
- `app.bootstrap(App)` in Development — mountet zusätzlich die Root-Komponente, damit `ng serve` / der Standalone-Modus mit Routing funktioniert.
|
- **`app.bootstrap(App)` in Development** — mountet zusätzlich die Root-Komponente, damit `ng serve` / der Standalone-Modus mit Routing funktioniert.
|
||||||
|
|
||||||
Der Custom-Element-Tag muss **kleingeschrieben mit Bindestrichen** sein (z. B. `my-company-orders`). Verwenden Sie denselben Tag in Ihrem Manifest.
|
Der Custom-Element-Tag muss **kleingeschrieben mit Bindestrichen** sein (z. B. `my-company-orders`). Verwenden Sie denselben Tag in Ihrem Manifest.
|
||||||
|
|
||||||
@@ -527,14 +510,12 @@ PHX lädt ein JSON-Manifest, das auf Ihr Entry-Skript verweist und den Custom-El
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
| Feld | Beschreibung |
|
||||||
|
|------|--------------|
|
||||||
|
| `path` | Absolute URL zu `main.js` (und Basis für Chunk-Auflösung) |
|
||||||
|
| `items[].tagName` | In `main.ts` registrierter Custom-Element-Tag |
|
||||||
|
|
||||||
| Feld | Beschreibung |
|
**Beispiel lokale Entwicklung** (`public/manifest.json` in diesem Repo):
|
||||||
| ----------------- | --------------------------------------------------------- |
|
|
||||||
| `path` | Absolute URL zu `main.js` (und Basis für Chunk-Auflösung) |
|
|
||||||
| `items[].tagName` | In `main.ts` registrierter Custom-Element-Tag |
|
|
||||||
|
|
||||||
|
|
||||||
**Beispiel lokale Entwicklung** (`public/local.manifest.json` in diesem Repo):
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -543,11 +524,11 @@ PHX lädt ein JSON-Manifest, das auf Ihr Entry-Skript verweist und den Custom-El
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Veröffentlichtes Beispiel** (Root-`manifest.json`, über npm/unpkg bereitgestellt):
|
**Gehostetes Beispiel** (`latest/manifest.json` in diesem Repo):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"path": "https://unpkg.com/@phx-erp/phx-frontend-plugin-demo",
|
"path": "https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/main.js",
|
||||||
"items": [{ "tagName": "frontend-plugin-demo" }]
|
"items": [{ "tagName": "frontend-plugin-demo" }]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -557,7 +538,7 @@ PHX lädt ein JSON-Manifest, das auf Ihr Entry-Skript verweist und den Custom-El
|
|||||||
1. Hosten Sie `main.js` (und ggf. weitere Chunks) unter einer URL, die aus den Browsern Ihrer Nutzer erreichbar ist — dieselben Netzwerkregeln wie für Ihre PHX-Instanz.
|
1. Hosten Sie `main.js` (und ggf. weitere Chunks) unter einer URL, die aus den Browsern Ihrer Nutzer erreichbar ist — dieselben Netzwerkregeln wie für Ihre PHX-Instanz.
|
||||||
2. Veröffentlichen Sie ein Manifest-JSON unter einer stabilen URL.
|
2. Veröffentlichen Sie ein Manifest-JSON unter einer stabilen URL.
|
||||||
3. In PHX: **Admin → Custom Elements** → Manifest-URL hinzufügen und Mount-Pfad oder Tag wählen.
|
3. In PHX: **Admin → Custom Elements** → Manifest-URL hinzufügen und Mount-Pfad oder Tag wählen.
|
||||||
4. Ab- und wieder anmelden. Das Plugin ist verfügbar unter `https://your.phx.instance/customElements/*PATH`*.
|
4. Ab- und wieder anmelden. Das Plugin ist verfügbar unter `https://your.phx.instance/customElements/*PATH*`.
|
||||||
|
|
||||||
Für die lokale Plugin-Host-Entwicklung siehe [Plugin lokal bereitstellen](#plugin-lokal-bereitstellen).
|
Für die lokale Plugin-Host-Entwicklung siehe [Plugin lokal bereitstellen](#plugin-lokal-bereitstellen).
|
||||||
|
|
||||||
@@ -573,7 +554,7 @@ Statischen Server installieren:
|
|||||||
yarn add -D serve
|
yarn add -D serve
|
||||||
```
|
```
|
||||||
|
|
||||||
`**scripts/serve-dist.mjs**`
|
**`scripts/serve-dist.mjs`**
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
@@ -594,7 +575,7 @@ const child = spawn(serveBin, ['-l', port, '--cors', '--no-etag'], {
|
|||||||
child.on('exit', (code) => process.exit(code ?? 0));
|
child.on('exit', (code) => process.exit(code ?? 0));
|
||||||
```
|
```
|
||||||
|
|
||||||
`**serve.json**` (ersetzen Sie `*PROJECT-NAME*` durch Ihren Angular-Projektnamen aus `angular.json`)
|
**`serve.json`** (ersetzen Sie `*PROJECT-NAME*` durch Ihren Angular-Projektnamen aus `angular.json`)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -620,6 +601,8 @@ In `package.json` ergänzen:
|
|||||||
|
|
||||||
Eigener Port: `PORT=8080 yarn serve`.
|
Eigener Port: `PORT=8080 yarn serve`.
|
||||||
|
|
||||||
|
> **Nur in diesem Repository:** `scripts/copy-latest.mjs` kopiert gebaute JS-Dateien nach `latest/`, damit das [Live-Demo](#live-demo)-Manifest auf einen stabilen Pfad in Git verweisen kann. Für die lokale Plugin-Host-Entwicklung ist das nicht erforderlich.
|
||||||
|
|
||||||
### Apollo, Auth Guard und Login
|
### Apollo, Auth Guard und Login
|
||||||
|
|
||||||
Im **Production**-Modus (eingebettet in PHX) übernimmt der Host die Authentifizierung. Nutzen Sie den Apollo Client aus `IPluginServices` über `PhoenixHostBridgeService` — ein separater Login-Flow ist nicht nötig.
|
Im **Production**-Modus (eingebettet in PHX) übernimmt der Host die Authentifizierung. Nutzen Sie den Apollo Client aus `IPluginServices` über `PhoenixHostBridgeService` — ein separater Login-Flow ist nicht nötig.
|
||||||
@@ -667,9 +650,9 @@ Wenden Sie dasselbe Muster auf andere Host-Services an (z. B. Notifications), we
|
|||||||
|
|
||||||
#### Login-Callback-Route
|
#### Login-Callback-Route
|
||||||
|
|
||||||
Für die Entwicklung ohne voreingestellten API-Key benötigen Sie einen Weg, einen API-Token zu erhalten und zu speichern. Dieses Demo nutzt einen **weiterleitungsbasierten Auth-Callback** auf `/login` statt eines eingebetteten Login-Formulars — beide Ansätze sind jedoch möglich: Sie können auch ein eigenes Login-Formular bauen, die GraphQL-`login`-Mutation aufrufen und den Token selbst verwalten.
|
Für die Entwicklung ohne voreingestellten API-Key fügen Sie eine `/login`-Route hinzu, die als **Auth-Callback** dient — nicht als Login-Formular. Ist der Nutzer nicht authentifiziert, leitet der Auth Guard zur PHX-Login-Seite (`environment.serverUrl`) weiter. Nach erfolgreichem Login leitet PHX zurück auf die `/login`-Route Ihres Plugins mit einem `authToken`-Query-Parameter und einem base64-kodierten `redirectTo`-Ziel.
|
||||||
|
|
||||||
Bei der Weiterleitungsvariante leitet der Auth Guard nicht authentifizierte Nutzer zur PHX-Login-Seite (`environment.serverUrl`) weiter. Nach erfolgreichem Login leitet PHX zurück auf die `/login`-Route Ihres Plugins mit einem `authToken`-Query-Parameter und einem base64-kodierten `redirectTo`-Ziel. Die Callback-Komponente speichert den Token in `localStorage` und navigiert zurück zur ursprünglichen Seite:
|
Die Callback-Komponente speichert den Token in `localStorage` und navigiert zurück zur ursprünglichen Seite:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@Component({
|
@Component({
|
||||||
@@ -747,34 +730,32 @@ Empfohlene Skripte nach dieser Anleitung:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
| Skript | Beschreibung |
|
||||||
|
|--------|--------------|
|
||||||
|
| `build` | Production-Watch-Build für PHX |
|
||||||
|
| `serve` | Stellt kompilierte Assets für PHX bereit (Standard-Port 3223) |
|
||||||
|
| `plugin` | Führt `build` + `serve` zusammen aus — verwenden Sie `yarn run plugin` |
|
||||||
|
| `client` | Eigenständiger Angular-Dev-Server auf Port 4201 |
|
||||||
|
| `codegen` | Generiert GraphQL-TypeScript-Typen neu |
|
||||||
|
|
||||||
| Skript | Beschreibung |
|
Dieses Repository führt zusätzlich `copy-latest.mjs` parallel zu `build` aus, um Artefakte nach `latest/` für die gehostete Demo zu veröffentlichen — das ist für Ihr eigenes Plugin nicht erforderlich.
|
||||||
| --------- | ---------------------------------------------------------------------- |
|
|
||||||
| `build` | Production-Watch-Build für PHX |
|
|
||||||
| `serve` | Stellt kompilierte Assets für PHX bereit (Standard-Port 3223) |
|
|
||||||
| `plugin` | Führt `build` + `serve` zusammen aus — verwenden Sie `yarn run plugin` |
|
|
||||||
| `client` | Eigenständiger Angular-Dev-Server auf Port 4201 |
|
|
||||||
| `codegen` | Generiert GraphQL-TypeScript-Typen neu |
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Fehlerbehebung
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
| Symptom | Zu prüfen |
|
||||||
| Symptom | Zu prüfen |
|
|---------|-----------|
|
||||||
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| PHX zeigt eine alte Plugin-Version | Hard-Refresh; prüfen, ob `serve.json` `Cache-Control: no-store` für JS setzt; `yarn run plugin` neu starten |
|
||||||
| PHX zeigt eine alte Plugin-Version | Hard-Refresh; prüfen, ob `serve.json` `Cache-Control: no-store` für JS setzt; `yarn run plugin` neu starten |
|
|
||||||
| `401` / GraphQL-Auth-Fehler im Standalone-Modus | `apiKey` in `environment.development.ts` setzen, oder sicherstellen, dass `serverUrl` auf Ihre PHX-Instanz zeigt, damit die Login-Weiterleitung funktioniert |
|
| `401` / GraphQL-Auth-Fehler im Standalone-Modus | `apiKey` in `environment.development.ts` setzen, oder sicherstellen, dass `serverUrl` auf Ihre PHX-Instanz zeigt, damit die Login-Weiterleitung funktioniert |
|
||||||
| `yarn add @phx-erp/shared` schlägt fehl | Netzwerkzugriff auf die öffentliche npm-Registry prüfen; `yarn install` erneut ausführen |
|
| `yarn add @phx-erp/shared` schlägt fehl | Netzwerkzugriff auf die öffentliche npm-Registry prüfen; `yarn install` erneut ausführen |
|
||||||
| Routing in PHX funktioniert nicht | Routen-Segmente zu `stripTrailingSegments` in `providePhoenixPluginWithPrimeNG` hinzufügen |
|
| Routing in PHX funktioniert nicht | Routen-Segmente zu `stripTrailingSegments` in `providePhoenixPluginWithPrimeNG` hinzufügen |
|
||||||
| Tailwind-Klassen fehlen in einer Komponente | `@tailwind`-Direktiven in den `styles` der Komponente ergänzen |
|
| Tailwind-Klassen fehlen in einer Komponente | `@tailwind`-Direktiven in den `styles` der Komponente ergänzen |
|
||||||
| `yarn codegen` schlägt fehl | PHX muss laufen und die Schema-URL in `codegen.ts` erreichbar sein |
|
| `yarn codegen` schlägt fehl | PHX muss laufen und die Schema-URL in `codegen.ts` erreichbar sein |
|
||||||
| Custom Element nicht gefunden | Tag im Manifest muss exakt mit `customElements.define` / `bootstrapPhoenixPluginCustomElement` übereinstimmen |
|
| Custom Element nicht gefunden | Tag im Manifest muss exakt mit `customElements.define` / `bootstrapPhoenixPluginCustomElement` übereinstimmen |
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Bei Integrationsfragen oder PHX-spezifischen APIs wenden Sie sich an Ihren PHX-Partner.
|
Bei Integrationsfragen oder PHX-spezifischen APIs wenden Sie sich an Ihren PHX-Partner.
|
||||||
|
|||||||
155
README.md
155
README.md
@@ -1,17 +1,15 @@
|
|||||||
# PHX Frontend Plugin Demo
|
# 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`.
|
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:
|
The same codebase supports two workflows:
|
||||||
|
|
||||||
|
| Mode | Purpose | Typical command |
|
||||||
| Mode | Purpose | Typical command |
|
|------|---------|-----------------|
|
||||||
| --------------------- | ----------------------------------------- | ----------------- |
|
| **Plugin host** | Run inside PHX as a custom element | `yarn run plugin` |
|
||||||
| **Plugin host** | Run inside PHX as a custom element | `yarn run plugin` |
|
| **Standalone client** | Develop locally like a normal Angular app | `yarn client` |
|
||||||
| **Standalone client** | Develop locally like a normal Angular app | `yarn client` |
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,7 +22,6 @@ The same codebase supports two workflows:
|
|||||||
- [Development modes](#development-modes)
|
- [Development modes](#development-modes)
|
||||||
- [Project structure](#project-structure)
|
- [Project structure](#project-structure)
|
||||||
- [Create your own plugin](#create-your-own-plugin)
|
- [Create your own plugin](#create-your-own-plugin)
|
||||||
- [Scaffold CLI (recommended)](#scaffold-cli-recommended)
|
|
||||||
- [1. Basic setup](#1-basic-setup)
|
- [1. Basic setup](#1-basic-setup)
|
||||||
- [2. Tailwind CSS](#2-tailwind-css)
|
- [2. Tailwind CSS](#2-tailwind-css)
|
||||||
- [3. GraphQL Codegen](#3-graphql-codegen)
|
- [3. GraphQL Codegen](#3-graphql-codegen)
|
||||||
@@ -80,21 +77,26 @@ For local standalone development you also need either:
|
|||||||
|
|
||||||
## Quick start
|
## 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:**
|
1. **Install dependencies:**
|
||||||
```bash
|
|
||||||
|
```bash
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Create a local development environment** (optional, for standalone mode):
|
2. **Create a local development environment** (optional, for standalone mode):
|
||||||
```bash
|
|
||||||
|
```bash
|
||||||
cp src/environments/environment.example.ts src/environments/environment.development.ts
|
cp src/environments/environment.example.ts src/environments/environment.development.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
Adjust `apiUrl`, `wsUrl`, and optionally `apiKey` in that file. The file is gitignored.
|
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`):
|
3. **Generate GraphQL types** (requires PHX admin API at the schema URL configured in `codegen.ts`):
|
||||||
```bash
|
|
||||||
|
```bash
|
||||||
yarn codegen
|
yarn codegen
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Run in one of the development modes** below.
|
4. **Run in one of the development modes** below.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -104,10 +106,10 @@ These steps apply to **this demo repository**. To create your own plugin from sc
|
|||||||
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:
|
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://unpkg.com/@phx-erp/phx-frontend-plugin-demo/manifest.json
|
https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/manifest.json
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
The manifest resolves to the compiled `main.js` in the `latest/` directory of this repository.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -122,7 +124,7 @@ yarn client
|
|||||||
# equivalent: ng serve --port 4201 --watch --configuration development
|
# equivalent: ng serve --port 4201 --watch --configuration development
|
||||||
```
|
```
|
||||||
|
|
||||||
Open **[http://localhost:4201/](http://localhost:4201/)**.
|
Open **http://localhost:4201/**.
|
||||||
|
|
||||||
### Plugin host mode (inside PHX)
|
### Plugin host mode (inside PHX)
|
||||||
|
|
||||||
@@ -134,20 +136,18 @@ yarn run plugin
|
|||||||
|
|
||||||
This runs `yarn build` and `yarn serve` concurrently:
|
This runs `yarn build` and `yarn serve` concurrently:
|
||||||
|
|
||||||
|
| Script | What it does |
|
||||||
| Script | What it does |
|
|--------|----------------|
|
||||||
| ------------ | ---------------------------------------------------------------------------------- |
|
| `yarn build` | Watches production build (in this repo, also syncs output to `latest/` for the hosted demo) |
|
||||||
| `yarn build` | Watches production build |
|
| `yarn serve` | Serves `dist/.../browser/` at **http://localhost:3223/** |
|
||||||
| `yarn serve` | Serves `dist/.../browser/` at **[http://localhost:3223/](http://localhost:3223/)** |
|
|
||||||
|
|
||||||
|
|
||||||
Point your PHX instance at the local manifest:
|
Point your PHX instance at the local manifest:
|
||||||
|
|
||||||
```
|
```
|
||||||
http://localhost:3223/local.manifest.json
|
http://localhost:3223/manifest.json
|
||||||
```
|
```
|
||||||
|
|
||||||
(`public/local.manifest.json` is copied into the served output.)
|
(`public/manifest.json` is copied into the served output.)
|
||||||
|
|
||||||
> **Note:** Use `yarn run plugin`, not `yarn plugin` — Yarn treats `plugin` as a built-in command.
|
> **Note:** Use `yarn run plugin`, not `yarn plugin` — Yarn treats `plugin` as a built-in command.
|
||||||
|
|
||||||
@@ -166,15 +166,16 @@ yarn codegen # Regenerate GraphQL types
|
|||||||
|
|
||||||
```
|
```
|
||||||
phx-frontend-plugin-demo/
|
phx-frontend-plugin-demo/
|
||||||
|
├── latest/ # Published build output (main.js, manifest.json)
|
||||||
├── public/
|
├── public/
|
||||||
│ └── local.manifest.json # Local dev manifest (copied into dist)
|
│ └── manifest.json # Local manifest (path → localhost:3223)
|
||||||
├── manifest.json # Published manifest (npm/unpkg)
|
|
||||||
├── scripts/
|
├── scripts/
|
||||||
|
│ ├── copy-latest.mjs # (this repo only) Sync dist/*.js → latest/ for hosted demo
|
||||||
│ └── serve-dist.mjs # Static server for plugin-host dev
|
│ └── serve-dist.mjs # Static server for plugin-host dev
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── app/
|
│ ├── app/
|
||||||
│ │ ├── components/ # Demo pages (hello-world, product-view, …)
|
│ │ ├── components/ # Demo pages (hello-world, product-view, …)
|
||||||
│ │ ├── login.ts # Auth callback route — redirect flow (standalone development)
|
│ │ ├── login.ts # Auth callback route (standalone development)
|
||||||
│ │ ├── services/
|
│ │ ├── services/
|
||||||
│ │ │ ├── apollo.service.ts
|
│ │ │ ├── apollo.service.ts
|
||||||
│ │ │ └── phoenix-host-bridge.service.ts
|
│ │ │ └── phoenix-host-bridge.service.ts
|
||||||
@@ -197,23 +198,7 @@ phx-frontend-plugin-demo/
|
|||||||
|
|
||||||
## Create your own plugin
|
## Create your own plugin
|
||||||
|
|
||||||
### Scaffold CLI (recommended)
|
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/).
|
||||||
|
|
||||||
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.
|
Replace placeholders such as `*PROJECT-NAME*`, `*YOUR-TAG*`, and `*YOUR-TOKEN*` with your values.
|
||||||
|
|
||||||
@@ -322,7 +307,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).
|
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
|
```ts
|
||||||
export abstract class Environment {
|
export abstract class Environment {
|
||||||
@@ -334,7 +319,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
|
```ts
|
||||||
import { Environment } from './environment.interface';
|
import { Environment } from './environment.interface';
|
||||||
@@ -348,7 +333,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
|
```ts
|
||||||
import { Environment } from './environment.interface';
|
import { Environment } from './environment.interface';
|
||||||
@@ -503,8 +488,8 @@ bootstrapPhoenixPluginCustomElement(App, '*YOUR-TAG*', appConfig).then((app) =>
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
- `bootstrapPhoenixPluginCustomElement` — creates the Angular application and registers the custom element for PHX.
|
- **`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.
|
- **`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.
|
The custom element tag must be **lowercase with hyphens** (e.g. `my-company-orders`). Use the same tag in your manifest.
|
||||||
|
|
||||||
@@ -525,14 +510,12 @@ 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` |
|
||||||
|
|
||||||
| Field | Description |
|
**Local development example** (`public/manifest.json` in this repo):
|
||||||
| ----------------- | --------------------------------------------------------- |
|
|
||||||
| `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
|
```json
|
||||||
{
|
{
|
||||||
@@ -541,11 +524,11 @@ PHX loads a JSON manifest that points to your entry script and declares the cust
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Published example** (root `manifest.json`, served from npm via unpkg):
|
**Hosted example** (`latest/manifest.json` in this repo):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"path": "https://unpkg.com/@phx-erp/phx-frontend-plugin-demo",
|
"path": "https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/main.js",
|
||||||
"items": [{ "tagName": "frontend-plugin-demo" }]
|
"items": [{ "tagName": "frontend-plugin-demo" }]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -555,7 +538,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.
|
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.
|
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.
|
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).
|
For local plugin-host development, see [Serving the plugin locally](#serving-the-plugin-locally).
|
||||||
|
|
||||||
@@ -571,7 +554,7 @@ Install the static server:
|
|||||||
yarn add -D serve
|
yarn add -D serve
|
||||||
```
|
```
|
||||||
|
|
||||||
`**scripts/serve-dist.mjs**`
|
**`scripts/serve-dist.mjs`**
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
@@ -592,7 +575,7 @@ const child = spawn(serveBin, ['-l', port, '--cors', '--no-etag'], {
|
|||||||
child.on('exit', (code) => process.exit(code ?? 0));
|
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
|
```json
|
||||||
{
|
{
|
||||||
@@ -618,6 +601,8 @@ Add to `package.json`:
|
|||||||
|
|
||||||
Use a custom port: `PORT=8080 yarn serve`.
|
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
|
### 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.
|
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.
|
||||||
@@ -665,9 +650,9 @@ Apply the same pattern for other host services (e.g. notifications) when you nee
|
|||||||
|
|
||||||
#### Login callback route
|
#### Login callback route
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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:
|
The callback component stores the token in `localStorage` and navigates back to the original page:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@Component({
|
@Component({
|
||||||
@@ -745,34 +730,32 @@ 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 |
|
||||||
|
|
||||||
| Script | Description |
|
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.
|
||||||
| --------- | ------------------------------------------------------- |
|
|
||||||
| `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
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Things to check |
|
||||||
| 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` |
|
||||||
| 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 |
|
| `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` |
|
| `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` |
|
| 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` |
|
| 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 |
|
| `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` |
|
| Custom element not found | Tag in manifest must exactly match `customElements.define` / `bootstrapPhoenixPluginCustomElement` |
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
For integration questions or PHX-specific APIs, contact your PHX partner.
|
For integration questions or PHX-specific APIs, contact your PHX partner.
|
||||||
|
|||||||
3838
latest/main.js
Normal file
3838
latest/main.js
Normal file
File diff suppressed because one or more lines are too long
8
latest/manifest.json
Normal file
8
latest/manifest.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"path": "https://gitea.phx-erp.de/api/v1/repos/PHXGMBH/phx-frontend-plugin-webcomponent-demo/raw/master/latest/main.js",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"tagName": "frontend-plugin-demo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
2
latest/polyfills.js
Normal file
2
latest/polyfills.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"path": "https://unpkg.com/@phx-erp/phx-frontend-plugin-demo",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"tagName": "frontend-plugin-demo"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
18
package.json
18
package.json
@@ -1,22 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@phx-erp/phx-frontend-plugin-demo",
|
"name": "phx-frontend-plugin-demo",
|
||||||
"version": "0.1.0",
|
"version": "0.0.0",
|
||||||
"files": [
|
|
||||||
"dist/phx-frontend-plugin-demo/browser",
|
|
||||||
"manifest.json"
|
|
||||||
],
|
|
||||||
"exports": {
|
|
||||||
".": "./dist/phx-frontend-plugin-demo/browser/main.js",
|
|
||||||
"./manifest.json": "./manifest.json"
|
|
||||||
},
|
|
||||||
"private": false,
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve --configuration development",
|
"start": "ng serve --configuration development",
|
||||||
"build": "ng build --watch --output-hashing none --configuration production",
|
"build": "concurrently \"ng build --watch --output-hashing none --configuration production\" \"node scripts/copy-latest.mjs --watch\"",
|
||||||
"build:once": "ng build --output-hashing none --configuration production",
|
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"build:dev": "ng build --output-hashing none --watch --configuration development",
|
"build:dev": "ng build --output-hashing none --watch --configuration development",
|
||||||
|
"copy-latest": "node scripts/copy-latest.mjs",
|
||||||
"serve": "node ./scripts/serve-dist.mjs",
|
"serve": "node ./scripts/serve-dist.mjs",
|
||||||
"codegen": "graphql-codegen",
|
"codegen": "graphql-codegen",
|
||||||
"client": "ng serve --port 4201 --watch --configuration development",
|
"client": "ng serve --port 4201 --watch --configuration development",
|
||||||
@@ -34,6 +25,7 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "20",
|
"@angular/animations": "20",
|
||||||
"@angular/common": "^20.3.0",
|
"@angular/common": "^20.3.0",
|
||||||
|
|||||||
62
scripts/copy-latest.mjs
Normal file
62
scripts/copy-latest.mjs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { watch } from 'node:fs';
|
||||||
|
import { access, constants, copyFile, mkdir, readdir, unlink } from 'node:fs/promises';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const root = join(__dirname, '..');
|
||||||
|
const sourceDir = join(root, 'dist/phx-frontend-plugin-demo/browser');
|
||||||
|
const destDir = join(root, 'latest');
|
||||||
|
|
||||||
|
async function copyJsFiles() {
|
||||||
|
try {
|
||||||
|
await access(sourceDir, constants.F_OK);
|
||||||
|
await mkdir(destDir, { recursive: true });
|
||||||
|
|
||||||
|
const files = (await readdir(sourceDir)).filter((f) => f.endsWith('.js'));
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
await copyFile(join(sourceDir, file), join(destDir, file));
|
||||||
|
}
|
||||||
|
|
||||||
|
const destFiles = (await readdir(destDir)).filter((f) => f.endsWith('.js'));
|
||||||
|
for (const file of destFiles) {
|
||||||
|
if (!files.includes(file)) {
|
||||||
|
await unlink(join(destDir, file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[latest] Copied ${files.length} JS file(s)`);
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code !== 'ENOENT') {
|
||||||
|
console.error('[latest] Copy failed:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function watchJsFiles() {
|
||||||
|
let timeout;
|
||||||
|
|
||||||
|
const debouncedCopy = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(copyJsFiles, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tryWatch = () => {
|
||||||
|
access(sourceDir, constants.F_OK)
|
||||||
|
.then(() => {
|
||||||
|
copyJsFiles();
|
||||||
|
watch(sourceDir, debouncedCopy);
|
||||||
|
console.log('[latest] Watching for JS changes in dist');
|
||||||
|
})
|
||||||
|
.catch(() => setTimeout(tryWatch, 500));
|
||||||
|
};
|
||||||
|
|
||||||
|
tryWatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.argv.includes('--watch')) {
|
||||||
|
watchJsFiles();
|
||||||
|
} else {
|
||||||
|
copyJsFiles();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user