Package Exports
- @enokdev/ng-panel
- @enokdev/ng-panel/package.json
Readme
@enokdev/ng-panel
Une bibliothèque moderne et dynamique de panneau d'administration pour les applications Angular avec DaisyUI et TailwindCSS.
✨ Fonctionnalités Principales
- 🎨 Interface moderne avec TailwindCSS et DaisyUI
- 🔧 Composants dynamiques (tableaux, formulaires, statistiques)
- 🌙 Support multi-thèmes avec 32+ thèmes DaisyUI
- 📱 Design responsive pour tous les appareils
- 🚀 Composants standalone Angular 17+
- 📊 Gestion d'état avec Angular Signals
- 🎯 TypeScript support complet
- 🔌 Architecture modulaire et extensible
- 🌐 Navigation hiérarchique avec menus dépliables
- 🔔 Système de notifications intégré
🚀 Démarrage Rapide
Installation
# Installer le package principal
npm install @enokdev/ng-panel
# Installer les dépendances peer requises
npm install @angular/common @angular/core @angular/forms @angular/router
# Installer TailwindCSS et DaisyUI
npm install tailwindcss@^4.1.0 @tailwindcss/postcss daisyui autoprefixer postcssConfiguration Rapide
- Configurer TailwindCSS (tailwind.config.js) :
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{html,ts}",
    "./node_modules/@enokdev/ng-panel/**/*.{html,ts,js,mjs}"
  ],
  plugins: [require("daisyui")],
  daisyui: {
    themes: true // Active tous les thèmes DaisyUI
  }
}- Configurer PostCSS (postcss.config.js) :
module.exports = {
  plugins: {
    '@tailwindcss/postcss': {},
    autoprefixer: {},
  }
}- Importer les styles (styles.css) :
@import "tailwindcss";- Configurer les styles (src/styles.css) :
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Import des icônes Material Icons */
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');- Configuration PostCSS (postcss.config.js) :
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}Première Application
app.component.ts - Configuration de base :
import { Component, OnInit, inject } from '@angular/core';
import { PanelLayoutComponent, PanelService } from '@enokdev/ng-panel';
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [PanelLayoutComponent],
  template: `<lib-panel-layout/>`
})
export class AppComponent implements OnInit {
  private panelService = inject(PanelService);
  ngOnInit() {
    // Configuration du panel
    this.panelService.setConfig({
      title: 'Mon Admin Panel',
      logo: 'https://via.placeholder.com/60x60/4F46E5/FFFFFF?text=AP',
      profileConfig: {
        avatar: 'https://via.placeholder.com/40x40/10B981/FFFFFF?text=U',
        username: 'Administrateur',
        actions: [
          {
            label: 'Mon Profil',
            icon: 'person',
            route: '/profile'
          },
          {
            label: 'Déconnexion',
            icon: 'logout',
            action: () => console.log('Déconnexion')
          }
        ]
      },
      menu: [
        {
          label: 'Tableau de Bord',
          icon: 'dashboard',
          route: '/dashboard'
        },
        {
          label: 'Gestion',
          icon: 'settings',
          children: [
            {
              label: 'Utilisateurs',
              icon: 'people',
              route: '/users'
            },
            {
              label: 'Produits',
              icon: 'inventory',
              route: '/products'
            }
          ]
        }
      ]
    });
    // Enregistrement des modèles de données
    this.registerModels();
  }
  private registerModels() {
    this.panelService.registerModel('user', {
      name: 'user',
      fields: [
        {
          name: 'firstName',
          type: 'text',
          label: 'Prénom',
          required: true
        },
        {
          name: 'lastName',
          type: 'text',
          label: 'Nom',
          required: true
        },
        {
          name: 'email',
          type: 'email',
          label: 'Email',
          required: true
        },
        {
          name: 'role',
          type: 'select',
          label: 'Rôle',
          options: [
            { label: 'Administrateur', value: 'admin' },
            { label: 'Utilisateur', value: 'user' }
          ]
        }
      ],
      actions: [
        {
          name: 'edit',
          label: 'Modifier',
          type: 'primary',
          icon: 'edit'
        },
        {
          name: 'delete',
          label: 'Supprimer',
          type: 'danger',
          icon: 'delete'
        }
      ],
      list: {
        pageSize: 10,
        sortable: true
      }
    });
  }
}app.routes.ts - Configuration du routing :
import { Routes } from '@angular/router';
export const routes: Routes = [
  {
    path: 'dashboard',
    loadComponent: () => import('./features/dashboard/dashboard.component')
      .then(m => m.DashboardComponent)
  },
  {
    path: 'users',
    loadComponent: () => import('./features/users/users.component')
      .then(m => m.UsersComponent)
  },
  {
    path: 'products',
    loadComponent: () => import('./features/products/products.component')
      .then(m => m.ProductsComponent)
  },
  {
    path: '',
    redirectTo: '/dashboard',
    pathMatch: 'full'
  }
];📖 Documentation Complète
- Guide de Démarrage - Configuration détaillée étape par étape
- Exemples Complets - Cas d'usage avancés et patterns
- Guide de Dépannage - Solutions aux problèmes courants
🧩 Composants Disponibles
1. Dynamic Table - Tableaux Dynamiques
Créez des tableaux interactifs avec tri, pagination et actions personnalisées.
<lib-dynamic-table
  modelName="user"
  [data]="users"
/>Fonctionnalités :
- ✅ Tri par colonnes
- ✅ Pagination automatique
- ✅ Actions sur les lignes
- ✅ Sélection multiple
- ✅ Filtrage intégré
2. Dynamic Form - Formulaires Dynamiques
Générez des formulaires avec validation à partir de modèles de données.
<lib-dynamic-form
  modelName="user"
  [initialData]="userData"
  (onSubmit)="handleSubmit($event)"
  (onCancel)="handleCancel()"
/>Types de champs supportés :
- text,- email,- password
- number,- date,- datetime-local
- select,- multiselect
- boolean(checkbox/toggle)
- textarea
- file(upload)
3. Dynamic Stats - Cartes de Statistiques
Affichez vos métriques avec des cartes visuelles attrayantes.
<lib-dynamic-stats [data]="statistiques"/>statistiques: StatItem[] = [
  {
    title: 'Utilisateurs Totaux',
    value: 1247,
    color: 'bg-blue-500',
    icon: 'people'
  },
  {
    title: 'Revenus du Mois',
    value: '€48,250',
    color: 'bg-green-500',
    icon: 'attach_money'
  }
];4. Toast Notifications - Système de Notifications
Notifications élégantes et non-intrusives.
import { ToastService } from '@enokdev/ng-panel';
constructor(private toastService: ToastService) {}
showNotifications() {
  this.toastService.success('Succès!', 'Opération réussie');
  this.toastService.error('Erreur!', 'Quelque chose s\'est mal passé');
  this.toastService.warning('Attention!', 'Vérifiez vos données');
  this.toastService.info('Info', 'Nouvelle information disponible');
}📋 Exemple Complet : Gestion d'Utilisateurs
users.component.ts :
import { Component, inject, signal } from '@angular/core';
import { DynamicTableComponent, DynamicFormComponent, ToastService } from '@enokdev/ng-panel';
@Component({
  selector: 'app-users',
  standalone: true,
  imports: [DynamicTableComponent, DynamicFormComponent],
  template: `
    <div class="p-6 space-y-6">
      <!-- En-tête avec actions -->
      <div class="flex justify-between items-center">
        <div>
          <h1 class="text-3xl font-bold">Gestion des Utilisateurs</h1>
          <p class="text-base-content/70">Gérez les comptes utilisateurs</p>
        </div>
        <div class="space-x-3">
          @if (!showForm()) {
            <button class="btn btn-outline" (click)="exportUsers()">
              <i class="material-icons text-sm">download</i>
              Exporter
            </button>
            <button class="btn btn-primary" (click)="toggleForm()">
              <i class="material-icons text-sm">add</i>
              Nouvel Utilisateur
            </button>
          }
        </div>
      </div>
      <!-- Statistiques rapides -->
      @if (!showForm()) {
        <div class="stats stats-horizontal shadow w-full">
          <div class="stat">
            <div class="stat-figure text-primary">
              <i class="material-icons text-2xl">people</i>
            </div>
            <div class="stat-title">Total</div>
            <div class="stat-value text-primary">{{ users().length }}</div>
          </div>
          <div class="stat">
            <div class="stat-figure text-success">
              <i class="material-icons text-2xl">verified</i>
            </div>
            <div class="stat-title">Actifs</div>
            <div class="stat-value text-success">{{ activeUsers() }}</div>
          </div>
        </div>
      }
      <!-- Formulaire ou tableau -->
      @if (showForm()) {
        <div class="card bg-base-100 shadow-xl">
          <div class="card-body">
            <h2 class="card-title">
              {{ selectedUser() ? 'Modifier' : 'Créer' }} un utilisateur
            </h2>
            <lib-dynamic-form
              modelName="user"
              [initialData]="selectedUser()"
              (onSubmit)="handleSubmit($event)"
              (onCancel)="toggleForm()"
            />
          </div>
        </div>
      } @else {
        <div class="card bg-base-100 shadow-xl">
          <div class="card-body">
            <lib-dynamic-table
              modelName="user"
              [data]="users()"
            />
          </div>
        </div>
      }
    </div>
  `
})
export class UsersComponent {
  private toastService = inject(ToastService);
  showForm = signal(false);
  selectedUser = signal<any>(null);
  
  users = signal([
    { 
      id: 1, 
      firstName: 'Jean', 
      lastName: 'Dupont', 
      email: 'jean.dupont@example.com', 
      role: 'admin',
      active: true
    },
    { 
      id: 2, 
      firstName: 'Marie', 
      lastName: 'Martin', 
      email: 'marie.martin@example.com', 
      role: 'user',
      active: true
    }
  ]);
  activeUsers = computed(() => 
    this.users().filter(user => user.active).length
  );
  toggleForm() {
    this.showForm.update(v => !v);
    if (!this.showForm()) {
      this.selectedUser.set(null);
    }
  }
  handleSubmit(userData: any) {
    if (this.selectedUser()) {
      // Modifier utilisateur existant
      const users = this.users().map(user => 
        user.id === this.selectedUser().id 
          ? { ...user, ...userData }
          : user
      );
      this.users.set(users);
      this.toastService.success('Succès', 'Utilisateur modifié');
    } else {
      // Créer nouvel utilisateur
      const newUser = {
        id: Math.max(...this.users().map(u => u.id)) + 1,
        ...userData
      };
      this.users.update(users => [...users, newUser]);
      this.toastService.success('Succès', 'Utilisateur créé');
    }
    this.toggleForm();
  }
  exportUsers() {
    this.toastService.info('Export', 'Export en cours...');
    // Logique d'export
  }
}Available Components
- Dynamic Table:
<lib-dynamic-table
  modelName="user"
  [data]="users"
/>- Dynamic Form:
<lib-dynamic-form
  modelName="user"
  [initialData]="userData"
  (onSubmit)="handleSubmit($event)"
  (onCancel)="handleCancel()"
/>- Dynamic Stats:
<lib-dynamic-stats
  [data]="statsData"
/>- Toast Notifications:
// Inject ToastService and use it
constructor(private toastService: ToastService) {}
showSuccess() {
  this.toastService.success('Success!', 'Operation completed successfully');
}
showError() {
  this.toastService.error('Error!', 'Something went wrong');
}🎨 Gestion des Thèmes
ng-panel supporte 32+ thèmes DaisyUI prêts à l'emploi :
Thèmes populaires :
- light/- dark- Thèmes classiques
- cupcake- Rose et doux
- corporate- Professionnel et sobre
- synthwave- Cyberpunk néon
- retro- Vintage coloré
- cyberpunk- Futuriste
- Et bien d'autres...
Configuration des thèmes :
// tailwind.config.js
daisyui: {
  themes: [
    "light",
    "dark", 
    "cupcake",
    "corporate",
    "synthwave",
    // ... ou true pour tous les thèmes
  ]
}Changement de thème programmatique :
// Changer le thème
document.documentElement.setAttribute('data-theme', 'dark');
// Ou avec un service personnalisé
@Injectable()
export class ThemeService {
  setTheme(themeName: string) {
    document.documentElement.setAttribute('data-theme', themeName);
    localStorage.setItem('theme', themeName);
  }
}Sélecteur de thème dans l'interface :
@Component({
  template: `
    <div class="dropdown">
      <label tabindex="0" class="btn btn-ghost">
        <i class="material-icons">palette</i>
        Thème
      </label>
      <ul class="dropdown-content menu p-2 shadow bg-base-100 rounded-box">
        @for (theme of themes; track theme) {
          <li>
            <a (click)="setTheme(theme.value)">{{ theme.label }}</a>
          </li>
        }
      </ul>
    </div>
  `
})
export class ThemeSelectorComponent {
  themes = [
    { value: 'light', label: 'Clair' },
    { value: 'dark', label: 'Sombre' },
    { value: 'cupcake', label: 'Cupcake' }
  ];
  setTheme(theme: string) {
    document.documentElement.setAttribute('data-theme', theme);
  }
}🔧 Configuration Avancée
Modèles de Données Complexes
this.panelService.registerModel('product', {
  name: 'product',
  fields: [
    {
      name: 'name',
      type: 'text',
      label: 'Nom du produit',
      required: true,
      validators: [Validators.minLength(3)]
    },
    {
      name: 'category',
      type: 'select',
      label: 'Catégorie',
      required: true,
      options: [
        { label: 'Électronique', value: 'electronics' },
        { label: 'Vêtements', value: 'clothing' },
        { label: 'Livres', value: 'books' }
      ]
    },
    {
      name: 'price',
      type: 'number',
      label: 'Prix (€)',
      required: true,
      min: 0,
      step: 0.01
    },
    {
      name: 'inStock',
      type: 'boolean',
      label: 'En stock'
    },
    {
      name: 'description',
      type: 'textarea',
      label: 'Description',
      rows: 4
    },
    {
      name: 'images',
      type: 'file',
      label: 'Images',
      multiple: true,
      accept: 'image/*'
    }
  ],
  actions: [
    {
      name: 'view',
      label: 'Voir',
      type: 'secondary',
      icon: 'visibility',
      handler: (item) => this.viewProduct(item)
    },
    {
      name: 'edit',
      label: 'Modifier',
      type: 'primary',
      icon: 'edit',
      handler: (item) => this.editProduct(item)
    },
    {
      name: 'delete',
      label: 'Supprimer',
      type: 'danger',
      icon: 'delete',
      confirmMessage: 'Êtes-vous sûr ?',
      handler: (item) => this.deleteProduct(item)
    }
  ],
  list: {
    pageSize: 25,
    sortable: true,
    defaultSort: { field: 'name', direction: 'asc' },
    filters: [
      {
        name: 'category',
        type: 'select',
        label: 'Catégorie',
        options: [
          { label: 'Électronique', value: 'electronics' },
          { label: 'Vêtements', value: 'clothing' }
        ]
      }
    ]
  },
  form: {
    layout: 'vertical',
    submitLabel: 'Enregistrer le produit',
    cancelLabel: 'Annuler',
    sections: [
      {
        title: 'Informations générales',
        fields: ['name', 'category', 'description']
      },
      {
        title: 'Prix et stock',
        fields: ['price', 'inStock']
      },
      {
        title: 'Médias',
        fields: ['images']
      }
    ]
  }
});Menu Hiérarchique Avancé
menu: [
  {
    label: 'Tableau de Bord',
    icon: 'dashboard',
    route: '/dashboard',
    badge: '3' // Badge de notification
  },
  {
    label: 'E-Commerce',
    icon: 'store',
    children: [
      {
        label: 'Produits',
        icon: 'inventory',
        route: '/products',
        badge: 'NEW'
      },
      {
        label: 'Catégories',
        icon: 'category',
        route: '/categories'
      },
      {
        label: 'Commandes',
        icon: 'shopping_cart',
        route: '/orders',
        badge: '12'
      }
    ]
  },
  {
    label: 'Clients',
    icon: 'people',
    children: [
      {
        label: 'Tous les clients',
        icon: 'people_outline',
        route: '/customers'
      },
      {
        label: 'Groupes',
        icon: 'group',
        route: '/customer-groups'
      }
    ]
  },
  { type: 'divider', label: 'Administration' }, // Séparateur
  {
    label: 'Paramètres',
    icon: 'settings',
    route: '/settings'
  }
]Validation Personnalisée
import { AbstractControl, ValidatorFn } from '@angular/forms';
// Validateur personnalisé
export function customEmailValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const email = control.value;
    if (email && !email.endsWith('@company.com')) {
      return { 'customEmail': { value: control.value } };
    }
    return null;
  };
}
// Utilisation dans le modèle
{
  name: 'email',
  type: 'email',
  label: 'Email professionnel',
  required: true,
  validators: [Validators.email, customEmailValidator()]
}📚 API Reference
Interfaces Principales
PanelConfig
Configuration globale du panel d'administration.
interface PanelConfig {
  title: string;                    // Titre affiché dans l'en-tête
  logo?: string;                    // URL du logo
  theme?: ThemeConfig;              // Configuration de thème personnalisé
  menu: MenuItem[];                 // Structure du menu de navigation
  profileConfig?: ProfileConfig;    // Configuration du profil utilisateur
}
interface MenuItem {
  label: string;                    // Libellé du menu
  icon?: string;                    // Icône Material Icons
  route?: string;                   // Route de navigation
  children?: MenuItem[];            // Sous-menus
  badge?: string;                   // Badge de notification
  type?: 'divider';                 // Type spécial pour séparateur
}
interface ProfileConfig {
  avatar?: string;                  // URL de l'avatar
  username?: string;                // Nom d'utilisateur
  actions: ProfileAction[];         // Actions du menu profil
}
interface ProfileAction {
  label: string;                    // Libellé de l'action
  icon?: string;                    // Icône Material Icons
  route?: string;                   // Route de navigation
  action?: () => void;              // Fonction à exécuter
  type?: 'divider';                 // Type spécial pour séparateur
}ModelConfig
Configuration des modèles de données pour les formulaires et tableaux.
interface ModelConfig {
  name: string;                     // Nom unique du modèle
  fields: FieldConfig[];            // Définition des champs
  actions?: ActionConfig[];         // Actions disponibles (modifier, supprimer, etc.)
  list?: ListConfig;                // Configuration d'affichage en liste
  form?: FormConfig;                // Configuration du formulaire
}
interface FieldConfig {
  name: string;                     // Nom du champ (propriété de l'objet)
  type: FieldType;                  // Type de champ
  label: string;                    // Libellé affiché
  required?: boolean;               // Champ obligatoire
  placeholder?: string;             // Texte d'aide
  options?: SelectOption[];         // Options pour select/multiselect
  validators?: ValidatorFn[];       // Validateurs Angular
  min?: number;                     // Valeur minimale (number)
  max?: number;                     // Valeur maximale (number)
  step?: number;                    // Pas d'incrémentation (number)
  rows?: number;                    // Nombre de lignes (textarea)
  multiple?: boolean;               // Sélection multiple (file, select)
  accept?: string;                  // Types de fichiers acceptés (file)
  maxFiles?: number;                // Nombre maximum de fichiers (file)
  dependsOn?: string;               // Champ conditionnel dépendant
}
type FieldType = 
  | 'text' | 'email' | 'password' | 'number' 
  | 'date' | 'datetime-local' | 'time'
  | 'boolean' | 'select' | 'multiselect'
  | 'textarea' | 'file' | 'group';
interface SelectOption {
  label: string;                    // Libellé affiché
  value: any;                       // Valeur de l'option
}
interface ActionConfig {
  name: string;                     // Nom unique de l'action
  label: string;                    // Libellé affiché
  type: 'primary' | 'secondary' | 'danger' | 'warning';
  icon?: string;                    // Icône Material Icons
  handler?: (item: any) => void;    // Fonction à exécuter
  condition?: (item: any) => boolean; // Condition d'affichage
  confirmMessage?: string;          // Message de confirmation
}
interface ListConfig {
  pageSize?: number;                // Taille de page (défaut: 10)
  sortable?: boolean;               // Tri activé (défaut: true)
  defaultSort?: {                   // Tri par défaut
    field: string;
    direction: 'asc' | 'desc';
  };
  columns?: ColumnConfig[];         // Configuration des colonnes
  filters?: FieldConfig[];          // Filtres disponibles
}
interface ColumnConfig {
  name: string;                     // Nom du champ
  label: string;                    // Libellé de la colonne
  sortable?: boolean;               // Colonne triable
  type?: 'text' | 'number' | 'date' | 'currency' | 'badge';
  width?: string;                   // Largeur CSS
}
interface FormConfig {
  layout?: 'horizontal' | 'vertical'; // Disposition du formulaire
  submitLabel?: string;             // Libellé du bouton de soumission
  cancelLabel?: string;             // Libellé du bouton d'annulation
  sections?: FormSection[];         // Sections du formulaire
}
interface FormSection {
  title: string;                    // Titre de la section
  fields: string[];                 // Champs de la section
  collapsible?: boolean;            // Section pliable
  collapsed?: boolean;              // État initial (plié)
}StatItem
Configuration des cartes de statistiques.
interface StatItem {
  title: string;                    // Titre de la statistique
  value: string | number;           // Valeur à afficher
  color: string;                    // Couleur de fond (classe CSS)
  icon?: string;                    // Icône Material Icons
  change?: {                        // Évolution (optionnel)
    value: number;
    type: 'increase' | 'decrease';
  };
  subtitle?: string;                // Sous-titre (optionnel)
}Services Principaux
PanelService
Service principal pour la configuration du panel.
class PanelService {
  // Configuration globale
  setConfig(config: PanelConfig): void
  config(): Signal<PanelConfig>
  // Gestion des modèles
  registerModel(name: string, config: ModelConfig): void
  getModel(name: string): ModelConfig | undefined
  models(): Signal<Map<string, ModelConfig>>
}ToastService
Service de gestion des notifications.
class ToastService {
  success(title: string, message?: string, duration?: number): void
  error(title: string, message?: string, duration?: number): void
  warning(title: string, message?: string, duration?: number): void
  info(title: string, message?: string, duration?: number): void
  
  // Notification personnalisée
  show(notification: ToastNotification): void
}
interface ToastNotification {
  type: 'success' | 'error' | 'warning' | 'info';
  title: string;
  message?: string;
  duration?: number;        // Durée en millisecondes (défaut: 5000)
  persistent?: boolean;     // Ne se ferme pas automatiquement
}⚡ Optimisation et Bonnes Pratiques
Performance
Lazy Loading
// Routes avec lazy loading
const routes: Routes = [
  {
    path: 'users',
    loadComponent: () => import('./users/users.component')
      .then(m => m.UsersComponent)
  }
];Change Detection
// Utiliser OnPush pour de meilleures performances
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class OptimizedComponent { }Signals vs Observables
// ✅ Préférer les signals pour l'état local
users = signal<User[]>([]);
filteredUsers = computed(() => 
  this.users().filter(user => user.active)
);
// ✅ Observables pour les opérations asynchrones
data$ = this.http.get('/api/data');Architecture Recommandée
src/
├── app/
│   ├── core/                    # Services globaux, guards, interceptors
│   │   ├── auth/
│   │   ├── guards/
│   │   └── interceptors/
│   ├── shared/                  # Composants, pipes, directives partagés
│   │   ├── components/
│   │   ├── pipes/
│   │   └── models/
│   ├── features/                # Modules métier
│   │   ├── dashboard/
│   │   ├── users/
│   │   └── products/
│   └── layout/                  # Configuration du layout
└── assets/                      # Ressources statiquesGestion d'État
// Service de gestion d'état avec signals
@Injectable({
  providedIn: 'root'
})
export class UserStateService {
  private usersSignal = signal<User[]>([]);
  private loadingSignal = signal(false);
  readonly users = this.usersSignal.asReadonly();
  readonly loading = this.loadingSignal.asReadonly();
  async loadUsers() {
    this.loadingSignal.set(true);
    try {
      const users = await this.userService.getUsers();
      this.usersSignal.set(users);
    } finally {
      this.loadingSignal.set(false);
    }
  }
  addUser(user: User) {
    this.usersSignal.update(users => [...users, user]);
  }
  updateUser(id: number, updates: Partial<User>) {
    this.usersSignal.update(users =>
      users.map(user => 
        user.id === id ? { ...user, ...updates } : user
      )
    );
  }
}🎯 Migration et Mise à Jour
Depuis ng-panel v0.x vers v1.x
- Mise à jour des imports :
// Avant
import { PanelModule } from '@enokdev/ng-panel';
// Après
import { PanelLayoutComponent, PanelService } from '@enokdev/ng-panel';- Migration vers les composants standalone :
// Avant (NgModule)
@NgModule({
  imports: [PanelModule],
  declarations: [AppComponent]
})
// Après (Standalone)
@Component({
  standalone: true,
  imports: [PanelLayoutComponent]
})- Migration vers les signals :
// Avant
users$ = new BehaviorSubject<User[]>([]);
// Après
users = signal<User[]>([]);Compatibilité des Versions
| ng-panel | Angular | Node.js | TailwindCSS | DaisyUI | 
|---|---|---|---|---|
| 0.1.x | 19.x, 20.x | 18.x, 20.x | 3.x, 4.1+ | 4.x | 
| 1.x | 19.x, 20.x | 18.x, 20.x | 3.x, 4.1+ | 4.x | 
| 2.x | 20.x | 20.x | 4.1+ | 4.x | 
| 2.1+ | 21.x | 22.x | 4.1+ | 4.x | 
🛠️ Développement et Contribution
Configuration de l'Environnement de Développement
# Cloner le repository
git clone https://github.com/tky0065/ng-panel.git
cd ng-panel
# Installer les dépendances
npm install
# Lancer le build en mode watch
npm run build:lib:watch
# Lancer l'application de test
npm run serve:test-appStructure du Projet
ng-panel/
├── projects/
│   ├── ng-panel/              # Bibliothèque principale
│   │   ├── src/
│   │   │   ├── lib/
│   │   │   │   ├── components/
│   │   │   │   ├── services/
│   │   │   │   └── models/
│   │   │   └── public-api.ts
│   │   └── README.md
│   └── test-app/              # Application de test
└── docs/                      # DocumentationGuidelines de Contribution
- Fork le repository
- Créez une branche pour votre fonctionnalité : git checkout -b feature/amazing-feature
- Committez vos changements : git commit -m 'feat: add amazing feature'
- Pushez vers la branche : git push origin feature/amazing-feature
- Ouvrez une Pull Request
Convention de Commit
Suivez la convention Conventional Commits :
- feat:Nouvelle fonctionnalité
- fix:Correction de bug
- docs:Mise à jour de la documentation
- style:Changements de formatage
- refactor:Refactoring du code
- test:Ajout ou modification de tests
- chore:Tâches de maintenance
🌟 Features
🌟 Fonctionnalités Complètes
- ✅ Design Responsive avec TailwindCSS
- ✅ 32+ Thèmes DaisyUI prêts à l'emploi
- ✅ Génération Dynamique de formulaires avec validation
- ✅ Tableaux Dynamiques avec tri, pagination et actions
- ✅ Notifications Toast élégantes et configurables
- ✅ Gestion de Profil utilisateur intégrée
- ✅ Menu Hiérarchique avec sous-menus et badges
- ✅ Gestion d'Erreurs gracieuse et user-friendly
- ✅ Support TypeScript complet avec types stricts
- ✅ Composants Standalone Angular 17+
- ✅ Signals pour une gestion d'état moderne
- ✅ Lazy Loading pour des performances optimales
- ✅ Accessibilité (ARIA, navigation clavier)
- ✅ Internationalisation prête
- ✅ Dark/Light Mode automatique
- ✅ Progressive Web App ready
🌐 Compatibilité Navigateurs
- ✅ Chrome (dernière version)
- ✅ Firefox (dernière version)
- ✅ Safari (dernière version)
- ✅ Edge (dernière version)
- ✅ Mobile Safari (iOS 12+)
- ✅ Chrome Mobile (Android 8+)
📋 Roadmap
Version 1.1 (En cours)
- Composant de graphiques intégré
- Drag & Drop pour les listes
- Export Excel/CSV natif
- Templates de pages pré-construits
Version 1.2 (À venir)
- Mode offline avec synchronisation
- Composants de calendrier/planning
- Système de permissions granulaire
- Widget dashboard configurables
Version 2.0 (Future)
- Support Angular 21+
- SSR/SSG optimizations
- Micro-frontends support
- Advanced analytics dashboard
🤝 Contribution
Nous accueillons toutes les contributions ! Consultez notre Guide de Contribution pour plus de détails.
Contributors
📄 Licence
Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus de détails.
🙏 Remerciements
- Angular Team pour le framework extraordinaire
- DaisyUI pour les composants UI magnifiques
- TailwindCSS pour le système de design flexible
- Material Icons pour les icônes
📞 Support et Communauté
- 📖 Documentation : Documentation complète
- 🐛 Issues : GitHub Issues
- 💬 Discussions : GitHub Discussions
- 📧 Email : support@enokdev.com
- 🌐 Site Web : enokdev.com
Fait avec ❤️ par EnokDev
Si ce projet vous aide, n'hésitez pas à lui donner une ⭐ !