Skip to main content

Overview

Shared components are highly reusable UI components located in src/app/shared/. They provide consistent functionality across the entire application.

Generic List Component

hh-generic-list - A versatile, paginated data table component. Location: src/app/shared/components/generic-list/generic-list.component.ts:1

Props

export interface ColumnConfig {
  key: string;              // Property key from data object
  label: string;            // Display label for column header
  sortable?: boolean;       // Enable sorting (future feature)
  formatter?: (value: any, item: any) => string;  // Custom formatter
  isHtml?: boolean;         // Allow HTML in formatted content
}
InputTypeDefaultDescription
itemsT[][]Array of items to display
columnsColumnConfig[][]Column configuration (auto-detect if empty)
itemsPerPagenumber10Items per page
titlestring?-Optional table title
showActionsbooleanfalseShow edit/delete buttons
canEdit(item: T) => boolean-Function to determine if item is editable
canDelete(item: T) => boolean-Function to determine if item is deletable

Outputs

OutputTypeDescription
rowClickEventEmitter<T>Emitted when a row is clicked
pageChangeEventEmitter<number>Emitted when page changes
itemsPerPageChangeEventEmitter<number>Emitted when items per page changes
editEventEmitter<T>Emitted when edit button is clicked
deleteEventEmitter<T>Emitted when delete button is clicked

Usage Example

// Component TypeScript
vehicleColumns: ColumnConfig[] = [
  { key: 'brand', label: 'Marca', sortable: true },
  { key: 'model', label: 'Modelo', sortable: true },
  { key: 'licensePlate', label: 'Placas', sortable: false },
  { 
    key: 'year', 
    label: 'Año', 
    formatter: (value) => value.toString() 
  }
];

vehicles = signal<Vehicle[]>([]);

onVehicleClick(vehicle: Vehicle) {
  console.log('Vehicle clicked:', vehicle);
}

onEditVehicle(vehicle: Vehicle) {
  // Open edit modal
}

onDeleteVehicle(vehicle: Vehicle) {
  // Confirm and delete
}
<!-- Component Template -->
<hh-generic-list
  [items]="vehicles()"
  [columns]="vehicleColumns"
  [title]="'Mis Vehículos'"
  [itemsPerPage]="5"
  [showActions]="true"
  (rowClick)="onVehicleClick($event)"
  (edit)="onEditVehicle($event)"
  (delete)="onDeleteVehicle($event)"
></hh-generic-list>
Source: src/app/components/resident/registro-auto/registrar-auto.component.html:29

Features

  • Auto-detection: Automatically detects columns from data if not specified
  • Pagination: Built-in pagination with customizable page size
  • Actions: Optional edit/delete buttons with conditional rendering
  • Custom Formatters: Transform cell values with formatter functions
  • HTML Support: Render HTML in cells when isHtml: true
  • Responsive: Mobile-friendly responsive design

File Upload Component

hh-file-upload - Multi-file upload with drag-and-drop support and validation. Location: src/app/shared/components/file-upload/file-upload.component.ts:1

Props

export type AllowedFileType = 'image' | 'video' | 'document';

export interface MaxSizesByType {
  image?: number;     // Bytes
  video?: number;     // Bytes
  document?: number;  // Bytes
}
InputTypeDefaultDescription
maxFilesnumber10Maximum number of files
allowedTypesAllowedFileType[]['document']Allowed file types
savePathstring''Path where files will be saved (required)
maxSizesByTypeMaxSizesByType?See defaultsMax file sizes by type
componentIdstring'file-upload'Unique ID for multiple instances
Default Max Sizes:
  • Images: 5 MB
  • Videos: 10 MB
  • Documents: 5 MB

Outputs

OutputTypeDescription
filesChangeoutput<File[]>Emits array of valid selected files

Public Methods

getSelectedFiles(): File[]  // Returns currently selected files
reset(): void               // Clears all selected files

Usage Example

// Component TypeScript
fileUploadMaxFiles = 3;
fileUploadAllowedTypes: AllowedFileType[] = ['image', 'document'];
fileUploadSavePath = signal('/uploads/pagos');
fileUploadMaxSizes: MaxSizesByType = {
  image: 5 * 1024 * 1024,    // 5 MB
  document: 5 * 1024 * 1024   // 5 MB
};

selectedFiles = signal<File[]>([]);

onFilesChange(files: File[]) {
  this.selectedFiles.set(files);
  console.log('Selected files:', files);
}
<!-- Component Template -->
<hh-file-upload
  [maxFiles]="fileUploadMaxFiles"
  [allowedTypes]="fileUploadAllowedTypes"
  [savePath]="fileUploadSavePath()"
  [maxSizesByType]="fileUploadMaxSizes"
  componentId="pago-residente-comprobante"
  (filesChange)="onFilesChange($event)"
></hh-file-upload>
Source: src/app/components/pagos-residente/pago-residente-form/pago-residente-form.component.html:94

Features

  • Drag & Drop: Full drag-and-drop support
  • Multiple Files: Support for multiple file uploads
  • Type Validation: Validates file types (image/video/document)
  • Size Validation: Per-type file size limits
  • Error Handling: Clear error messages for validation failures
  • Preview: Shows selected file names
  • Dynamic Slots: Add/remove file input slots dynamically

hh-banner-carousel - Displays promotional banners with date filtering. Location: src/app/shared/components/banner-carousel/banner-carousel.component.ts:1

Props

export enum BannerPeriod {
  Month = 'month',        // 30 days
  Bimester = 'bimester',  // 60 days
  Quarter = 'quarter',    // 90 days
  HalfYear = 'halfYear',  // 180 days
  Year = 'year'           // 365 days
}
InputTypeDefaultDescription
periodBannerPeriodBannerPeriod.MonthTime period for banner filtering

Usage Example

<hh-banner-carousel [period]="BannerPeriod.Quarter"></hh-banner-carousel>

Features

  • Date Filtering: Shows only active banners within selected period
  • Smooth Scrolling: Carousel navigation with smooth scroll
  • Responsive: Mobile-friendly horizontal scroll
  • Loading State: Shows spinner while loading banners
  • Error Handling: Gracefully handles loading errors
  • UTC Dates: Proper timezone handling

Community Filter Component

hh-community-filter - Dropdown selector for filtering by community. Location: src/app/shared/components/community-filter/community-filter.component.ts:1

Props

export interface CommunityFilterItem {
  id?: string;
  nombre: string;
}
InputTypeRequiredDescription
communitiesCommunityFilterItem[]YesList of communities
selectedIdstringYesCurrently selected ID
showAllOptionbooleanNo (false)Show “All communities” option

Outputs

OutputTypeDescription
selectedIdChangeoutput<string>Emits selected community ID or ‘all’

Usage Example

communities = signal<CommunityFilterItem[]>([
  { id: '1', nombre: 'Community A' },
  { id: '2', nombre: 'Community B' }
]);

selectedCommunityId = signal<string>('1');

onCommunityChange(id: string) {
  this.selectedCommunityId.set(id);
  // Reload data for selected community
}
<hh-community-filter
  [communities]="communities()"
  [selectedId]="selectedCommunityId()"
  [showAllOption]="true"
  (selectedIdChange)="onCommunityChange($event)"
></hh-community-filter>

Top Menu Component

hh-top-menu - Horizontal navigation menu for sub-sections. Location: src/app/shared/components/top-menu/top-menu.component.ts:1

Props

export interface TopMenuItem {
  route: string;   // Router path
  label: string;   // Display text
  icon: string;    // Font Awesome icon class
  badge?: string;  // Optional badge (e.g., notification count)
}
InputTypeRequiredDescription
menuItemsTopMenuItem[]YesArray of menu items

Usage Example

menuItems: TopMenuItem[] = [
  { route: '/admin/pagos', label: 'Pagos', icon: 'fa-money-bill', badge: '3' },
  { route: '/admin/morosos', label: 'Morosos', icon: 'fa-exclamation-triangle' },
  { route: '/admin/banco', label: 'Banco', icon: 'fa-bank' }
];
<hh-top-menu [menuItems]="menuItems"></hh-top-menu>

Notification Component

hh-notification - Toast-style notification display. Location: src/app/shared/components/notification/notification.component.ts:1

Props

export enum NotificationType {
  SUCCESS = 'success',
  ERROR = 'error',
  WARNING = 'warning',
  INFO = 'info'
}

export interface Notification {
  id: string;
  type: NotificationType;
  message: string;
  duration?: number;  // Auto-dismiss time in ms
}
InputTypeRequiredDescription
notificationNotificationYesNotification object

Usage with Service

private notificationService = inject(NotificationService);

// Show success notification
this.notificationService.success('Vehicle saved successfully!');

// Show error notification
this.notificationService.error('Failed to save vehicle');

// Show warning
this.notificationService.warning('Please review the form');

// Show info
this.notificationService.info('Processing your request...');

Features

  • Auto-dismiss: Configurable auto-dismiss duration
  • Manual Dismiss: Close button
  • Type Styling: Different colors/icons per notification type
  • Icon Mapping: Automatic Font Awesome icon selection
  • Container: Use with hh-notification-container for stacking

Header Component

hh-header - Application header with branding and navigation. Location: src/app/shared/header/header.component.ts:1

Properties

title = 'Happy Habitat'
subtitle = 'Administrando comunidades en armonia'

Usage

<hh-header></hh-header>
Contains:
  • hh-nav-bar - Theme selector and top navigation
  • hh-user-info - User profile information

hh-footer - Application footer. Location: src/app/shared/footer/footer.component.ts:1

Usage

<hh-footer></hh-footer>

Left Menu Component

hh-left-menu - Main sidebar navigation menu. Location: src/app/shared/left-menu/left-menu.component.ts:1

Features

  • Role-based: Different menu items based on user role
  • Active Route: Highlights current active route
  • Computed Menu: Uses computed signal to filter menu based on route
  • Panic Button: Emergency alert button for residents

Usage

<hh-left-menu></hh-left-menu>
Menu Structure: Defined in src/app/shared/data/menu-options.data.ts
hh-nav-bar - Top navigation bar with theme selector. Location: src/app/shared/nav-bar/nav-bar.component.ts:1

Features

  • Theme Switching: DaisyUI theme selector (6 themes)
  • LocalStorage: Persists theme preference
  • Signal-based: Reactive theme updates

Available Themes

themes = [
  { value: 'lemonade', label: 'Lemonade' },
  { value: 'dark', label: 'Oscuro' },
  { value: 'winter', label: 'Winter' },
  { value: 'forest', label: 'Bosque' },
  { value: 'autumn', label: 'Autumn' },
  { value: 'lofi', label: 'Lofi' }
];

Right Bar Component

hh-right-bar - Sidebar for notifications and announcements. Location: src/app/shared/right-bar/right-bar.component.ts:1

Usage

<hh-right-bar></hh-right-bar>
Contains:
  • hh-avisos-list - List of recent announcements/notices

Pagination Component

hh-pagination - Pagination controls (basic implementation). Location: src/app/shared/pagination/pagination.component.ts:1

Props

InputTypeRequiredDescription
totalPagesnumberYesTotal number of pages
currentPagenumberYesCurrent active page

Usage

<hh-pagination 
  [totalPages]="totalPages()" 
  [currentPage]="currentPage()"
></hh-pagination>
Note: For full-featured pagination, use hh-generic-list which has built-in pagination.
hh-breadcrums - Breadcrumb navigation (basic implementation). Location: src/app/shared/breadcrums/breadcrums.component.ts:1

Usage

<hh-breadcrums></hh-breadcrums>

Chart Components

Chart visualization components using Chart.js.

Area Chart

Location: src/app/shared/charts/area-chart.component.ts:1

Bar Chart

Location: src/app/shared/charts/bar-chart.component.ts:1

Usage

<hh-area-chart [data]="chartData"></hh-area-chart>
<hh-bar-chart [data]="barChartData"></hh-bar-chart>

Best Practices

Using Shared Components

  1. Import as Standalone: All shared components are standalone
    import { GenericListComponent } from '@app/shared/components/generic-list/generic-list.component';
    
    @Component({
      imports: [GenericListComponent]
    })
    
  2. Type Safety: Always define interfaces for component data
    interface Vehicle {
      id: string;
      brand: string;
      model: string;
      year: number;
    }
    
  3. Signal Inputs: Components use modern signal-based inputs
    items = signal<Vehicle[]>([]);
    
  4. Event Handling: Use output signals for events
    (rowClick)="onRowClick($event)"
    
  5. Responsive Design: Leverage Tailwind’s responsive utilities
    <div class="w-full sm:w-1/2 lg:w-1/3"></div>
    

Next Steps

Build docs developers (and LLMs) love