bways-report 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +291 -422
- package/esm2022/lib/core/config/bways-report-config.mjs +83 -0
- package/esm2022/lib/core/index.mjs +2 -1
- package/esm2022/lib/core/models/report-definition.model.mjs +1 -1
- package/esm2022/lib/core/services/index.mjs +2 -1
- package/esm2022/lib/core/services/report-data-discovery.service.mjs +138 -0
- package/esm2022/lib/core/services/report-session-context.service.mjs +11 -7
- package/esm2022/lib/core/tokens/reporting-config.token.mjs +1 -1
- package/esm2022/lib/core/utils/data-normalization.util.mjs +87 -1
- package/esm2022/lib/core/utils/identifier-humanizer.mjs +15 -4
- package/esm2022/lib/core/utils/report-parameter.util.mjs +119 -3
- package/esm2022/lib/data-access/adapters/endpoint-datasource.adapter.mjs +69 -0
- package/esm2022/lib/data-access/adapters/endpoint-lookup.adapter.mjs +98 -0
- package/esm2022/lib/data-access/adapters/endpoint-masterdata.adapter.mjs +57 -0
- package/esm2022/lib/data-access/adapters/endpoint-persistence.adapter.mjs +85 -0
- package/esm2022/lib/data-access/adapters/endpoint-schema-discovery.adapter.mjs +163 -0
- package/esm2022/lib/data-access/adapters/endpoint-tokens.mjs +12 -0
- package/esm2022/lib/data-access/adapters/http-lookup.adapter.mjs +20 -30
- package/esm2022/lib/data-access/adapters/index.mjs +8 -1
- package/esm2022/lib/data-access/contracts/lookup.contract.mjs +1 -1
- package/esm2022/lib/features/designer/components/component-palette/component-palette.component.mjs +12 -2
- package/esm2022/lib/features/designer/components/design-canvas/design-canvas.component.mjs +71 -13
- package/esm2022/lib/features/designer/components/dialogs/report-saved-dialog.component.mjs +407 -0
- package/esm2022/lib/features/designer/components/property-editor/parameter-binding-editor.component.mjs +403 -222
- package/esm2022/lib/features/designer/components/property-editor/property-editor-action.component.mjs +41 -34
- package/esm2022/lib/features/designer/components/property-editor/property-editor-datasource.component.mjs +142 -20
- package/esm2022/lib/features/designer/components/property-editor/property-editor-general.component.mjs +3 -3
- package/esm2022/lib/features/designer/components/property-editor/property-editor-grid.component.mjs +154 -5
- package/esm2022/lib/features/designer/components/property-editor/property-editor-layout.component.mjs +11 -5
- package/esm2022/lib/features/designer/components/property-editor/property-editor-report.component.mjs +3 -3
- package/esm2022/lib/features/designer/components/property-editor/property-editor-validation.component.mjs +15 -3
- package/esm2022/lib/features/designer/components/property-editor/property-editor.component.mjs +112 -39
- package/esm2022/lib/features/designer/components/toolbar/designer-toolbar.component.mjs +204 -10
- package/esm2022/lib/features/designer/designer.component.mjs +44 -3
- package/esm2022/lib/features/designer/services/designer-state.service.mjs +70 -3
- package/esm2022/lib/features/embed/bways-report-builder.component.mjs +207 -0
- package/esm2022/lib/features/embed/bways-report-menu.component.mjs +433 -0
- package/esm2022/lib/features/embed/bways-report-page.component.mjs +204 -0
- package/esm2022/lib/features/embed/index.mjs +5 -0
- package/esm2022/lib/features/embed/inject-report-menu.mjs +99 -0
- package/esm2022/lib/features/index.mjs +2 -1
- package/esm2022/lib/features/persistence/services/report-store.service.mjs +43 -5
- package/esm2022/lib/features/routing/components/routed-report-runtime.component.mjs +4 -4
- package/esm2022/lib/features/routing/index.mjs +2 -2
- package/esm2022/lib/features/routing/models/report-route.models.mjs +1 -1
- package/esm2022/lib/features/routing/report-routes.mjs +42 -1
- package/esm2022/lib/features/routing/resolvers/report.resolver.mjs +6 -8
- package/esm2022/lib/features/routing/services/report-menu.service.mjs +14 -4
- package/esm2022/lib/features/runtime/dynamic-renderer.component.mjs +22 -3
- package/esm2022/lib/features/runtime/report-grid-bridge.component.mjs +121 -15
- package/esm2022/lib/primitives/components/button/rpt-button.component.mjs +28 -5
- package/esm2022/lib/primitives/components/dropdown/rpt-dropdown.component.mjs +113 -20
- package/esm2022/lib/primitives/components/icon/rpt-icon.component.mjs +8 -1
- package/esm2022/lib/primitives/components/index.mjs +2 -1
- package/esm2022/lib/primitives/components/number/rpt-number.component.mjs +9 -3
- package/esm2022/lib/primitives/components/searchable-select/rpt-searchable-select.component.mjs +386 -0
- package/esm2022/lib/provide-reporting.mjs +156 -11
- package/fesm2022/bways-report.mjs +5703 -1766
- package/fesm2022/bways-report.mjs.map +1 -1
- package/lib/core/config/bways-report-config.d.ts +161 -0
- package/lib/core/index.d.ts +1 -0
- package/lib/core/models/report-definition.model.d.ts +16 -0
- package/lib/core/services/index.d.ts +1 -0
- package/lib/core/services/report-data-discovery.service.d.ts +38 -0
- package/lib/core/services/report-session-context.service.d.ts +1 -0
- package/lib/core/tokens/reporting-config.token.d.ts +18 -0
- package/lib/core/utils/data-normalization.util.d.ts +10 -0
- package/lib/core/utils/report-parameter.util.d.ts +18 -1
- package/lib/data-access/adapters/endpoint-datasource.adapter.d.ts +13 -0
- package/lib/data-access/adapters/endpoint-lookup.adapter.d.ts +14 -0
- package/lib/data-access/adapters/endpoint-masterdata.adapter.d.ts +11 -0
- package/lib/data-access/adapters/endpoint-persistence.adapter.d.ts +15 -0
- package/lib/data-access/adapters/endpoint-schema-discovery.adapter.d.ts +21 -0
- package/lib/data-access/adapters/endpoint-tokens.d.ts +21 -0
- package/lib/data-access/adapters/http-lookup.adapter.d.ts +2 -1
- package/lib/data-access/adapters/index.d.ts +7 -0
- package/lib/data-access/contracts/lookup.contract.d.ts +2 -2
- package/lib/features/designer/components/component-palette/component-palette.component.d.ts +1 -0
- package/lib/features/designer/components/design-canvas/design-canvas.component.d.ts +6 -2
- package/lib/features/designer/components/dialogs/report-saved-dialog.component.d.ts +34 -0
- package/lib/features/designer/components/property-editor/parameter-binding-editor.component.d.ts +6 -0
- package/lib/features/designer/components/property-editor/property-editor-action.component.d.ts +3 -0
- package/lib/features/designer/components/property-editor/property-editor-datasource.component.d.ts +12 -2
- package/lib/features/designer/components/property-editor/property-editor-grid.component.d.ts +41 -1
- package/lib/features/designer/components/property-editor/property-editor.component.d.ts +19 -0
- package/lib/features/designer/components/toolbar/designer-toolbar.component.d.ts +13 -1
- package/lib/features/designer/designer.component.d.ts +4 -0
- package/lib/features/designer/services/designer-state.service.d.ts +12 -1
- package/lib/features/embed/bways-report-builder.component.d.ts +48 -0
- package/lib/features/embed/bways-report-menu.component.d.ts +40 -0
- package/lib/features/embed/bways-report-page.component.d.ts +61 -0
- package/lib/features/embed/index.d.ts +4 -0
- package/lib/features/embed/inject-report-menu.d.ts +44 -0
- package/lib/features/index.d.ts +1 -0
- package/lib/features/persistence/services/report-store.service.d.ts +5 -0
- package/lib/features/routing/index.d.ts +1 -1
- package/lib/features/routing/models/report-route.models.d.ts +3 -0
- package/lib/features/routing/report-routes.d.ts +40 -0
- package/lib/features/routing/resolvers/report.resolver.d.ts +1 -1
- package/lib/features/runtime/dynamic-renderer.component.d.ts +1 -0
- package/lib/features/runtime/report-grid-bridge.component.d.ts +8 -59
- package/lib/primitives/components/button/rpt-button.component.d.ts +3 -0
- package/lib/primitives/components/dropdown/rpt-dropdown.component.d.ts +18 -2
- package/lib/primitives/components/index.d.ts +2 -0
- package/lib/primitives/components/searchable-select/rpt-searchable-select.component.d.ts +89 -0
- package/lib/provide-reporting.d.ts +28 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,572 +1,441 @@
|
|
|
1
1
|
# BWays-Report — Integration & Developer Guide
|
|
2
2
|
|
|
3
|
-
`bways-report` is an enterprise
|
|
3
|
+
`bways-report` is an enterprise reporting platform and visual drag-and-drop page builder for Angular 18+ business & ERP applications with NestJS / SQL Server.
|
|
4
4
|
|
|
5
|
-
It
|
|
5
|
+
It empowers developers and business users to visually design reporting pages with filters, lookup dropdowns, metric cards, charts, and data grids. Report configurations are saved as clean JSON metadata (`ReportDefinition`), and the runtime engine dynamically generates reactive forms, controls, queries, and live grid views on the fly.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
[](https://www.npmjs.com/package/bways-report)
|
|
9
|
-
[](LICENSE)
|
|
10
|
+
[](CHANGELOG.md)
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What's New in v0.2.0
|
|
14
|
+
|
|
15
|
+
- **1 Config Object + 3 Drop-in Components**: Eliminate boilerplate adapters with `BwaysReportConfig` and drop-in tags.
|
|
16
|
+
- **`<bways-report-builder>`**: Drop-in visual designer shell with width/height controls, starter blueprints, and save flow.
|
|
17
|
+
- **`<bways-report-page>`**: Live runtime form & dynamic grid page rendered automatically from a report slug.
|
|
18
|
+
- **`<bways-report-menu>` & `injectReportMenu()`**: Responsive navigation menu (sidebar, navbar, dropdown) with search and category folding.
|
|
19
|
+
- **`bwaysReportRoutes()`**: Declarative Angular routing for live report pages (`/reports/:slug`).
|
|
20
|
+
- **Post-Save Interactive Modal**: Immediate live form preview with page routing settings (slug, menu group, publish toggle).
|
|
21
|
+
- **CDK Overlay Viewport Flipping**: Zero clipping for searchable dropdowns inside cards and scroll containers.
|
|
22
|
+
- **Quick Action ("+") & Inline Renaming**: Double-click report title to rename in place; `+` button to start fresh pages.
|
|
23
|
+
|
|
10
24
|
---
|
|
11
25
|
|
|
12
26
|
## Table of Contents
|
|
13
27
|
|
|
14
|
-
1. [
|
|
15
|
-
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
1. [Quick Start (5 Steps)](#1-quick-start-5-steps)
|
|
29
|
+
- [Step 1: Install](#step-1-install)
|
|
30
|
+
- [Step 2: Configure Once (`app.config.ts`)](#step-2-configure-once-appconfigts)
|
|
31
|
+
- [Step 3: Drop-in Builder Component](#step-3-drop-in-builder-component)
|
|
32
|
+
- [Step 4: Host Navigation Menu](#step-4-host-navigation-menu)
|
|
33
|
+
- [Step 5: Declarative Routes (`app.routes.ts`)](#step-5-declarative-routes-approutests)
|
|
34
|
+
2. [Visual Designer Features](#2-visual-designer-features)
|
|
35
|
+
- [Single-Mode Technical Property Editor](#single-mode-technical-property-editor)
|
|
36
|
+
- [CDK Overlay Viewport Flip](#cdk-overlay-viewport-flip)
|
|
37
|
+
- [Post-Save Live Preview & Settings Dialog](#post-save-live-preview--settings-dialog)
|
|
38
|
+
- [Inline Double-Click Renaming](#inline-double-click-renaming)
|
|
39
|
+
- [Report Switcher & Quick New Page ("+")](#report-switcher--quick-new-page-)
|
|
40
|
+
3. [Backend Contract Reference](#3-backend-contract-reference)
|
|
41
|
+
4. [Dynamic Session Context & Auto-Parameter Binding](#4-dynamic-session-context--auto-parameter-binding)
|
|
42
|
+
5. [Plugging in BWays-Grid (`UltraGridComponent`)](#5-plugging-in-bways-grid-ultragridcomponent)
|
|
43
|
+
6. [Advanced Custom Adapters](#6-advanced-custom-adapters)
|
|
44
|
+
7. [Theming & CSS Custom Properties](#7-theming--css-custom-properties)
|
|
45
|
+
8. [Exporting Data (CSV & JSON)](#8-exporting-data-csv--json)
|
|
28
46
|
|
|
29
47
|
---
|
|
30
48
|
|
|
31
|
-
## 1.
|
|
49
|
+
## 1. Quick Start (5 Steps)
|
|
32
50
|
|
|
33
|
-
|
|
51
|
+
### Step 1: Install
|
|
34
52
|
|
|
35
53
|
```bash
|
|
36
|
-
|
|
54
|
+
npm install bways-report bways-grid @angular/cdk
|
|
37
55
|
```
|
|
38
56
|
|
|
39
|
-
|
|
57
|
+
> **Compatibility**: Angular 18+ or 19+ standalone architecture with Zone.js or Zoneless change detection.
|
|
40
58
|
|
|
41
59
|
---
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
### A. 1-Line App Setup
|
|
61
|
+
### Step 2: Configure Once (`app.config.ts`)
|
|
46
62
|
|
|
47
|
-
|
|
63
|
+
Configure `provideBwaysReport()` with a single `BwaysReportConfig` object:
|
|
48
64
|
|
|
49
65
|
```typescript
|
|
50
66
|
// src/app/app.config.ts
|
|
51
|
-
import { ApplicationConfig } from '@angular/core';
|
|
52
|
-
import {
|
|
67
|
+
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
|
68
|
+
import { provideHttpClient } from '@angular/common/http';
|
|
69
|
+
import { provideRouter } from '@angular/router';
|
|
70
|
+
import { provideBwaysReport, BwaysReportConfig } from 'bways-report';
|
|
71
|
+
import { UltraGridComponent } from 'bways-grid';
|
|
72
|
+
import { routes } from './app.routes';
|
|
73
|
+
import { AuthService } from './services/auth.service';
|
|
74
|
+
|
|
75
|
+
const reportConfig: BwaysReportConfig = {
|
|
76
|
+
// Backend Endpoints (only these 3-4 URLs needed)
|
|
77
|
+
spEndpoint: 'http://localhost:3000/api/reporting/procedures', // GET -> SPs + parameters
|
|
78
|
+
tablesEndpoint: 'http://localhost:3000/api/reporting/tables', // GET -> Tables + columns
|
|
79
|
+
executeEndpoint: 'http://localhost:3000/api/reporting/execute', // POST -> Query / SP execution
|
|
80
|
+
lookupEndpoint: 'http://localhost:3000/api/reporting/lookup', // POST -> Dropdown master data / lookup (optional)
|
|
81
|
+
reportsEndpoint: 'http://localhost:3000/api/reporting/reports', // Optional CRUD (defaults to localStorage)
|
|
82
|
+
|
|
83
|
+
// Visual Designer Configuration
|
|
84
|
+
components: ['formInputs', 'actionButtons', 'displayLabels'], // Palette categories to show (default: all)
|
|
85
|
+
autoParameter: true, // Auto-bind @BranchId, @CompanyId from session
|
|
86
|
+
session: () => inject(AuthService).currentSession(), // Session source for autoParameter
|
|
87
|
+
|
|
88
|
+
// Live Pages & Data Grid
|
|
89
|
+
routeBase: 'reports', // Live pages mount at /reports/:slug
|
|
90
|
+
grid: UltraGridComponent, // UltraGrid from bways-grid (or built-in table)
|
|
91
|
+
defaultPageSize: 25,
|
|
92
|
+
};
|
|
53
93
|
|
|
54
94
|
export const appConfig: ApplicationConfig = {
|
|
55
95
|
providers: [
|
|
56
|
-
|
|
57
|
-
|
|
96
|
+
provideZoneChangeDetection({ eventCoalescing: true }),
|
|
97
|
+
provideHttpClient(),
|
|
98
|
+
provideRouter(routes),
|
|
99
|
+
provideBwaysReport(reportConfig),
|
|
58
100
|
],
|
|
59
101
|
};
|
|
60
102
|
```
|
|
61
103
|
|
|
62
|
-
> **Zero-Backend / Local Storage Mode:**
|
|
63
|
-
> For local prototyping, offline demos, or frontend-only dev, use:
|
|
64
|
-
> ```typescript
|
|
65
|
-
> provideReporting(withLocalStorage()); // seeds built-in blueprints and saves to browser localStorage
|
|
66
|
-
> ```
|
|
67
|
-
|
|
68
104
|
---
|
|
69
105
|
|
|
70
|
-
###
|
|
106
|
+
### Step 3: Drop-in Builder Component
|
|
107
|
+
|
|
108
|
+
Embed the full visual drag-and-drop report designer with one HTML tag:
|
|
71
109
|
|
|
72
|
-
|
|
110
|
+
```html
|
|
111
|
+
<!-- src/app/pages/admin-reports/admin-reports.component.html -->
|
|
112
|
+
<bways-report-builder
|
|
113
|
+
width="100%"
|
|
114
|
+
height="calc(100vh - 64px)"
|
|
115
|
+
(saved)="onReportSaved($event)"
|
|
116
|
+
/>
|
|
117
|
+
```
|
|
73
118
|
|
|
74
119
|
```typescript
|
|
120
|
+
// src/app/pages/admin-reports/admin-reports.component.ts
|
|
75
121
|
import { Component } from '@angular/core';
|
|
76
|
-
import {
|
|
122
|
+
import { BwaysReportBuilderComponent, ReportDefinition } from 'bways-report';
|
|
77
123
|
|
|
78
124
|
@Component({
|
|
79
125
|
standalone: true,
|
|
80
|
-
imports: [
|
|
81
|
-
|
|
82
|
-
<!-- 1 tag to run and render the full report page -->
|
|
83
|
-
<rpt-report [id]="'sales-ledger'" />
|
|
84
|
-
`,
|
|
126
|
+
imports: [BwaysReportBuilderComponent],
|
|
127
|
+
templateUrl: './admin-reports.component.html',
|
|
85
128
|
})
|
|
86
|
-
export class
|
|
129
|
+
export class AdminReportsComponent {
|
|
130
|
+
onReportSaved(report: ReportDefinition): void {
|
|
131
|
+
console.log('Report saved and published:', report.name, report.slug);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
87
134
|
```
|
|
88
135
|
|
|
89
136
|
---
|
|
90
137
|
|
|
91
|
-
###
|
|
138
|
+
### Step 4: Host Navigation Menu
|
|
92
139
|
|
|
93
|
-
|
|
140
|
+
Display published reports in your host ERP navigation bar or sidebar:
|
|
94
141
|
|
|
142
|
+
#### Option A: Drop-in Component
|
|
143
|
+
```html
|
|
144
|
+
<!-- Sidebar variant with search filter and category folding -->
|
|
145
|
+
<bways-report-menu variant="sidebar" />
|
|
146
|
+
|
|
147
|
+
<!-- Or dropdown variant for top navigation bars -->
|
|
148
|
+
<bways-report-menu variant="dropdown" />
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Option B: Reactive Signal (`injectReportMenu`)
|
|
95
152
|
```typescript
|
|
96
153
|
import { Component } from '@angular/core';
|
|
97
|
-
import {
|
|
154
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
155
|
+
import { injectReportMenu } from 'bways-report';
|
|
98
156
|
|
|
99
157
|
@Component({
|
|
100
158
|
standalone: true,
|
|
101
|
-
imports: [
|
|
159
|
+
imports: [RouterLink, RouterLinkActive],
|
|
102
160
|
template: `
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
161
|
+
<nav class="erp-menu">
|
|
162
|
+
@for (group of menu(); track group.groupName) {
|
|
163
|
+
<div class="menu-group">
|
|
164
|
+
<h3>{{ group.groupName }}</h3>
|
|
165
|
+
@for (item of group.items; track item.id) {
|
|
166
|
+
<a [routerLink]="item.route" routerLinkActive="active">
|
|
167
|
+
{{ item.name }}
|
|
168
|
+
</a>
|
|
169
|
+
}
|
|
170
|
+
</div>
|
|
171
|
+
}
|
|
172
|
+
</nav>
|
|
108
173
|
`,
|
|
109
174
|
})
|
|
110
|
-
export class
|
|
111
|
-
|
|
112
|
-
console.log('Report saved successfully:', definition);
|
|
113
|
-
}
|
|
175
|
+
export class NavigationSidebarComponent {
|
|
176
|
+
readonly menu = injectReportMenu();
|
|
114
177
|
}
|
|
115
178
|
```
|
|
116
179
|
|
|
117
180
|
---
|
|
118
181
|
|
|
119
|
-
###
|
|
182
|
+
### Step 5: Declarative Routes (`app.routes.ts`)
|
|
120
183
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
```json
|
|
124
|
-
{
|
|
125
|
-
"grid": {
|
|
126
|
-
"dataSourceId": "ds-sales",
|
|
127
|
-
"columns": [...],
|
|
128
|
-
"options": {
|
|
129
|
-
"presets": [
|
|
130
|
-
{
|
|
131
|
-
"id": "preset_1728200000",
|
|
132
|
-
"name": "Q3 High Value Clients",
|
|
133
|
-
"gridState": {
|
|
134
|
-
"filters": { "amount": { "operator": "gte", "value": 50000 } },
|
|
135
|
-
"columnOrder": ["invoiceId", "amount", "customerName"]
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
]
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
Users can switch between views seamlessly from the top dropdown, keeping all formatting and custom filters intact.
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
### Backend Integration: Mounting `BwaysReportingModule` in NestJS
|
|
148
|
-
|
|
149
|
-
To power the built-in HTTP adapters, mount `BwaysReportingModule` directly inside your existing NestJS host application:
|
|
184
|
+
Add one line to your route definitions to enable dynamic live report pages:
|
|
150
185
|
|
|
151
186
|
```typescript
|
|
152
|
-
// app.
|
|
153
|
-
import {
|
|
154
|
-
import {
|
|
155
|
-
|
|
156
|
-
@Module({
|
|
157
|
-
imports: [
|
|
158
|
-
BwaysReportingModule.forRoot({
|
|
159
|
-
security: {
|
|
160
|
-
allowedSchemas: ['dbo', 'reporting'],
|
|
161
|
-
disallowRawSql: true, // SQL injection prevention
|
|
162
|
-
validateEntitiesAgainstCatalog: true, // Checks sys.procedures catalog
|
|
163
|
-
},
|
|
164
|
-
}),
|
|
165
|
-
],
|
|
166
|
-
})
|
|
167
|
-
export class AppModule {}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
### Option B: Custom Adapters (Full Manual Control)
|
|
187
|
+
// src/app/app.routes.ts
|
|
188
|
+
import { Routes } from '@angular/router';
|
|
189
|
+
import { bwaysReportRoutes } from 'bways-report';
|
|
190
|
+
import { AdminReportsComponent } from './pages/admin-reports/admin-reports.component';
|
|
173
191
|
|
|
174
|
-
|
|
192
|
+
export const routes: Routes = [
|
|
193
|
+
// Admin visual designer route
|
|
194
|
+
{ path: 'admin/reports', component: AdminReportsComponent },
|
|
175
195
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
provideBwaysReport({
|
|
182
|
-
gridComponent: BWaysGridComponent,
|
|
183
|
-
dataSourceAdapter: MyCustomDataSourceAdapter,
|
|
184
|
-
persistenceAdapter: MyCustomPersistenceAdapter,
|
|
185
|
-
masterDataProvider: MyCustomMasterDataProvider,
|
|
186
|
-
defaultPageSize: 25,
|
|
187
|
-
enableDesigner: true,
|
|
196
|
+
// Live user reporting pages: /reports/:slug (e.g. /reports/sale-report)
|
|
197
|
+
{
|
|
198
|
+
path: 'reports',
|
|
199
|
+
children: bwaysReportRoutes({
|
|
200
|
+
// includeBuilderRoute: false, // Set true to include /reports/new and /reports/:slug/edit
|
|
188
201
|
}),
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
},
|
|
203
|
+
];
|
|
191
204
|
```
|
|
192
205
|
|
|
206
|
+
When users click "Sale Report" in the menu, `/reports/sale-report` opens the **live interactive form** with reactive inputs and data grid without needing custom page components!
|
|
207
|
+
|
|
193
208
|
---
|
|
194
209
|
|
|
195
|
-
##
|
|
210
|
+
## 2. Visual Designer Features
|
|
196
211
|
|
|
197
|
-
|
|
212
|
+
### Single-Mode Technical Property Editor
|
|
213
|
+
Eliminates confusing simplified/advanced toggles. Standardized on technical database terminology:
|
|
214
|
+
- `Database Table / View`
|
|
215
|
+
- `Stored Proc`
|
|
216
|
+
- `Display Field (Visible Text)`
|
|
217
|
+
- `Value Field (Stored Key)`
|
|
218
|
+
- `Dependent On (Cascading Parent Field)`
|
|
198
219
|
|
|
199
|
-
###
|
|
220
|
+
### CDK Overlay Viewport Flip
|
|
221
|
+
Searchable selects (`<rpt-searchable-select>`) and comboboxes (`<rpt-dropdown>`) render inside Angular CDK Overlays attached to the document body. They automatically evaluate available viewport space and flip between `below-start`, `above-start`, `below-end`, and `above-end` to eliminate clipping inside cards and scrollable drawers.
|
|
200
222
|
|
|
201
|
-
|
|
223
|
+
### Post-Save Live Preview & Settings Dialog
|
|
224
|
+
Saving a report in the builder automatically opens a two-column modal:
|
|
225
|
+
- **Left Column**: Page settings (Display Name, URL Slug `/reports/:slug`, Menu Group, Publish to Menu toggle, and a Copy Route button).
|
|
226
|
+
- **Right Column**: Live interactive form preview rendered via `<rpt-runtime>` allowing instant testing of validation and execution before publishing.
|
|
202
227
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
@Injectable({ providedIn: 'root' })
|
|
211
|
-
export class ErpDataSourceAdapter extends ReportDataSourceAdapter {
|
|
212
|
-
private readonly http = inject(HttpClient);
|
|
213
|
-
|
|
214
|
-
execute(
|
|
215
|
-
dataSource: ReportDataSourceDefinition,
|
|
216
|
-
parameters: Record<string, unknown>
|
|
217
|
-
): Observable<unknown[]> {
|
|
218
|
-
// Send report filter parameters to your backend API endpoint
|
|
219
|
-
return this.http.post<unknown[]>(dataSource.endpoint, parameters);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
### B. Persistence Adapter
|
|
228
|
+
### Inline Double-Click Renaming
|
|
229
|
+
Double-click the report title in the toolbar to edit it inline:
|
|
230
|
+
- `Enter` saves the new name and suggests a URL slug.
|
|
231
|
+
- `Escape` cancels edits.
|
|
232
|
+
- `blur` persists the change.
|
|
233
|
+
- Changes are pushed to the undo/redo history stack.
|
|
225
234
|
|
|
226
|
-
|
|
235
|
+
### Report Switcher & Quick New Page ("+")
|
|
236
|
+
- **"+" Button**: Immediately instantiates a fresh blank canvas (with confirmation if unsaved changes exist).
|
|
237
|
+
- **Report Switcher**: Dropdown populated from saved reports allowing rapid switching between report definitions.
|
|
227
238
|
|
|
228
|
-
|
|
229
|
-
// src/app/services/erp-persistence.adapter.ts
|
|
230
|
-
import { Injectable, inject } from '@angular/core';
|
|
231
|
-
import { HttpClient } from '@angular/common/http';
|
|
232
|
-
import { Observable } from 'rxjs';
|
|
233
|
-
import {
|
|
234
|
-
ReportPersistenceAdapter,
|
|
235
|
-
ReportDefinition,
|
|
236
|
-
ReportSummary,
|
|
237
|
-
} from 'bways-report';
|
|
238
|
-
|
|
239
|
-
@Injectable({ providedIn: 'root' })
|
|
240
|
-
export class ErpPersistenceAdapter extends ReportPersistenceAdapter {
|
|
241
|
-
private readonly http = inject(HttpClient);
|
|
242
|
-
private readonly baseUrl = '/api/reports';
|
|
243
|
-
|
|
244
|
-
load(reportId: string): Observable<ReportDefinition> {
|
|
245
|
-
return this.http.get<ReportDefinition>(`${this.baseUrl}/${reportId}`);
|
|
246
|
-
}
|
|
239
|
+
---
|
|
247
240
|
|
|
248
|
-
|
|
249
|
-
return this.http.post<ReportDefinition>(this.baseUrl, report);
|
|
250
|
-
}
|
|
241
|
+
## 3. Backend Contract Reference
|
|
251
242
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
243
|
+
| Endpoint | Method | Path | Request Format | Response Format |
|
|
244
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
245
|
+
| `spEndpoint` | `GET` | `/api/reporting/procedures` | None | `StoredProcedureSummary[]` |
|
|
246
|
+
| `tablesEndpoint` | `GET` | `/api/reporting/tables` | None | `TableSummary[]` |
|
|
247
|
+
| `executeEndpoint` | `POST` | `/api/reporting/execute` | `{ kind, name, schema, parameters, columns?, top? }` | `Record<string, unknown>[]` |
|
|
248
|
+
| `reportsEndpoint` *(opt)* | `GET/POST/PUT/DELETE` | `/api/reporting/reports` | JSON `ReportDefinition` | `ReportSummary[]` / `ReportDefinition` |
|
|
255
249
|
|
|
256
|
-
|
|
257
|
-
|
|
250
|
+
### Stored Procedure Discovery Format
|
|
251
|
+
```json
|
|
252
|
+
[
|
|
253
|
+
{
|
|
254
|
+
"name": "usp_GetCustomerList",
|
|
255
|
+
"schema": "dbo",
|
|
256
|
+
"parameters": [
|
|
257
|
+
{ "name": "@BranchId", "sqlType": "int", "required": true },
|
|
258
|
+
{ "name": "@SearchTerm", "sqlType": "varchar", "required": false, "defaultValue": "" }
|
|
259
|
+
]
|
|
258
260
|
}
|
|
261
|
+
]
|
|
262
|
+
```
|
|
259
263
|
|
|
260
|
-
|
|
261
|
-
|
|
264
|
+
### Table Discovery Format
|
|
265
|
+
```json
|
|
266
|
+
[
|
|
267
|
+
{
|
|
268
|
+
"name": "Customers",
|
|
269
|
+
"schema": "dbo",
|
|
270
|
+
"type": "table",
|
|
271
|
+
"columns": [
|
|
272
|
+
{ "name": "CustomerId", "sqlType": "int" },
|
|
273
|
+
{ "name": "CustomerName", "sqlType": "nvarchar" }
|
|
274
|
+
]
|
|
262
275
|
}
|
|
263
|
-
|
|
276
|
+
]
|
|
264
277
|
```
|
|
265
278
|
|
|
266
|
-
###
|
|
279
|
+
### Execution Request Format
|
|
280
|
+
Supports both simplified and enterprise DTO contracts out-of-the-box:
|
|
267
281
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
@Injectable({ providedIn: 'root' })
|
|
278
|
-
export class ErpMasterDataProvider extends MasterDataProvider {
|
|
279
|
-
private readonly http = inject(HttpClient);
|
|
280
|
-
|
|
281
|
-
getOptions(
|
|
282
|
-
masterName: string,
|
|
283
|
-
filter?: Record<string, unknown>
|
|
284
|
-
): Observable<DropdownOption[]> {
|
|
285
|
-
return this.http.get<DropdownOption[]>(`/api/master-data/${masterName}`, {
|
|
286
|
-
params: filter as any,
|
|
287
|
-
});
|
|
282
|
+
**Option A (Simplified contract):**
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"kind": "procedure",
|
|
286
|
+
"name": "usp_GetCustomerList",
|
|
287
|
+
"schema": "dbo",
|
|
288
|
+
"parameters": {
|
|
289
|
+
"BranchId": 1,
|
|
290
|
+
"SearchTerm": "Acme"
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
293
|
```
|
|
291
294
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
// src/app/app.routes.ts
|
|
302
|
-
import { Routes } from '@angular/router';
|
|
303
|
-
import { createReportRoutes } from 'bways-report';
|
|
304
|
-
|
|
305
|
-
export const routes: Routes = [
|
|
306
|
-
{
|
|
307
|
-
path: 'reports',
|
|
308
|
-
children: createReportRoutes({
|
|
309
|
-
// Optional: enforce auth or role guards
|
|
310
|
-
runtimeGuards: [],
|
|
311
|
-
designerGuards: [], // e.g. [AdminGuard]
|
|
312
|
-
enableDesignerRoutes: true, // creates 'new' and ':reportId/edit' routes
|
|
313
|
-
}),
|
|
295
|
+
**Option B (Enterprise DTO contract):**
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"dataSource": {
|
|
299
|
+
"id": "usp_GetCustomerList",
|
|
300
|
+
"type": "stored_procedure",
|
|
301
|
+
"name": "usp_GetCustomerList",
|
|
302
|
+
"schema": "dbo"
|
|
314
303
|
},
|
|
315
|
-
|
|
304
|
+
"parameters": {
|
|
305
|
+
"BranchId": 1
|
|
306
|
+
},
|
|
307
|
+
"sessionContext": {
|
|
308
|
+
"companyId": 101,
|
|
309
|
+
"branchId": 1
|
|
310
|
+
}
|
|
311
|
+
}
|
|
316
312
|
```
|
|
317
313
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
// src/app/components/sidebar/erp-sidebar.component.ts
|
|
329
|
-
import { Component, inject } from '@angular/core';
|
|
330
|
-
import { CommonModule } from '@angular/common';
|
|
331
|
-
import { RouterModule } from '@angular/router';
|
|
332
|
-
import { ReportMenuService } from 'bways-report';
|
|
333
|
-
|
|
334
|
-
@Component({
|
|
335
|
-
selector: 'erp-sidebar',
|
|
336
|
-
standalone: true,
|
|
337
|
-
imports: [CommonModule, RouterModule],
|
|
338
|
-
template: `
|
|
339
|
-
<aside class="sidebar">
|
|
340
|
-
<input
|
|
341
|
-
type="search"
|
|
342
|
-
placeholder="Search reports..."
|
|
343
|
-
(input)="onSearch($event)"
|
|
344
|
-
/>
|
|
345
|
-
|
|
346
|
-
@for (group of menuService.menuGroups(); track group.category) {
|
|
347
|
-
<div class="menu-group">
|
|
348
|
-
<h4>{{ group.displayName }}</h4>
|
|
349
|
-
<ul>
|
|
350
|
-
@for (item of group.items; track item.id) {
|
|
351
|
-
<li>
|
|
352
|
-
<a [routerLink]="item.route" routerLinkActive="active">
|
|
353
|
-
{{ item.name }}
|
|
354
|
-
@if (item.isDraft) {
|
|
355
|
-
<span class="badge">Draft</span>
|
|
356
|
-
}
|
|
357
|
-
</a>
|
|
358
|
-
</li>
|
|
359
|
-
}
|
|
360
|
-
</ul>
|
|
361
|
-
</div>
|
|
362
|
-
}
|
|
363
|
-
</aside>
|
|
364
|
-
`,
|
|
365
|
-
})
|
|
366
|
-
export class ErpSidebarComponent {
|
|
367
|
-
readonly menuService = inject(ReportMenuService);
|
|
368
|
-
|
|
369
|
-
onSearch(event: Event): void {
|
|
370
|
-
const query = (event.target as HTMLInputElement).value;
|
|
371
|
-
this.menuService.setSearchQuery(query);
|
|
372
|
-
}
|
|
314
|
+
### Dropdown Lookup Request Format (`lookupEndpoint`)
|
|
315
|
+
Dropdowns with database data sources query `/api/reporting/lookup` (or fallback to `executeEndpoint`):
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"entityName": "Sel_UserWiseCompUnit",
|
|
319
|
+
"sourceType": "stored_procedure",
|
|
320
|
+
"schema": "dbo",
|
|
321
|
+
"valueField": "COMPUNIT_ID",
|
|
322
|
+
"displayField": "COMPUNIT_NAME",
|
|
323
|
+
"parameters": {}
|
|
373
324
|
}
|
|
374
325
|
```
|
|
375
326
|
|
|
376
327
|
---
|
|
377
328
|
|
|
378
|
-
##
|
|
379
|
-
|
|
380
|
-
If you prefer embedding components inside existing pages instead of routing:
|
|
381
|
-
|
|
382
|
-
### Report Runtime (Interactive Report Page)
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
import { Component } from '@angular/core';
|
|
386
|
-
import { ReportRuntimeComponent, ReportDefinition } from 'bways-report';
|
|
387
|
-
|
|
388
|
-
@Component({
|
|
389
|
-
selector: 'my-report-page',
|
|
390
|
-
standalone: true,
|
|
391
|
-
imports: [ReportRuntimeComponent],
|
|
392
|
-
template: `
|
|
393
|
-
<!-- Option A: Load by ID via persistence adapter -->
|
|
394
|
-
<rpt-runtime [reportId]="'sales-register-2024'" />
|
|
329
|
+
## 4. Dynamic Session Context & Auto-Parameter Binding
|
|
395
330
|
|
|
396
|
-
|
|
397
|
-
<!-- <rpt-runtime [report]="customDefinition" /> -->
|
|
398
|
-
`,
|
|
399
|
-
})
|
|
400
|
-
export class MyReportPageComponent {}
|
|
401
|
-
```
|
|
331
|
+
When `autoParameter: true` is configured, parameters matching active ERP session keys are automatically populated and hidden from non-technical users:
|
|
402
332
|
|
|
403
|
-
|
|
333
|
+
| Stored Procedure Parameter | Matching Session Keys |
|
|
334
|
+
| :--- | :--- |
|
|
335
|
+
| `@BranchId`, `@branch_id`, `@bid`, `@branchcode`, `@locationid` | `branchId`, `branch`, `locationId` |
|
|
336
|
+
| `@CompanyId`, `@company_id`, `@cid`, `@companycode`, `@tenantid` | `companyId`, `company`, `tenantId` |
|
|
337
|
+
| `@UserId`, `@user_id`, `@uid`, `@usercode`, `@operatorid` | `userId`, `user`, `loginUser` |
|
|
338
|
+
| `@FinancialYearId`, `@fyear`, `@fiscalyear`, `@yearid` | `financialYearId`, `fYear` |
|
|
404
339
|
|
|
340
|
+
Session values can be updated at runtime:
|
|
405
341
|
```typescript
|
|
406
|
-
import {
|
|
407
|
-
import {
|
|
342
|
+
import { inject } from '@angular/core';
|
|
343
|
+
import { ReportSessionContextService } from 'bways-report';
|
|
408
344
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
standalone: true,
|
|
412
|
-
imports: [DesignerComponent],
|
|
413
|
-
template: `
|
|
414
|
-
<rpt-designer
|
|
415
|
-
[initialReport]="reportDefinition"
|
|
416
|
-
(save)="onSaveReport($event)"
|
|
417
|
-
/>
|
|
418
|
-
`,
|
|
419
|
-
})
|
|
420
|
-
export class MyDesignerPageComponent {
|
|
421
|
-
reportDefinition: ReportDefinition = /* ... */;
|
|
345
|
+
export class SessionWidget {
|
|
346
|
+
private readonly session = inject(ReportSessionContextService);
|
|
422
347
|
|
|
423
|
-
|
|
424
|
-
|
|
348
|
+
onBranchSwitch(branchId: number): void {
|
|
349
|
+
this.session.setSessionParameter('branchId', branchId);
|
|
425
350
|
}
|
|
426
351
|
}
|
|
427
352
|
```
|
|
428
353
|
|
|
429
354
|
---
|
|
430
355
|
|
|
431
|
-
##
|
|
356
|
+
## 5. Plugging in BWays-Grid (`UltraGridComponent`)
|
|
432
357
|
|
|
433
|
-
|
|
358
|
+
`BWays-Report` provides first-class support for `bways-grid` (`v0.1.18+` / `v0.1.19+`). Pass `grid: UltraGridComponent` to `provideBwaysReport()`:
|
|
434
359
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
})
|
|
441
|
-
```
|
|
360
|
+
- **Runtime `@Input()` Safety**: Dynamic grid bridging inspects component metadata (`ɵcmp.inputs`) before calling `ComponentRef.setInput()`, preventing Angular `NG0303` runtime template binding errors.
|
|
361
|
+
- **Row Selection Alignment**: Row selection mode (`single` vs `multiple`) is passed cleanly through `config.rowSelection` and `config.selectionMode`, preserving full compatibility with `UltraGridComponent`.
|
|
362
|
+
- **Circular Echo Suppression**: Grid state changes originating from user interaction are tracked to avoid re-triggering parent input cycles. Column menus, search inputs, and drag-handles stay open and focused without flicker.
|
|
363
|
+
- **Filter State Preservation**: Preserves both `activeFilters` (checkboxes) and `conditionFilters` (expressions) across runtime updates and JSON serialization.
|
|
364
|
+
- **Selective Layout Auto-Persistence**: Structural layout changes (column widths, pinning, grouping) persist immediately, while interactive data filtering is debounced to avoid undo stack pollution.
|
|
442
365
|
|
|
443
|
-
|
|
366
|
+
---
|
|
444
367
|
|
|
445
|
-
|
|
368
|
+
## 6. Advanced Custom Adapters
|
|
446
369
|
|
|
447
|
-
|
|
370
|
+
For applications with custom HTTP pipelines, gRPC, or microservice gateways, extend the library's abstract contracts:
|
|
448
371
|
|
|
449
372
|
```typescript
|
|
450
373
|
provideBwaysReport({
|
|
451
|
-
|
|
452
|
-
|
|
374
|
+
gridComponent: UltraGridComponent,
|
|
375
|
+
dataSourceAdapter: MyCustomDataSourceAdapter,
|
|
376
|
+
persistenceAdapter: MyCustomPersistenceAdapter,
|
|
377
|
+
schemaDiscoveryAdapter: MyCustomSchemaDiscoveryAdapter,
|
|
378
|
+
lookupAdapter: MyCustomLookupAdapter,
|
|
379
|
+
});
|
|
453
380
|
```
|
|
454
381
|
|
|
455
|
-
|
|
382
|
+
Abstract base classes:
|
|
383
|
+
- `ReportDataSourceAdapter`
|
|
384
|
+
- `ReportPersistenceAdapter`
|
|
385
|
+
- `ReportSchemaDiscoveryAdapter`
|
|
386
|
+
- `ReportLookupAdapter`
|
|
456
387
|
|
|
457
388
|
---
|
|
458
389
|
|
|
459
390
|
## 7. Theming & CSS Custom Properties
|
|
460
391
|
|
|
461
|
-
`bways-report` uses CSS custom properties
|
|
392
|
+
`bways-report` uses Vanilla CSS custom properties. Customize the tokens in your application's `styles.css`:
|
|
462
393
|
|
|
463
394
|
```css
|
|
464
|
-
/* In your host application's styles.scss or theme file */
|
|
465
395
|
:root {
|
|
466
|
-
|
|
467
|
-
--rpt-primary: #
|
|
468
|
-
--rpt-
|
|
469
|
-
--rpt-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
--rpt-
|
|
473
|
-
--rpt-bg-subtle: #f8fafc;
|
|
474
|
-
--rpt-text-color: #0f172a;
|
|
475
|
-
--rpt-text-muted: #64748b;
|
|
476
|
-
--rpt-border-color: #e2e8f0;
|
|
477
|
-
|
|
478
|
-
/* Geometry & Typography */
|
|
479
|
-
--rpt-radius-md: 6px;
|
|
480
|
-
--rpt-radius-lg: 12px;
|
|
396
|
+
--rpt-color-primary: #2563eb;
|
|
397
|
+
--rpt-color-primary-hover: #1d4ed8;
|
|
398
|
+
--rpt-color-bg: #ffffff;
|
|
399
|
+
--rpt-color-surface: #f8fafc;
|
|
400
|
+
--rpt-color-text: #0f172a;
|
|
401
|
+
--rpt-color-text-muted: #64748b;
|
|
402
|
+
--rpt-color-border: #cbd5e1;
|
|
481
403
|
--rpt-font-family: 'Inter', system-ui, sans-serif;
|
|
482
|
-
--rpt-
|
|
483
|
-
|
|
484
|
-
/* Status Colors */
|
|
485
|
-
--rpt-success-color: #10b981;
|
|
486
|
-
--rpt-warning-color: #f59e0b;
|
|
487
|
-
--rpt-error-color: #ef4444;
|
|
404
|
+
--rpt-border-radius: 6px;
|
|
405
|
+
--rpt-border-radius-lg: 8px;
|
|
488
406
|
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
---
|
|
492
|
-
|
|
493
|
-
## 8. Registering Custom Host Controls
|
|
494
|
-
|
|
495
|
-
You can register custom Angular components (e.g., custom slider, multi-select, rich customer picker) into the `ComponentRegistry` so they appear in the Visual Designer palette and render dynamically at runtime:
|
|
496
407
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
type: 'customer-picker',
|
|
504
|
-
displayName: 'Customer Picker',
|
|
505
|
-
category: 'input',
|
|
506
|
-
icon: 'person_search',
|
|
507
|
-
component: MyCustomPickerComponent,
|
|
508
|
-
createDefaultConfig: () => ({
|
|
509
|
-
id: 'cust-picker',
|
|
510
|
-
type: 'customer-picker',
|
|
511
|
-
label: 'Select Customer',
|
|
512
|
-
properties: { placeholder: 'Search customer...' },
|
|
513
|
-
layout: { columnSpan: 4 },
|
|
514
|
-
}),
|
|
515
|
-
propertySchema: [
|
|
516
|
-
{ key: 'label', label: 'Label', type: 'string', required: true, group: 'General' },
|
|
517
|
-
{ key: 'key', label: 'Field Name (Key)', type: 'string', required: true, group: 'General' },
|
|
518
|
-
{ key: 'required', label: 'Required', type: 'boolean', group: 'Validation' },
|
|
519
|
-
],
|
|
520
|
-
});
|
|
408
|
+
[data-theme='dark'] {
|
|
409
|
+
--rpt-color-bg: #0f172a;
|
|
410
|
+
--rpt-color-surface: #1e293b;
|
|
411
|
+
--rpt-color-text: #f8fafc;
|
|
412
|
+
--rpt-color-text-muted: #94a3b8;
|
|
413
|
+
--rpt-color-border: #334155;
|
|
521
414
|
}
|
|
522
415
|
```
|
|
523
416
|
|
|
524
417
|
---
|
|
525
418
|
|
|
526
|
-
##
|
|
419
|
+
## 8. Exporting Data (CSV & JSON)
|
|
527
420
|
|
|
528
|
-
|
|
421
|
+
Inject `ReportExportService` in any host component:
|
|
529
422
|
|
|
530
423
|
```typescript
|
|
531
424
|
import { Component, inject } from '@angular/core';
|
|
532
425
|
import { ReportExportService } from 'bways-report';
|
|
533
426
|
|
|
534
|
-
@Component({
|
|
535
|
-
|
|
536
|
-
standalone: true,
|
|
537
|
-
template: `
|
|
538
|
-
<button (click)="downloadCsv()">Download CSV</button>
|
|
539
|
-
<button (click)="downloadJson()">Download Metadata</button>
|
|
540
|
-
`,
|
|
541
|
-
})
|
|
542
|
-
export class ExportActionsComponent {
|
|
427
|
+
@Component({ ... })
|
|
428
|
+
export class ExportComponent {
|
|
543
429
|
private readonly exportService = inject(ReportExportService);
|
|
544
430
|
|
|
545
|
-
|
|
546
|
-
const data = [
|
|
547
|
-
{ invoiceNo: 'INV-001', customer: 'Acme Corp', total: 4950 },
|
|
548
|
-
{ invoiceNo: 'INV-002', customer: 'Globex Inc', total: 14080 },
|
|
549
|
-
];
|
|
550
|
-
|
|
431
|
+
onExportCsv(data: unknown[]): void {
|
|
551
432
|
this.exportService.exportCsv(data, {
|
|
552
|
-
fileName: '
|
|
433
|
+
fileName: 'sales-register.csv',
|
|
553
434
|
columns: [
|
|
554
435
|
{ field: 'invoiceNo', header: 'Invoice #' },
|
|
555
|
-
{ field: '
|
|
556
|
-
{
|
|
557
|
-
field: 'total',
|
|
558
|
-
header: 'Total ($)',
|
|
559
|
-
formatter: (val: number) => `$${val.toLocaleString()}`
|
|
560
|
-
},
|
|
436
|
+
{ field: 'amount', header: 'Amount' },
|
|
561
437
|
],
|
|
562
|
-
addBom: true, //
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
downloadJson(): void {
|
|
567
|
-
this.exportService.exportJson(myReportDefinition, {
|
|
568
|
-
fileName: 'report-schema.json',
|
|
569
|
-
pretty: true,
|
|
438
|
+
addBom: true, // UTF-8 Excel compatibility
|
|
570
439
|
});
|
|
571
440
|
}
|
|
572
441
|
}
|
|
@@ -576,4 +445,4 @@ export class ExportActionsComponent {
|
|
|
576
445
|
|
|
577
446
|
## License
|
|
578
447
|
|
|
579
|
-
|
|
448
|
+
Proprietary License. Copyright (c) 2026 CCPL / BWays Team. All rights reserved.
|