@ff-admin/core (1.5.4)

Published 2026-06-09 10:36:11 +00:00 by jkeffects in FF-Admin/ff-admin-core

Installation

@ff-admin:registry=
npm install @ff-admin/core@1.5.4
"@ff-admin/core": "1.5.4"

About this package

ff-admin-core

Basis-UI-Funktionalität für ff-admin Anwendungen als wiederverwendbares npm-Package.

Package Content

📋 Inhaltsverzeichnis

Übersicht

@ff-admin/core stellt Basis-UI-Komponenten, Layouts, Stores, Router, Enums und Helper für FF-Admin Anwendungen bereit. Das Package sorgt für eine konsistente UI-Struktur und wiederverwendbare Funktionalität in allen Anwendungen.

Was übernimmt das Package?

🖥️ Basis-UI-Inhalte

  • Wiederverwendbare UI-Komponenten (z.B. Layouts, Tabs, Modals, Icons, Progress Bars)
  • Stores für globale State-Verwaltung (Pinia)
  • Router-Konfiguration und Guards
  • Enum-Definitionen für Permissions, Module, Sections etc.
  • Helper-Funktionen für UI und Kommunikation
  • Socket.IO Client-Integration
  • PWA-Unterstützung
  • Push-Benachrichtigungen (Web Push mit VAPID)
  • Konsistente Styles und globale CSS

Das Package stellt die Basis für alle FF-Admin Anwendungen bereit und sorgt für ein einheitliches UI und Verhalten.

Installation

npm install @ff-admin/core

Wichtig: in der viteConfig der Anwendung muss die Dependency Optimization für das package deaktiviert werden.

export default defineConfig({
  plugins: [
    vue(),
    VitePWA({ /* deine config */ }), // falls pwa benötigt
    // ... andere plugins
  ],
  optimizeDeps: {
    exclude: ['@ff-admin/core']
  }
}) 

Peer Dependencies

Das Package benötigt folgende Peer Dependencies:

  • vue >= 3.0.0
  • pinia >= 2.0.0
  • vue-router >= 4.0.0

Verwendung & Erweiterung

Das Package wird typischerweise in den zentralen Dateien der Anwendung (z.B. core-setup.ts, main.ts, router/index.ts) eingebunden und konfiguriert. Die Erweiterung erfolgt über TypeScript-Interfaces, um projektspezifische Funktionalität und Enums zu ergänzen.

Einbindung und Initialisierung

  • Importiere das Package und initialisiere zentrale Services (z.B. HTTP, Socket, Konfigurationswerte).
  • Binde globale Komponenten, Stores und Router ein.
  • Konfiguriere Namespaces, Enums und weitere projektspezifische Typen über Interface-Erweiterungen.

Beispielhafter Ablauf (vereinfacht):

// Importiere das Package und zentrale Services
import "@ff-admin/core";
import { configureSocketNamespace, HTTPManager, NavigationConfigVals, PackageConfigVals, SocketManager } from "@ff-admin/core";

// (Optional) Erweiterung von Typen für projektspezifische Enums und Namespaces
declare module "@ff-admin/core" {
  interface PermissionSectionMap {
    // Eigene Berechtigungs-Section hinzufügen
    mySection: void;
  }
  interface PermissionModuleMap {
    // Eigene Berechtigungs-Module hinzufügen
    myModule: void;
    anotherModule: void;
  }
  interface SettingTopicMap {
    myApp: void;
  }
  interface SettingValueMapping {
    "myApp.apiKey": string;
    "myApp.timeout": number;
    "myApp.baseUrl": string;
  }
  interface ModuleSettingTopicMap {
    myModule: void;
  }
  interface ModuleSettingValueMapping {
    "myModule.setting1": string;
    "myModule.setting2": number;
    "myModule.enabled": boolean;
  }
  interface SocketNamespaceMap {
    myCustomNamespace: "/custom";
    anotherNamespace: "/another";
  }
}

// Konfigurationswerte setzen
PackageConfigVals.serverAddress = "...";
PackageConfigVals.devMode = true;
PackageConfigVals.clientVersion = ""; // Alternativ package JSON auslesen

// Initialisierung und Konfiguration
HTTPManager.init();
SocketManager.init();
configureSocketNamespace({
  myCustomNamespace: "/custom",
  anotherNamespace: "/another",
});

// Angabe der Navigationsstruktur
NavigationConfigVals.defaultActiveNavigation = "configuration"
NavigationConfigVals.topLevel = [/*...*/]
// direktes setzen der Struktur
NavigationConfigVals.navigation = {/*...*/}
// oder einzelne Keys setzen
NavigationConfigVals.setNavigationKey("configuration", {/*...*/})

Werte wie im Backend müssen nur für den SocketNamespace definiert werden. Alle anderen Konfigurationswerte werden über den Server bezogen.

Integration in die App

  • Binde das Package in die Hauptdatei deiner App ein (z.B. in main.ts).
  • Verwende die bereitgestellten Komponenten, Stores, Router und Helper in deiner Anwendung.
  • Nutze die Basisrouten und Guards aus dem Package und ergänze sie bei Bedarf um eigene Routen.

Beispiel (allgemein):

import "./core-setup";
import { createApp } from "vue";
import { createPinia } from "pinia";
import router from "./router";
import App from "./App.vue";

const app = createApp(App);
app.use(createPinia());
app.use(router);
// Weitere globale Properties oder Plugins
PackageConfigVals.router = router;
app.mount("#app");

Geplante Benachrichtigungen (Scheduled Notifications)

Das Package stellt eine vollständige UI bereit, über die Benutzer ihre geplanten Benachrichtigungen konfigurieren können. Die verfügbaren Notification-Typen werden dynamisch vom Server geladen (registriert via registerScheduledNotificationGenerator im Backend).

Route

Die Seite ist unter /account/notification-schedule verfügbar (Route-Name: account-notification_schedule).

Store

Der useNotificationScheduleStore (Pinia) verwaltet den State und die API-Kommunikation:

import { useNotificationScheduleStore } from "@ff-admin/core";

const store = useNotificationScheduleStore();

// Schedules und verfügbare Typen laden
store.fetchSchedules();
store.fetchAvailable();

// Schedule erstellen/aktualisieren
await store.upsertSchedule({
  reportKey: "unit/inspection-remind",
  weekdays: [1, 2, 3, 4, 5], // Mo-Fr
  time: "08:00",
  channels: ["inApp", "email"],
  active: true,
});

// Schedule löschen
await store.deleteSchedule("unit/inspection-remind");

Konfigurationsoptionen für Benutzer

  • Wochentage: Auswahl von Sonntag bis Samstag
  • Uhrzeit: Konfigurierbare Sendezeit (HH:MM)
  • Kanäle: In-App, E-Mail, Web-Push (Mehrfachauswahl)
  • Aktiv/Inaktiv: Einzelne Schedules ein-/ausschalten

Tägliche Benachrichtigungen

Benachrichtigungstypen, die vom Backend mit daily: true registriert wurden, werden im Frontend ohne Wochentag-Auswahl dargestellt. Der Benutzer kann bei diesen nur die Uhrzeit und die Kanäle einstellen. Die Wochentage werden automatisch auf alle Tage gesetzt, da der Inhalt tagesaktuell ist und bei Auslassung verloren ginge.


Push-Benachrichtigungen

Das Package stellt Web-Push-Funktionalität bereit (VAPID-basiert). Die Integration erfordert:

  1. Einen Push-Service-Worker im public/-Verzeichnis der Anwendung
  2. Einbindung des SW in die PWA- oder App-Konfiguration
  3. Verwendung der bereitgestellten Composables für Subscribe/Unsubscribe

Service Worker einrichten

Das Package liefert ein Vite-Plugin mit, das den Push-Service-Worker automatisch im Dev-Server bereitstellt und beim Build in den Output kopiert. Kein manuelles Kopieren nötig.

Variante A: Vite-Plugin (empfohlen)

// vite.config.ts
import { vitePushServiceWorker } from "@ff-admin/core/vite-push-sw";

export default defineConfig({
  plugins: [
    vitePushServiceWorker(),
    // ... andere Plugins
  ],
});

Das Plugin:

  • Stellt /sw-push.js im Dev-Server bereit (middleware)
  • Kopiert sw-push.js automatisch in den Build-Output

Kombination mit vite-plugin-pwa (generateSW-Modus)

Wenn die App vite-plugin-pwa im Standard-Modus (generateSW) nutzt, wird der Push-Handler über importScripts in den generierten Service Worker eingebunden:

// vite.config.ts
import { vitePushServiceWorker } from "@ff-admin/core/vite-push-sw";

export default defineConfig({
  plugins: [
    vitePushServiceWorker(),
    VitePWA({
      workbox: {
        importScripts: ["/sw-push.js"],
        // ... weitere workbox-Konfiguration
      },
    }),
  ],
});

Der generierte Workbox-SW führt importScripts('/sw-push.js') aus, wodurch die Push-Event-Listener aktiv werden.

Variante B: Manuell (ohne Plugin)

Alternativ kann sw-push.js direkt aus dem Package in das public/-Verzeichnis kopiert werden:

node_modules/@ff-admin/core/sw-push.js → public/sw-push.js

Integration ohne PWA

Falls die App kein vite-plugin-pwa nutzt, gibt es keinen automatisch generierten Workbox-SW. In diesem Fall wird sw-push.js als eigenständiger Service Worker registriert.

1. Vite-Plugin einbinden (stellt /sw-push.js bereit)

// vite.config.ts
import { vitePushServiceWorker } from "@ff-admin/core/vite-push-sw";

export default defineConfig({
  plugins: [
    vitePushServiceWorker(),
    // ... andere Plugins (KEIN VitePWA nötig)
  ],
});

2. Service Worker beim App-Start registrieren

// main.ts
import { registerPushServiceWorker } from "@ff-admin/core";

// Registriert /sw-push.js als eigenständigen Service Worker
await registerPushServiceWorker();

Die Funktion prüft, ob bereits ein Service Worker aktiv ist (z.B. von einer PWA) und registriert nur dann einen neuen, wenn keiner vorhanden ist.

Hinweise

  • Der SW wird unter dem Root-Scope (/) registriert — damit ist er für die gesamte App aktiv
  • registerPushServiceWorker() sollte vor dem ersten subscribe()-Aufruf aufgerufen werden (z.B. in main.ts)
  • Das Composable usePushNotification() nutzt navigator.serviceWorker.ready, das auf den registrierten SW wartet
  • Optional kann ein anderer Dateiname übergeben werden: registerPushServiceWorker("/my-sw.js")

Push-Composable verwenden

import { usePushNotification } from "@ff-admin/core";

const { isSupported, subscribe, unsubscribe, resubscribe } = usePushNotification();

// Prüfen ob Push verfügbar ist
if (isSupported) {
  // Benutzer für Push registrieren (fragt Notification-Permission ab)
  const success = await subscribe();
  
  // Abmelden
  await unsubscribe();
  
  // Bestehende Subscription verlängern (z.B. nach Token-Refresh)
  await resubscribe();
}

Push-Subscription-Store

import { usePushSubscriptionStore } from "@ff-admin/core";

const store = usePushSubscriptionStore();

// Alle Subscriptions des Users laden
await store.fetchSubscriptions();

// Eine Subscription remote deaktivieren (z.B. von einem anderen Gerät)
await store.deactivateSubscription(endpoint);

Automatische Integration

Das Package integriert Push automatisch an folgenden Stellen:

Stelle Aktion
Login (Passwort/TOTP + Passkey) subscribe() — registriert das Gerät
Token-Refresh resubscribe() — verlängert die Laufzeit
Logout unsubscribe() — meldet das Gerät ab

Account-View

Die Route push-subscriptions (Name: account-push_subscriptions) zeigt eine Übersicht aller registrierten Push-Subscriptions mit:

  • Geräte-Icon (Mobile/Tablet/Desktop)
  • Aktiv/Inaktiv-Status
  • Button zum Deaktivieren einzelner Subscriptions
  • "Push aktivieren"-Button zum Registrieren des aktuellen Geräts

Backend-Voraussetzungen

Der Server muss folgende Umgebungsvariablen bereitstellen:

Variable Beschreibung
VAPID_PUBLIC_KEY VAPID Public Key (Base64url)
VAPID_PRIVATE_KEY VAPID Private Key (Base64url)
VAPID_SUBJECT Kontakt-URL oder mailto: (z.B. mailto:admin@example.com)

Werden keine Keys angegeben, generiert der Server beim Start ein Schlüsselpaar und gibt es in der Konsole aus.


CSS-Klassen & Theming

Einbindung in der Anwendung

Damit Styling und Tailwind-Utilities korrekt funktionieren, sind drei Schritte notwendig:

1. vite.config.ts — Tailwind-Plugin einbinden

import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    tailwindcss(),
    // ... weitere Plugins
  ],
  optimizeDeps: {
    exclude: ["@ff-admin/core"],
  },
});

2. src/main.css — CSS in der richtigen Reihenfolge importieren

/* 1. Tailwind selbst */
@import "tailwindcss";

/* 2. Rohe Theme-Konfiguration des Package, damit der App-Build 
      die Tokens und Variants kennt */
@import "@ff-admin/core/theme.css";

/* 3. Fertig kompilierte Package-Styles (Komponenten, Base-Layer) */
@import "@ff-admin/core/style.css";

/* 4. Scan-Pfad für Package-Templates, damit Tailwind deren
      verwendete Klassen generiert */
@source "../node_modules/@ff-admin/core/dist/**/*.{js,vue}";

3. src/main.ts — CSS-Datei laden

import "./main.css";

Was die drei Dateien leisten

Import Typ Zweck
@ff-admin/core/theme.css Roh (unkompiliert) Gibt dem Tailwind-Compiler der App die Custom-Tokens (--color-primary etc.) und die hover-Variant ohne @media (hover: hover)
tailwindcss Tailwind Generiert alle Utility-Klassen für die App inkl. der Package-Tokens
@ff-admin/core/style.css Kompiliert Fertige Styles der Package-Komponenten (Base-Layer, Component-Klassen)

Theming — CSS-Variablen

Das gesamte Farbschema leitet sich aus einem einzigen Hue-Wert (0–360, HSL-Farbkreis) ab. Der Wert wird über die Admin-Einstellungen gesetzt und per Pinia-Store zur Laufzeit auf document.documentElement angewendet.

Variable Berechnung Verwendung
--hue 0–360 (Standard: 4 = Rot) Einziger konfigurierbarer Wert
--primary hsl(var(--hue) 100% 30%) Hauptfarbe (Buttons, Rahmen, Header)
--primary-hover hsl(var(--hue) 100% 25%) Hover-Zustand von Primary
--primary-subtle hsl(var(--hue) 100% 93%) Heller Hintergrund (aktive Tabs, Hover-Flächen)
--accent hsl(var(--hue) 80% 40%); Akzent
--success #7ac142 (fix) Erfolg
--failed #ff0000 (fix) Fehler

Alle Variablen sind über Tailwind als Farbklassen verfügbar (z. B. bg-primary, text-primary-subtle, border-primary-hover).


Navigation

.nav-tab

Basis-Klasse für einen Tab in einer Tab-Leiste (z. B. Passwort / TOTP). Wird mit .nav-tab-active oder .nav-tab-inactive kombiniert.

<p class="w-1/2 nav-tab" :class="tab === 'pw' ? 'nav-tab-active' : 'nav-tab-inactive'" @click="tab = 'pw'">
  Passwort
</p>
Klasse Beschreibung
.nav-tab Gemeinsame Basis: flex items-center justify-center, Padding, Schriftgröße
.nav-tab-active Aktiver Tab: bg-primary-subtle, Unterstrich in Primary-Farbe
.nav-tab-inactive Inaktiver Tab: Hover bg-primary-subtle

.nav-toplevel-active / .nav-toplevel-inactive

Für Top-Level-Navigationslinks in der Hauptnavigation (Desktop: ausgefüllter Primary-Hintergrund, Mobil: nur Textfarbe).

Für Einträge in der Seitenleiste. .sidebar-link-active fügt einen linken Primary-Balken und subtilen Hintergrund hinzu.

<div class="sidebar-link" :class="isActive ? 'sidebar-link-active' : ''">Eintrag</div>

Karten & Panels

.card

Weißes, abgerundetes Panel mit horizontalen Trennlinien (divide-y-2 divide-gray-300). Eignet sich für mehrteilige Bereiche.

<div class="card">
  <div class="card-header"></div>
  <div class="p-4">Inhalt</div>
</div>

.card-plain

Wie .card, aber ohne Trennlinien. Für schlichte weiße Panels.

.card-header

Kopfzeile innerhalb einer .card: horizontale Flex-Zeile mit justify-between, Innenabstand.


Listen-Einträge

Alle Listeneinträge (Benutzer, Rollen, Web-API-Keys, Backups, Sitzungen …) folgen diesem Schema:

<div class="list-card">
  <div class="list-card-header">
    <span>Titel</span>
    <span>Aktion</span>
  </div>
  <div class="list-card-body">
    <div class="list-card-row">
      <span class="list-card-row-label">Label</span>
      <span class="list-card-row-value">Wert</span>
    </div>
  </div>
</div>
Klasse Beschreibung
.list-card Äußerer Rahmen mit Primary-Border, abgerundet
.list-card-header Primary-Hintergrund, weißer Text, justify-between
.list-card-body Innenbereich mit Padding und vertikalem Gap
.list-card-row Horizontale Zeile für Label + Wert
.list-card-row-label Feste Mindestbreite (min-w-16) für linksbündige Labels
.list-card-row-value Wächst flexibel, schneidet zu langen Text ab

Modals

Alle Modals verwenden eine einheitliche Struktur:

<div class="modal-wrapper">
  <div class="modal-title">
    <p>Modal-Titel</p>
  </div>

  <!-- Formularfelder -->

  <div class="modal-footer">
    <div class="modal-footer-inner">
      <button>Abbrechen</button>
      <div class="modal-submit-row">
        <button primary>Bestätigen</button>
      </div>
    </div>
  </div>
</div>
Klasse Beschreibung
.modal-wrapper Max-Breite md:max-w-md, volle Breite
.modal-title Zentrierte Flex-Spalte; > p bekommt text-xl font-medium
.modal-footer Rechtsbündige Fußzeile
.modal-footer-inner Horizontale Zeile mit Gap und vertikalem Padding
.modal-submit-row Flex-Zeile für mehrstufige Submit-Aktionen (z. B. Bestätigung + Spinner)

Weitere Utility-Klassen

.badge

Kleines Tag/Chip-Pill für Labels, Rollen, Kanäle o. Ä.

<span class="badge">Admin</span>

Stil: px-2 py-0.5 bg-gray-200 border border-gray-400 rounded-full text-xs

.menu-item

Eintrag in einem Kontext- oder Dropdown-Menü. Hover verwendet bg-primary-subtle.

<div class="menu-item" @click="copy">
  <CopyIcon />
  <span>Kopieren</span>
</div>

.pagination-btn

Einzelne Zelle in einer Pagination-Leiste. first: und last: runden die Enden ab.

<ul class="flex">
  <li class="pagination-btn">«</li>
  <li class="pagination-btn">1</li>
  <li class="pagination-btn">»</li>
</ul>

Öffentliche Seiten (Login, Setup, Reset, Invite)

Für vollseitige zentrierte Seiten ohne Sidebar:

<div class="public-page">
  <div class="public-page-inner">
    <div class="public-page-header">
      <img src="…" alt="Logo" class="h-20 w-auto" />
      <h1 class="public-page-title">Seitentitel</h1>
    </div>

    <!-- Formular oder Inhalt -->
  </div>
</div>
Klasse Beschreibung
.public-page grow flex items-center justify-center, responsives Padding
.public-page-inner max-w-md w-full space-y-8
.public-page-header Flex-Spalte, zentriert, gap-4
.public-page-title text-4xl font-extrabold text-gray-900 text-center

Buttons

Buttons bekommen ihre Variante über HTML-Attribute (kein separates CSS von außen nötig):

<button primary>Primär-Aktion</button>
<button primary-outline>Sekundär-Aktion</button>
<button disabled>Deaktiviert</button>
Attribut Stil
primary Ausgefüllter Primary-Hintergrund, weißer Text, Hover dunkler
primary-outline Primary-Rahmen, transparenter Hintergrund; Hover füllt sich mit Primary
disabled / :disabled opacity-75, kein Klick möglich

Lizenz

AGPL-3.0-only

Author

JK Effects

Repository

https://code.jk-effects.cloud/FF-Admin/ff-admin-core.git

Dependencies

Dependencies

ID Version
@headlessui/vue ^1.7.23
@heroicons/vue ^2.2.0
@simplewebauthn/browser ^13.3.0
@tailwindcss/vite ^4.3.0
@vuepic/vue-datepicker ^13.0.0
@vueuse/core ^14.3.0
axios ^1.17.0
event-source-polyfill ^1.0.31
jwt-decode ^4.0.0
lodash.clonedeep ^4.5.0
lodash.difference ^4.5.0
lodash.differencewith ^4.5.0
lodash.isequal ^4.5.0
ms ^2.1.3
qrcode ^1.5.4
qs ^6.15.2
socket.io-client ^4.8.3
ua-parser-js ^2.0.10
uuid ^14.0.0
vue-qrcode-reader ^5.7.3

Development dependencies

ID Version
@rushstack/eslint-patch ^1.16.1
@tailwindcss/postcss ^4.3.0
@tsconfig/node20 ^20.1.9
@types/eslint ~9.6.1
@types/event-source-polyfill ^1.0.5
@types/lodash.clonedeep ^4.5.9
@types/lodash.difference ^4.5.9
@types/lodash.differencewith ^4.5.9
@types/lodash.isequal ^4.5.8
@types/ms ^2.1.0
@types/node ^25.9.2
@types/nprogress ^0.2.3
@types/qrcode ^1.5.6
@types/qs ^6.15.1
@types/uuid ^11.0.0
@vitejs/plugin-vue ^6.0.7
@vue/eslint-config-typescript ^14.8.0
@vue/tsconfig ^0.9.1
eslint ^10.4.1
eslint-plugin-vue ^10.9.2
npm-run-all2 ^9.0.1
tailwindcss ^4.3.0
typescript ^6.0.3
vite ^7.3.2
vite-plugin-dts ^4.5.4
vite-plugin-pwa ^1.3.0
vue-tsc ^3.3.4

Peer dependencies

ID Version
nprogress >=0.2.0
pinia >=3.0.4
quill ^2.0.3
quill-cursors ^4.0.4
vite >=7.3.0
vue >=3.5.27
vue-router >=5.0.2

Keywords

Feuerwehr ff-admin
Details
npm
2026-06-09 10:36:11 +00:00
29
JK Effects
AGPL-3.0-only
821 KiB
Assets (1)
core-1.5.4.tgz 821 KiB
Versions (32) View all
1.6.4 2026-07-08
1.6.3 2026-07-06
1.6.0 2026-07-04
1.5.5 2026-06-24
1.5.4 2026-06-09