Files
phx-frontend-plugin-webcomp…/README.DE.md

29 KiB

PHX Frontend Plugin Demo

English version

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, 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:

Modus Zweck Typischer Befehl
Plugin host Ausführung in PHX als Custom Element yarn run plugin
Standalone client Lokale Entwicklung wie eine normale Angular-App yarn client

Inhaltsverzeichnis


Übersicht

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      ┌───────────────────┐
│   PHX host      │ ─────────────────────► │  main.js (+ deps) │
│  (ERP shell)    │     loads & registers  │  custom element   │
└────────┬────────┘                        └────────┬──────────┘
         │                                          │
         │  pluginServices (Apollo, notifications…) │
         │  hostInjector                            │
         └──────────────────────────────────────────┘

Was diese Demo enthält:

  • Custom-Element-Tag: frontend-plugin-demo
  • Beispiel-Routen: Hello World, Product View, Address List
  • GraphQL-Abfragen über den PHX-Host-Apollo (Production) oder einen lokalen Apollo-Client (Development)
  • PHX-Login-Weiterleitung und Callback-Route für die eigenständige Entwicklung ohne vorkonfigurierten API-Key
  • PrimeNG- und Tailwind-Styling im PHX-Stil

Voraussetzungen

  • Node.js (LTS empfohlen)
  • Yarn v4
  • Eine laufende PHX-Instanz (für Plugin-Host-Tests und GraphQL-Schema/Codegen)

Für die lokale eigenständige Entwicklung benötigen Sie außerdem eines der folgenden:


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.

  1. Abhängigkeiten installieren:
 yarn install
  1. Lokale Development-Umgebung anlegen (optional, für den Standalone-Modus):
 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. 3. GraphQL-Typen generieren (erfordert PHX Admin API unter der in codegen.ts konfigurierten Schema-URL):

 yarn codegen
  1. In einem der Entwicklungsmodi unten starten.

Live-Demo

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

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.


Entwicklungsmodi

Standalone client (üblicher Angular-Workflow)

Startet die App mit der Development-Build-Konfiguration auf Port 4201 — Routing, PHX-Login-Weiterleitung und ein lokaler Apollo-Client funktionieren ohne Einbettung des Plugins in PHX.

yarn client
# equivalent: ng serve --port 4201 --watch --configuration development

Öffnen Sie http://localhost:4201/.

Plugin-Host-Modus (innerhalb von PHX)

Baut mit der Production-Konfiguration (ohne Output Hashing) und stellt die Ausgabe mit CORS- und No-Cache-Headern bereit, damit PHX während der Entwicklung aktuelle Bundles laden kann.

yarn run plugin

Dies führt yarn build und yarn serve parallel aus:

Skript Funktion
yarn build Production-Watch-Build
yarn serve Stellt dist/.../browser/ unter http://localhost:3223/ bereit

Verweisen Sie Ihre PHX-Instanz auf das lokale Manifest:

http://localhost:3223/local.manifest.json

(public/local.manifest.json wird in die bereitgestellte Ausgabe kopiert.)

Hinweis: Verwenden Sie yarn run plugin, nicht yarn plugin — Yarn behandelt plugin als eingebauten Befehl.

Terminal Keeper ist in .vscode/sessions.json vorkonfiguriert, um sowohl yarn run plugin als auch yarn client zu starten.

Weitere nützliche Befehle

yarn start          # ng serve with development configuration (default port 4200)
yarn codegen        # Regenerate GraphQL types

Projektstruktur

phx-frontend-plugin-demo/
├── public/
│   └── local.manifest.json    # Lokales Dev-Manifest (in dist kopiert)
├── manifest.json              # Veröffentlichtes Manifest (npm/unpkg)
├── scripts/
│   └── serve-dist.mjs         # Static server for plugin-host dev
├── src/
│   ├── app/
│   │   ├── components/        # Demo pages (hello-world, product-view, …)
│   │   ├── login.ts           # Auth-Callback-Route — Weiterleitungsflow (Standalone-Entwicklung)
│   │   ├── services/
│   │   │   ├── apollo.service.ts
│   │   │   └── phoenix-host-bridge.service.ts
│   │   ├── app.config.ts      # providePhoenixPluginWithPrimeNG, providers
│   │   ├── app.routes.ts
│   │   ├── apollo.provider.ts # Local Apollo (development only)
│   │   └── auth-guard.ts
│   ├── graphql/               # GraphQL documents for codegen
│   ├── environments/
│   │   ├── environment.ts              # Production defaults (used in PHX)
│   │   ├── environment.example.ts      # Template for local dev
│   │   └── environment.development.ts  # Local overrides (gitignored)
│   └── main.ts                # Registers the custom element
├── codegen.ts
├── serve.json                 # Cache headers for serve-dist.mjs
└── tailwind.config.js

Eigenes Plugin erstellen

Scaffold-CLI (empfohlen)

Der schnellste Weg ist die offizielle Scaffold-CLI, veröffentlicht als @phx-erp/create-phx-frontend-plugin:

# 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 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, Angular 20, PrimeNG 20 und Tailwind CSS.

Ersetzen Sie Platzhalter wie *PROJECT-NAME*, *YOUR-TAG* und *YOUR-TOKEN* durch Ihre Werte.

1. Grundsetup

mkdir *PROJECT-NAME*
cd *PROJECT-NAME*
npx @angular/cli@20 new *PROJECT-NAME* --directory ./ --package-manager yarn --style scss --ssr false --routing --standalone
yarn add primeng@20 @primeng/themes@20 tailwindcss@3.4.17 postcss tailwindcss-primeui @angular/animations@20 @angular/elements@20
yarn add -D typescript@5.9.2 @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typed-document-node concurrently
ng add apollo-angular

2. Tailwind CSS

Erstellen Sie tailwind.config.js im Projektroot. Diese Konfiguration ist an PHX angelehnt:

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'selector',
  content: ['./src/**/*.{html,ts,scss}'],
  theme: {
    extend: {
      animation: {
        fadein: 'fadein 0.5s ease-in-out',
        fadeout: 'fadeout 0.5s ease-in-out',
        fadeinleft: 'fadeinleft 0.5s ease-in-out',
        fadeinright: 'fadeinright 0.5s ease-in-out',
        fadeintop: 'fadeintop 0.5s ease-in-out',
        fadeinbottom: 'fadeinbottom 0.5s ease-in-out',
      },
      keyframes: {
        fadein: { '0%': { opacity: 0 }, '100%': { opacity: 1 } },
        fadeout: { '0%': { opacity: 1 }, '100%': { opacity: 0 } },
        fadeinleft: { '0%': { opacity: 0, transform: 'translateX(-100%)' }, '100%': { opacity: 1, transform: 'translateX(0)' } },
        fadeinright: { '0%': { opacity: 0, transform: 'translateX(100%)' }, '100%': { opacity: 1, transform: 'translateX(0)' } },
        fadeintop: { '0%': { opacity: 0, transform: 'translateY(-100%)' }, '100%': { opacity: 1, transform: 'translateY(0)' } },
        fadeinbottom: { '0%': { opacity: 0, transform: 'translateY(100%)' }, '100%': { opacity: 1, transform: 'translateY(0)' } },
      },
    },
  },
  plugins: [require('tailwindcss-primeui')],
};

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:

@tailwind base;
@tailwind components;
@tailwind utilities;

3. GraphQL Codegen

Erstellen Sie codegen.ts im Projektroot (passen Sie die Schema-URL an, falls Ihre PHX-Instanz woanders läuft):

import type { CodegenConfig } from '@graphql-codegen/cli';

const sharedConfig = {
  scalars: { DateTime: 'Date' },
  immutableTypes: false,
} as const;

const config: CodegenConfig = {
  overwrite: true,
  schema: 'http://localhost:3000/admin-api/schema.gql',
  documents: './src/graphql/*.ts',
  ignoreNoDocuments: true,
  generates: {
    './src/app/schema-types.ts': {
      plugins: ['typescript'],
      config: sharedConfig,
    },
    './src/app/generated.ts': {
      plugins: ['typescript-operations', 'typed-document-node'],
      config: {
        ...sharedConfig,
        importSchemaTypesFrom: './src/app/schema-types.ts',
      },
    },
  },
};

export default config;

In package.json ergänzen:

"codegen": "graphql-codegen"

Definieren Sie Queries und Mutations in src/graphql/ und führen Sie anschließend aus:

yarn codegen

Dies erzeugt src/app/schema-types.ts und src/app/generated.ts.

4. Environments

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

export abstract class Environment {
  production: boolean = false;
  apiUrl: string | undefined;
  wsUrl: string | undefined;
  apiKey: string | undefined;
  serverUrl: string = '';
}

**src/environments/environment.ts** (Production — verwendet bei Einbettung in PHX)

import { Environment } from './environment.interface';

export const environment: Environment = {
  production: true,
  apiUrl: undefined,
  wsUrl: undefined,
  apiKey: undefined,
  serverUrl: '',
};

**src/environments/environment.development.ts** (lokale Entwicklung — in .gitignore aufnehmen)

import { Environment } from './environment.interface';

export const environment: Environment = {
  production: false,
  apiUrl: 'http://localhost:3000/admin-api',
  wsUrl: 'ws://localhost:3000/admin-api',
  apiKey: undefined, // or a PHX API user token; otherwise redirects to PHX login
  serverUrl: 'https://localhost:4200', // PHX instance used for standalone login redirect
};

Fügen Sie fileReplacements zur Development-Build-Konfiguration in angular.json hinzu:

"development": {
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.development.ts"
    }
  ]
}

Tipp: Legen Sie environment.example.ts im Repository ab und kopieren Sie es lokal nach environment.development.ts, wie diese Demo es tut.

5. PHX-Bibliotheken

@phx-erp/shared und @phx-erp/shared-ui werden in der öffentlichen npm-Registry veröffentlicht.

Installieren Sie die Bibliotheken:

yarn add @phx-erp/shared @phx-erp/shared-ui

6. Plugin-Setup

Application config

Fügen Sie providePhoenixPluginWithPrimeNG in src/app/app.config.ts hinzu:

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { providePhoenixPluginWithPrimeNG } from '@phx-erp/shared-ui';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    // stripTrailingSegments: child route segments appended by PHX deep links
    // so APP_BASE_HREF still matches the host mount path.
    ...providePhoenixPluginWithPrimeNG({
      stripTrailingSegments: ['*YOUR*', '*ROUTES*', '*HERE*'],
    }),
    provideRouter(routes),
  ],
};

Routing: Übergeben Sie Ihre Routen-Pfadsegmente in stripTrailingSegments, oder leiten Sie sie automatisch ab:

stripTrailingSegments: routes.map((r) => r.path!).filter((p) => (p?.length ?? 0) > 0)

Host bridge service

Erstellen Sie src/app/services/phoenix-host-bridge.service.ts:

import { Injectable, Injector, signal } from '@angular/core';
import type { IPluginServices } from '@phx-erp/shared-ui';

@Injectable({ providedIn: 'root' })
export class PhoenixHostBridgeService {
  private readonly _hostInjector = signal<Injector | null>(null);
  private readonly _pluginServices = signal<IPluginServices | null>(null);

  hostInjector(): Injector | null {
    return this._hostInjector();
  }

  setHostInjector(injector: Injector): void {
    this._hostInjector.set(injector);
  }

  pluginServices(): IPluginServices | null {
    return this._pluginServices();
  }

  setPluginServices(services: IPluginServices): void {
    this._pluginServices.set(services);
  }
}

Die Host Bridge speichert von PHX bereitgestellte Services (Apollo Client, Notification Service usw.), damit Ihre Komponenten sie im Production-Modus nutzen können.

Root component

Ihre Root-Komponente empfängt pluginServices und hostInjector von PHX und leitet sie an die Bridge weiter:

import { Component, effect, inject, Injector, input } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { PhoenixHostBridgeService } from './services/phoenix-host-bridge.service';
import { IPluginServices, syncPhoenixHostInjector } from '@phx-erp/shared-ui';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `<router-outlet />`,
  styles: [`
@tailwind base;
@tailwind components;
@tailwind utilities;
`],
})
export class App {
  private readonly hostBridge = inject(PhoenixHostBridgeService);

  readonly pluginServices = input<IPluginServices>({});
  readonly hostInjector = input<Injector | undefined>(undefined);

  private readonly _syncHostInjector = syncPhoenixHostInjector(this.hostBridge, this.hostInjector);

  private readonly _syncPluginServices = effect(() => {
    this.hostBridge.setPluginServices(this.pluginServices());
  });
}

Bootstrap / custom element registration

Registrieren Sie in src/main.ts Ihr Plugin als Custom Element (ersetzen Sie *YOUR-TAG*):

import { appConfig } from './app/app.config';
import { App } from './app/app';
import { bootstrapPhoenixPluginCustomElement } from '@phx-erp/shared-ui';
import { environment } from './environments/environment';

bootstrapPhoenixPluginCustomElement(App, '*YOUR-TAG*', appConfig).then((app) => {
  if (!environment.production) {
    return app!.bootstrap(App);
  }
  return app;
});
  • 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.

Der Custom-Element-Tag muss kleingeschrieben mit Bindestrichen sein (z. B. my-company-orders). Verwenden Sie denselben Tag in Ihrem Manifest.

7. In PHX registrieren

Manifest-Format

PHX lädt ein JSON-Manifest, das auf Ihr Entry-Skript verweist und den Custom-Element-Tag deklariert:

{
  "path": "https://example.com/path/to/main.js",
  "items": [
    {
      "tagName": "*YOUR-TAG*"
    }
  ]
}
Feld Beschreibung
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):

{
  "path": "http://localhost:3223/main.js",
  "items": [{ "tagName": "frontend-plugin-demo" }]
}

Veröffentlichtes Beispiel (Root-manifest.json, über npm/unpkg bereitgestellt):

{
  "path": "https://unpkg.com/@phx-erp/phx-frontend-plugin-demo",
  "items": [{ "tagName": "frontend-plugin-demo" }]
}

Registrierungsschritte

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

Für die lokale Plugin-Host-Entwicklung siehe Plugin lokal bereitstellen.


Weitere Themen

Plugin lokal bereitstellen

Statischen Server installieren:

yarn add -D serve

**scripts/serve-dist.mjs**

import { spawn } from 'node:child_process';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const port = process.env.PORT ?? '3223';

const serveBin = join(root, 'node_modules', '.bin', 'serve');
const child = spawn(serveBin, ['-l', port, '--cors', '--no-etag'], {
  stdio: 'inherit',
  shell: true,
  cwd: root,
});

child.on('exit', (code) => process.exit(code ?? 0));

**serve.json** (ersetzen Sie *PROJECT-NAME* durch Ihren Angular-Projektnamen aus angular.json)

{
  "public": "dist/*PROJECT-NAME*/browser",
  "headers": [
    {
      "source": "**/*.{js,mjs}",
      "headers": [
        { "key": "Cache-Control", "value": "no-store, no-cache, must-revalidate" }
      ]
    }
  ]
}

Die Bereitstellung aus dem Projektroot (nicht direkt aus dist/) stellt sicher, dass serve.json angewendet wird und verhindert veraltete gecachte main.js nach Rebuilds.

In package.json ergänzen:

"serve": "node ./scripts/serve-dist.mjs"

Eigener Port: PORT=8080 yarn serve.

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 Development-Modus (Standalone) stellen Sie einen eigenen Apollo Client bereit und leiten nicht authentifizierte Nutzer zur PHX-Login-Seite weiter. PHX leitet danach zurück auf eine lokale /login-Callback-Route, die den Token speichert und den Nutzer zur ursprünglichen Seite zurückbringt.

Apollo provider

Erstellen Sie src/app/apollo.provider.ts (vollständige Implementierung in diesem Repository). Er:

  • verbindet HTTP- und WebSocket-Links mit environment.apiUrl / environment.wsUrl
  • sendet Authorization: Bearer … aus environment.apiKey oder localStorage
  • speichert Tokens aus dem Response-Header phoenix-auth-token

Nur in Development registrieren, z. B. in app.config.ts:

...(environment.production ? [] : [...apolloProvider()])

Apollo service abstraction

Ein kleiner Service wählt im Production-Modus den Host-Apollo-Client und im Development-Modus den lokalen Client:

@Injectable({ providedIn: 'root' })
export class ApolloService {
  private readonly _apollo = signal<Apollo>(undefined!);

  constructor(private readonly injector: Injector) {
    if (environment.production) {
      this._apollo.set(injector.get(PhoenixHostBridgeService)?.pluginServices()?.apollo!);
    } else {
      this._apollo.set(
        injector.get(PhoenixHostBridgeService)?.pluginServices()?.apollo ?? injector.get(Apollo)
      );
    }
  }

  apollo = () => this._apollo();
}

Wenden Sie dasselbe Muster auf andere Host-Services an (z. B. Notifications), wenn Sie Standalone-Fallbacks benötigen.

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.

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:

@Component({
  selector: 'app-login',
  template: `
    <div class="flex flex-col items-center justify-center h-screen w-screen opacity-20">
      <div class="animate-fadein duration-1000 animate-infinite animate-alternate text-4xl">Signing in...</div>
    </div>
  `,
})
export class Login {
  constructor(private readonly route: ActivatedRoute) {
    this.route.queryParams.subscribe((params) => {
      const token = params['authToken'];
      if (token) {
        const redirectTo = decodeURIComponent(atob(params['redirectTo']));
        localStorage.setItem('api-key', token);
        window.history.replaceState({}, '', redirectTo);
        window.location.href = redirectTo;
      }
    });
  }
}

Die vollständige Implementierung finden Sie unter src/app/login.ts in diesem Repo.

Auth guard

Nicht authentifizierte Nutzer nur im Development-Modus zur PHX-Login-Seite weiterleiten. Der redirectTo-Query-Parameter verknüpft die Weiterleitung über die /login-Callback-Route Ihres Plugins:

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot): boolean {
    const token = environment.apiKey ?? localStorage.getItem('api-key');
    if (!environment.production && !token) {
      const redirectTo = encodeURIComponent(
        btoa(`${window.location.protocol}//${window.location.host}/login?redirectTo=${encodeURIComponent(btoa(window.location.href))}`)
      );
      window.location.href = `${environment.serverUrl}/login?redirectTo=${redirectTo}`;
      return false;
    }
    return true;
  }
}

In app.routes.ts einbinden:

const canActivate = [environment.production ? () => true : AuthGuard];

export const routes: Routes = [
  { path: '', canActivate, component: HelloWorld },
  { path: 'product-view', canActivate, component: ProductView },
  { path: 'address-list', canActivate, component: AddressList },
  { path: 'login', component: Login },
];

package.json scripts

Empfohlene Skripte nach dieser Anleitung:

{
  "scripts": {
    "build": "ng build --watch --output-hashing none --configuration production",
    "serve": "node ./scripts/serve-dist.mjs",
    "plugin": "concurrently \"yarn build\" \"yarn serve\"",
    "client": "ng serve --port 4201 --watch --configuration development",
    "codegen": "graphql-codegen"
  }
}
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

Fehlerbehebung

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

Support

Bei Integrationsfragen oder PHX-spezifischen APIs wenden Sie sich an Ihren PHX-Partner.