feat: add refresh functionality to various components

- Implemented refresh event emission in multiple list components across partner and superAdmin modules.
- Updated consumers and customers list components to handle refresh actions.
- Enhanced dashboard component to fetch partner info on initialization.
- Introduced new API method in PartnerService to retrieve current partner data.
- Modified PartnerStore to utilize the new API method for fetching partner information.
- Updated UI elements to reflect changes in partner and license management, including new fields for license renewals.
- Added a new POS display component for better presentation of POS terminal information.
- Updated layout service and top bar to reflect new titles and improve user experience.
- Refactored existing components to ensure consistency and maintainability.
This commit is contained in:
2026-04-24 23:01:44 +03:30
parent 5bb5f10dbf
commit a816c05777
73 changed files with 559 additions and 97 deletions
+1
View File
@@ -0,0 +1 @@
export * from './pos-display';
+133
View File
@@ -0,0 +1,133 @@
# POS Display Component
A reusable component for displaying Point-of-Sale (POS) terminal information with configurable variants.
## Overview
The `PosDisplayComponent` displays POS entity information in a card-based layout with support for different visibility variants ('consumer', 'partner', 'full').
## Features
- **Variant Support**: Choose from 'consumer', 'partner', or 'full' display modes
- **Conditional Field Visibility**: Fields are dynamically shown/hidden based on variant
- **Edit Mode**: Toggle between view and edit modes
- **Responsive Grid**: Displays up to 3 columns with gap-4 spacing
- **More Actions**: Optional button for additional actions
## Usage
### Basic Usage
```typescript
import { PosDisplayComponent, IPosEntity } from '@/app/components';
@Component({
selector: 'app-my-component',
template: `
<app-pos-display
variant="consumer"
[pos]="posData"
[editMode]="isEditing"
(onMoreAction)="handleMoreAction()"
(editModeChange)="editModeChange($event)"
/>
`,
standalone: true,
imports: [PosDisplayComponent],
})
export class MyComponent {
posData = signal<IPosEntity>({
id: '123',
name: 'Terminal 1',
pos_type: 'PSP',
serial_number: 'SN123',
model: 'Model X',
status: 'active',
complex: { id: 'c1', name: 'Complex 1' },
device: { id: 'd1', name: 'Device A' },
provider: { id: 'p1', name: 'Provider A' },
});
isEditing = signal(false);
handleMoreAction(): void {
console.log('More action clicked');
}
editModeChange(editing: boolean): void {
this.isEditing.set(editing);
}
}
```
## Variants
### Consumer Variant
Displays: name, pos_type, serial_number, device, model, provider (6 fields)
### Partner Variant
Displays: name, pos_type, serial_number (3 fields)
### Full Variant
Displays: name, pos_type, serial_number, device, model, provider (6 fields - same as consumer)
## Inputs
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `variant` | 'consumer' \| 'partner' \| 'full' | 'full' | Display variant mode |
| `pos` | Signal<IPosEntity \| undefined> | undefined | POS entity data |
| `editMode` | Signal<boolean> | false | Edit mode state |
| `cardTitle` | string | 'اطلاعات پایانه‌ی فروش' | Card header title |
| `showMoreActions` | boolean | true | Show more actions button |
## Outputs
| Output | Payload | Description |
|--------|---------|-------------|
| `editModeChange` | boolean | Emitted when edit mode toggled |
| `onMoreAction` | void | Emitted when more actions button clicked |
| `onSubmit` | void | Emitted on form submission |
## Model (IPosEntity)
```typescript
interface IPosEntity {
id: string;
name: string;
serial_number: string;
model?: string;
status: string;
pos_type: string;
complex: ISummary;
device?: ISummary;
provider?: ISummary;
}
```
## Integration in Existing Component
To replace the inline display in your POS single view:
```typescript
// Before: Manual field rendering
<app-key-value label="عنوان" [value]="pos()?.name" />
<app-key-value label="نوع پایانه" [value]="pos()?.pos_type" />
// ... more fields
// After: Use the component
<app-pos-display
variant="full"
[pos]="pos"
[editMode]="editMode"
(onMoreAction)="toPosLanding()"
/>
```
## Files
- `pos-display.component.ts` - Component logic
- `pos-display.component.html` - Template
- `pos-display.component.scss` - Styles
- `models/posEntity.model.ts` - TypeScript interfaces and types
- `index.ts` - Barrel export
+2
View File
@@ -0,0 +1,2 @@
export * from './models/posEntity.model';
export * from './pos-display.component';
@@ -0,0 +1,27 @@
import ISummary from '@/core/models/summary';
export type PosVariant = 'consumer' | 'partner' | 'full';
export interface IPosEntity {
id: string;
name: string;
serial_number: string;
model?: string;
status: string;
pos_type: string;
complex: ISummary;
device?: ISummary;
provider?: ISummary;
}
export interface IPosDisplayConfig {
variant: PosVariant;
editable?: boolean;
showMoreActions?: boolean;
}
export const POS_VARIANT_FIELDS: Record<PosVariant, Array<keyof IPosEntity>> = {
consumer: ['name', 'pos_type', 'serial_number', 'device', 'model', 'provider'],
partner: ['name', 'pos_type', 'serial_number'],
full: ['name', 'pos_type', 'serial_number', 'device', 'model', 'provider'],
};
@@ -0,0 +1,17 @@
<app-card-data [cardTitle]="cardTitle" [editable]="true" [editMode]="isEditing()" (editModeChange)="toggleEditMode()">
<ng-template #moreActions>
@if (showMoreActions) {
<p-button type="button" variant="outlined" (onClick)="handleMoreAction()"> ورود به پایانه </p-button>
}
</ng-template>
<div class="flex flex-col gap-4">
<div class="grid grid-cols-3 gap-4 items-center">
@for (field of visibleFields(); track field) {
@if (isFieldVisible(field)) {
<app-key-value [label]="fieldLabels[field]" [value]="getFieldValue(field)" />
}
}
</div>
</div>
</app-card-data>
@@ -0,0 +1,78 @@
import { AppCardComponent, KeyValueComponent } from '@/shared/components';
import { CommonModule } from '@angular/common';
import { Component, effect, EventEmitter, Input, Output, signal } from '@angular/core';
import { Button } from 'primeng/button';
import { IPosEntity, POS_VARIANT_FIELDS, PosVariant } from './models/posEntity.model';
@Component({
selector: 'app-pos-entity-display',
standalone: true,
imports: [CommonModule, AppCardComponent, Button, KeyValueComponent],
templateUrl: './pos-display.component.html',
})
export class PosDisplayComponent {
@Input() variant: PosVariant = 'full';
@Input() pos = signal<IPosEntity | undefined>(undefined);
@Input() editMode = signal<boolean>(false);
@Input() cardTitle = 'اطلاعات پایانه‌ی فروش';
@Input() showMoreActions = true;
@Output() editModeChange = new EventEmitter<boolean>();
@Output() onMoreAction = new EventEmitter<void>();
@Output() onSubmit = new EventEmitter<void>();
visibleFields = signal<Array<keyof IPosEntity>>([]);
isEditing = signal<boolean>(false);
constructor() {
effect(() => {
this.visibleFields.set(POS_VARIANT_FIELDS[this.variant] || POS_VARIANT_FIELDS.full);
});
effect(() => {
this.isEditing.set(this.editMode());
});
}
get fieldLabels(): Record<string, string> {
return {
name: 'عنوان',
pos_type: 'نوع پایانه',
serial_number: 'شماره سریال',
device: 'نوع دستگاه',
model: 'مدل دستگاه',
provider: 'ارایه‌دهنده',
};
}
isFieldVisible(field: keyof IPosEntity): boolean {
return this.visibleFields().includes(field);
}
getFieldValue(field: keyof IPosEntity): any {
const posData = this.pos();
if (!posData) return null;
if (field === 'device' && posData.device) {
return posData.device.name;
}
if (field === 'provider' && posData.provider) {
return posData.provider.name;
}
return posData[field];
}
toggleEditMode(): void {
const newMode = !this.isEditing();
this.isEditing.set(newMode);
this.editModeChange.emit(newMode);
}
handleMoreAction(): void {
this.onMoreAction.emit();
}
handleSubmit(): void {
this.onSubmit.emit();
}
}