bways-report 0.1.9 → 0.2.0
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 +463 -316
- package/esm2022/lib/core/config/bways-report-config.mjs +29 -32
- package/esm2022/lib/core/models/report-datasource.model.mjs +1 -1
- package/esm2022/lib/core/services/report-api-execution.service.mjs +33 -11
- package/esm2022/lib/core/services/report-session-context.service.mjs +72 -3
- package/esm2022/lib/core/tokens/designer-config.token.mjs +4 -0
- package/esm2022/lib/core/tokens/reporting-config.token.mjs +1 -1
- package/esm2022/lib/core/utils/report-endpoint.util.mjs +22 -0
- package/esm2022/lib/core/utils/report-request-scope.mjs +48 -0
- package/esm2022/lib/data-access/adapters/endpoint-datasource.adapter.mjs +8 -1
- package/esm2022/lib/data-access/adapters/endpoint-lookup.adapter.mjs +5 -2
- package/esm2022/lib/data-access/adapters/endpoint-masterdata.adapter.mjs +5 -2
- package/esm2022/lib/data-access/adapters/endpoint-persistence.adapter.mjs +30 -14
- package/esm2022/lib/data-access/adapters/endpoint-schema-discovery.adapter.mjs +32 -28
- package/esm2022/lib/data-access/adapters/endpoint-tokens.mjs +1 -1
- package/esm2022/lib/data-access/adapters/http-datasource.adapter.mjs +4 -37
- package/esm2022/lib/data-access/adapters/http-lookup.adapter.mjs +4 -50
- package/esm2022/lib/data-access/adapters/http-persistence.adapter.mjs +43 -194
- package/esm2022/lib/data-access/adapters/http-schema-discovery.adapter.mjs +17 -65
- package/esm2022/lib/data-access/adapters/index.mjs +1 -1
- package/esm2022/lib/data-access/adapters/local-storage-persistence.adapter.mjs +13 -4
- package/esm2022/lib/data-access/adapters/report-id-availability.mjs +20 -0
- package/esm2022/lib/data-access/adapters/session-headers.mjs +11 -0
- package/esm2022/lib/data-access/contracts/index.mjs +1 -1
- package/esm2022/lib/data-access/contracts/report-persistence.contract.mjs +13 -1
- package/esm2022/lib/features/designer/components/design-canvas/design-canvas.component.mjs +50 -40
- package/esm2022/lib/features/designer/components/dialogs/report-saved-dialog.component.mjs +546 -119
- package/esm2022/lib/features/designer/components/property-editor/property-editor-datasource.component.mjs +12 -6
- package/esm2022/lib/features/designer/components/property-editor/property-editor-grid.component.mjs +9 -8
- package/esm2022/lib/features/designer/components/property-editor/property-editor.component.mjs +9 -4
- package/esm2022/lib/features/designer/components/toolbar/designer-toolbar.component.mjs +54 -32
- package/esm2022/lib/features/designer/designer.component.mjs +100 -67
- package/esm2022/lib/features/designer/rpt-report-designer.component.mjs +10 -4
- package/esm2022/lib/features/designer/services/designer-config.service.mjs +56 -0
- package/esm2022/lib/features/designer/services/designer-endpoints.service.mjs +155 -0
- package/esm2022/lib/features/designer/services/designer-state.service.mjs +3 -2
- package/esm2022/lib/features/embed/bways-report-builder.component.mjs +3 -3
- package/esm2022/lib/features/export/services/report-export.service.mjs +11 -6
- package/esm2022/lib/features/persistence/services/report-store.service.mjs +78 -23
- package/esm2022/lib/features/persistence/services/schema-validator.service.mjs +59 -3
- package/esm2022/lib/features/routing/components/routed-report-designer.component.mjs +12 -17
- package/esm2022/lib/features/routing/components/routed-report-runtime.component.mjs +5 -3
- package/esm2022/lib/features/routing/report-routes.mjs +6 -6
- package/esm2022/lib/features/routing/resolvers/report-route-identifier.mjs +5 -0
- package/esm2022/lib/features/routing/resolvers/report.resolver.mjs +3 -2
- package/esm2022/lib/features/runtime/report-execution.service.mjs +18 -5
- package/esm2022/lib/features/runtime/report-grid-bridge.component.mjs +4 -2
- package/esm2022/lib/features/runtime/report-runtime.component.mjs +94 -71
- package/esm2022/lib/features/runtime/rpt-report.component.mjs +49 -41
- package/esm2022/lib/primitives/components/chart-bridge/report-chart-bridge.component.mjs +7 -2
- package/esm2022/lib/primitives/components/icon/rpt-icon.component.mjs +13 -1
- package/esm2022/lib/provide-reporting.mjs +9 -29
- package/fesm2022/bways-report.mjs +1789 -1041
- package/fesm2022/bways-report.mjs.map +1 -1
- package/lib/core/config/bways-report-config.d.ts +14 -5
- package/lib/core/models/report-datasource.model.d.ts +5 -0
- package/lib/core/services/report-session-context.service.d.ts +9 -0
- package/lib/core/tokens/designer-config.token.d.ts +4 -0
- package/lib/core/tokens/reporting-config.token.d.ts +4 -0
- package/lib/core/utils/report-endpoint.util.d.ts +12 -0
- package/lib/core/utils/report-request-scope.d.ts +14 -0
- package/lib/data-access/adapters/endpoint-persistence.adapter.d.ts +6 -2
- package/lib/data-access/adapters/endpoint-schema-discovery.adapter.d.ts +4 -2
- package/lib/data-access/adapters/endpoint-tokens.d.ts +4 -0
- package/lib/data-access/adapters/http-datasource.adapter.d.ts +1 -3
- package/lib/data-access/adapters/http-lookup.adapter.d.ts +1 -3
- package/lib/data-access/adapters/http-persistence.adapter.d.ts +11 -16
- package/lib/data-access/adapters/http-schema-discovery.adapter.d.ts +3 -5
- package/lib/data-access/adapters/index.d.ts +1 -1
- package/lib/data-access/adapters/local-storage-persistence.adapter.d.ts +2 -2
- package/lib/data-access/adapters/report-id-availability.d.ts +6 -0
- package/lib/data-access/adapters/session-headers.d.ts +3 -0
- package/lib/data-access/contracts/index.d.ts +1 -0
- package/lib/data-access/contracts/report-persistence.contract.d.ts +16 -1
- package/lib/features/designer/components/design-canvas/design-canvas.component.d.ts +2 -0
- package/lib/features/designer/components/dialogs/report-saved-dialog.component.d.ts +37 -5
- package/lib/features/designer/components/property-editor/property-editor-datasource.component.d.ts +4 -3
- package/lib/features/designer/components/property-editor/property-editor-grid.component.d.ts +1 -0
- package/lib/features/designer/components/property-editor/property-editor.component.d.ts +1 -0
- package/lib/features/designer/components/toolbar/designer-toolbar.component.d.ts +7 -1
- package/lib/features/designer/designer.component.d.ts +15 -2
- package/lib/features/designer/rpt-report-designer.component.d.ts +5 -1
- package/lib/features/designer/services/designer-config.service.d.ts +13 -0
- package/lib/features/designer/services/designer-endpoints.service.d.ts +25 -0
- package/lib/features/persistence/services/report-store.service.d.ts +15 -1
- package/lib/features/persistence/services/schema-validator.service.d.ts +2 -0
- package/lib/features/routing/resolvers/report-route-identifier.d.ts +2 -0
- package/lib/features/runtime/report-execution.service.d.ts +3 -0
- package/lib/features/runtime/report-grid-bridge.component.d.ts +1 -0
- package/lib/features/runtime/report-runtime.component.d.ts +1 -0
- package/lib/features/runtime/rpt-report.component.d.ts +5 -0
- package/lib/primitives/components/chart-bridge/report-chart-bridge.component.d.ts +3 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,448 +1,595 @@
|
|
|
1
|
-
# BWays-Report —
|
|
1
|
+
# BWays-Report — Enterprise Reporting & Visual Page Builder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/bways-report)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://angular.io)
|
|
6
|
+
|
|
7
|
+
`bways-report` is an enterprise-grade reporting engine and visual drag-and-drop page builder for Angular 18+ business & ERP applications with NestJS / Microsoft SQL Server.
|
|
4
8
|
|
|
5
|
-
It
|
|
9
|
+
It eliminates the need to manually code components, reactive forms, date pickers, dropdowns, and data grids for every single report. Instead, reports are designed visually in the browser, stored permanently in the database as clean JSON metadata, and rendered dynamically at runtime with live stored procedures, server-side execution, and data export.
|
|
6
10
|
|
|
7
11
|
---
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
## Why bways-report? (GUI vs. Manual Coding)
|
|
14
|
+
|
|
15
|
+
In traditional ERP and business applications, creating 50 reports requires:
|
|
16
|
+
- Generating 50 separate Angular components (`ng g c ...`)
|
|
17
|
+
- Writing thousands of lines of boilerplate HTML forms, labels, validation, and CSS grids
|
|
18
|
+
- Hardcoding TypeScript column definitions, formatting, sorting, and pagination
|
|
19
|
+
- Recompiling, testing, and **re-deploying the frontend application** for every column or label change requested by clients
|
|
20
|
+
|
|
21
|
+
### With `bways-report`:
|
|
22
|
+
| Feature | Traditional Coding | With `bways-report` |
|
|
23
|
+
| :--- | :--- | :--- |
|
|
24
|
+
| **New Report Creation** | Hours of coding `.ts`, `.html`, `.css` | **2 minutes** in visual drag-and-drop designer |
|
|
25
|
+
| **Component Files** | 1 component per report (50 reports = 50 files) | **0 new files**. Handled dynamically by a single generic viewer |
|
|
26
|
+
| **Client Change Requests** | Edit code, re-build Angular bundle, redeploy | Edit in GUI, click **Save**. Changes are live instantly! |
|
|
27
|
+
| **Persistence** | Hardcoded templates | Stored in SQL Server (`CNFREPORTDEF`) |
|
|
28
|
+
| **Data Export** | Manual Excel/CSV export logic | Built-in 1-click Excel, CSV, and Print exports |
|
|
29
|
+
|
|
11
30
|
---
|
|
12
31
|
|
|
13
|
-
##
|
|
32
|
+
## How It Works (The Lifecycle)
|
|
14
33
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
```
|
|
35
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ 1. VISUAL DESIGNER (<rpt-designer>) │
|
|
37
|
+
│ • Drag & drop inputs, date ranges, lookup dropdowns │
|
|
38
|
+
│ • Connect stored procedures & select output grid columns │
|
|
39
|
+
│ • Click "Save" ───► Generates ReportDefinition JSON │
|
|
40
|
+
└──────────────────────────────┬──────────────────────────────┘
|
|
41
|
+
│ POST /api/reporting/reports
|
|
42
|
+
▼
|
|
43
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
44
|
+
│ 2. DATABASE STORAGE (SQL Server) │
|
|
45
|
+
│ • Saved to table: CNFREPORTDEF │
|
|
46
|
+
│ • Column DEFJSON: Stores the report JSON schema │
|
|
47
|
+
│ • Relational columns: CODE, NAME, VERSION, STATUS_CODE │
|
|
48
|
+
└──────────────────────────────┬──────────────────────────────┘
|
|
49
|
+
│ GET /api/reporting/reports/:id
|
|
50
|
+
▼
|
|
51
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
52
|
+
│ 3. RUNTIME ENGINE (<rpt-report> or bwaysReportRoutes) │
|
|
53
|
+
│ • User opens route: /reports/:slug │
|
|
54
|
+
│ • Fetches JSON schema from database │
|
|
55
|
+
│ • Renders live Reactive Forms, Parameter Validation, │
|
|
56
|
+
│ UltraGrid, Charts, and Excel/CSV/Print export toolbar │
|
|
57
|
+
└─────────────────────────────────────────────────────────────┘
|
|
58
|
+
```
|
|
23
59
|
|
|
24
60
|
---
|
|
25
61
|
|
|
26
62
|
## Table of Contents
|
|
27
63
|
|
|
28
|
-
1. [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
|
|
35
|
-
- [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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)
|
|
64
|
+
1. [Installation](#1-installation)
|
|
65
|
+
2. [Designer Configuration](#2-designer-configuration)
|
|
66
|
+
3. [Declarative Routes (`app.routes.ts`) — Zero Component Code](#3-declarative-routes-approutests)
|
|
67
|
+
4. [Component Usage Patterns](#4-component-usage-patterns)
|
|
68
|
+
- [Pattern A: Embed a Saved Report by ID (`<rpt-report [id]>`)](#pattern-a-embed-a-saved-report-by-id)
|
|
69
|
+
- [Pattern B: Embed from In-Memory JSON (`<rpt-report [report]>`)](#pattern-b-embed-from-in-memory-json)
|
|
70
|
+
- [Pattern C: Embed the Visual Designer (`<rpt-designer>`)](#pattern-c-embed-the-visual-designer)
|
|
71
|
+
- [Pattern D: Toggle View / Edit on the Same Page](#pattern-d-toggle-view--edit-on-the-same-page)
|
|
72
|
+
5. [Database Persistence & SQL Schema](#5-database-persistence--sql-schema)
|
|
73
|
+
6. [Save Confirmation & Host Notifications](#6-save-confirmation--host-notifications)
|
|
74
|
+
7. [Data Grid & UltraGrid Integration](#7-data-grid--ultragrid-integration)
|
|
75
|
+
8. [Backend API Contract](#8-backend-api-contract)
|
|
46
76
|
|
|
47
77
|
---
|
|
48
78
|
|
|
49
|
-
## 1.
|
|
79
|
+
## 1. Installation
|
|
50
80
|
|
|
51
|
-
|
|
81
|
+
Install the package and its peer dependencies via npm:
|
|
52
82
|
|
|
53
83
|
```bash
|
|
54
|
-
npm install bways-report
|
|
84
|
+
npm install bways-report @angular/cdk
|
|
55
85
|
```
|
|
56
86
|
|
|
57
|
-
> **
|
|
87
|
+
> **Requirements**: Angular 18+ or 19+ (standalone architecture). Compatible with both Zone.js and Zoneless change detection.
|
|
58
88
|
|
|
59
89
|
---
|
|
60
90
|
|
|
61
|
-
|
|
91
|
+
## 2. Designer Configuration
|
|
62
92
|
|
|
63
|
-
Configure
|
|
93
|
+
### Configure inside the designer page
|
|
94
|
+
|
|
95
|
+
Define a typed object in the page component and bind it through `[config]`. An embedded designer does not require `provideBwaysReport()` in `app.config.ts`. Register Angular's `provideHttpClient()` once in the host application so its authentication interceptors remain available.
|
|
64
96
|
|
|
65
97
|
```typescript
|
|
66
|
-
//
|
|
67
|
-
import {
|
|
98
|
+
// rpt-designer.component.ts
|
|
99
|
+
import { Component } from '@angular/core';
|
|
100
|
+
import { BwaysReportConfig, DesignerComponent } from 'bways-report';
|
|
101
|
+
|
|
102
|
+
@Component({
|
|
103
|
+
selector: 'app-rpt-designer',
|
|
104
|
+
standalone: true,
|
|
105
|
+
imports: [DesignerComponent],
|
|
106
|
+
templateUrl: './rpt-designer.component.html',
|
|
107
|
+
styles: [`
|
|
108
|
+
:host {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
height: calc(100dvh - 64px);
|
|
112
|
+
min-height: 0;
|
|
113
|
+
min-width: 0;
|
|
114
|
+
}
|
|
115
|
+
`],
|
|
116
|
+
})
|
|
117
|
+
export class RptDesignerComponent {
|
|
118
|
+
readonly designerConfig: BwaysReportConfig = {
|
|
119
|
+
spEndpoint: '/api/reporting/procedures',
|
|
120
|
+
tablesEndpoint: '/api/reporting/tables',
|
|
121
|
+
executeEndpoint: '/api/reporting/execute',
|
|
122
|
+
lookupEndpoint: '/api/reporting/lookup',
|
|
123
|
+
session: { companyId: 101, branchId: 1 },
|
|
124
|
+
autoParameter: true,
|
|
125
|
+
components: ['formInputs', 'actionButtons'],
|
|
126
|
+
routeBase: 'reports',
|
|
127
|
+
defaultPageSize: 25,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```html
|
|
133
|
+
<!-- rpt-designer.component.html -->
|
|
134
|
+
<rpt-designer [config]="designerConfig"></rpt-designer>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Give the designer page a definite height, as above, adjusting `64px` for your application header. Inside an existing flex layout, use `flex: 1; height: 100%; min-height: 0` on every page wrapper and `min-height: 0` on the parent flex work area. Canvas, preview and JSON then scroll inside the available height. A wrapper that grows with its content inside an `overflow: hidden` application shell clips the page instead.
|
|
138
|
+
|
|
139
|
+
For an application embedding only the designer, the reporting-related bootstrap configuration is:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// app.config.ts
|
|
143
|
+
import { ApplicationConfig } from '@angular/core';
|
|
68
144
|
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
|
-
};
|
|
93
145
|
|
|
94
146
|
export const appConfig: ApplicationConfig = {
|
|
95
|
-
providers: [
|
|
96
|
-
provideZoneChangeDetection({ eventCoalescing: true }),
|
|
97
|
-
provideHttpClient(),
|
|
98
|
-
provideRouter(routes),
|
|
99
|
-
provideBwaysReport(reportConfig),
|
|
100
|
-
],
|
|
147
|
+
providers: [provideHttpClient()],
|
|
101
148
|
};
|
|
102
149
|
```
|
|
103
150
|
|
|
104
|
-
|
|
151
|
+
Retain your application's existing router, authentication interceptors, and other providers. Page configuration selects reporting URLs and tenant context; backend authorization remains the host application's responsibility.
|
|
105
152
|
|
|
106
|
-
|
|
153
|
+
All fields are optional. Settings resolve in this order: library defaults, optional application defaults, `[config]`, then `[apiUrl]` for individual endpoint overrides. Omitted or undefined values inherit defaults. Arrays replace inherited arrays; an empty `components` array retains the existing meaning of unrestricted palette categories.
|
|
107
154
|
|
|
108
|
-
|
|
155
|
+
| Source | Scope | Overrides |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| Library defaults | Built-in fallback settings | None |
|
|
158
|
+
| `provideBwaysReport(...)` | Optional app-wide defaults, including separate viewers and menus | Library defaults |
|
|
159
|
+
| `[config]="designerConfig"` | One designer, including preview and save review | Supplied app settings |
|
|
160
|
+
| `[apiUrl]="apiUrls"` | Endpoint overrides for that designer | Supplied endpoint values from all other sources |
|
|
109
161
|
|
|
110
|
-
|
|
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
|
-
```
|
|
162
|
+
For example, a page can configure all settings locally while overriding only report storage:
|
|
118
163
|
|
|
119
164
|
```typescript
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
165
|
+
// Inside the host component class; import BwaysReportApiUrls from bways-report.
|
|
166
|
+
readonly apiUrls: BwaysReportApiUrls = {
|
|
167
|
+
reportsEndpoint: 'https://reports.example.com:9004/api/reporting/reports',
|
|
168
|
+
};
|
|
169
|
+
```
|
|
123
170
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
imports: [BwaysReportBuilderComponent],
|
|
127
|
-
templateUrl: './admin-reports.component.html',
|
|
128
|
-
})
|
|
129
|
-
export class AdminReportsComponent {
|
|
130
|
-
onReportSaved(report: ReportDefinition): void {
|
|
131
|
-
console.log('Report saved and published:', report.name, report.slug);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
171
|
+
```html
|
|
172
|
+
<rpt-designer [config]="designerConfig" [apiUrl]="apiUrls"></rpt-designer>
|
|
134
173
|
```
|
|
135
174
|
|
|
136
|
-
|
|
175
|
+
Each designer owns its effective settings, session resolution, preview, and storage adapters. Local `grid`, `chart`, `session`/`sessionProvider`, `apiEndpoints`, palette, routing, page size, timeout and date format follow this scope. Session sources support objects, signals, observables, factories using Angular `inject()`, and service classes. Replace the configuration object to update settings; session signals and observables update live. Existing draft edits are preserved and affected adapter caches are cleared. Setting `[config]="null"` restores application defaults while explicit `[apiUrl]` overrides remain active.
|
|
137
176
|
|
|
138
|
-
|
|
177
|
+
The public `<rpt-report-designer>` and `<rpt-designer-view>` facades also accept `[config]`. Configuration stays outside saved report JSON. Separately routed viewers and catalogs need their own application or route configuration; they do not inherit a destroyed designer page's settings. The save output is a notification after confirmed persistence and must not perform a second save.
|
|
139
178
|
|
|
140
|
-
|
|
179
|
+
See the complete example in [rpt-designer.component.ts](../playground/src/app/page-designer/rpt-designer.component.ts) and [rpt-designer.component.html](../playground/src/app/page-designer/rpt-designer.component.html).
|
|
141
180
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
181
|
+
### Optional application defaults
|
|
182
|
+
|
|
183
|
+
Keep endpoint defaults in a typed configuration object. Supply any subset: procedures, tables, execution, lookup, and report storage are independent capabilities. Missing discovery endpoints produce no entries; executing without an execution URL reports a configuration error. Report storage uses the built-in reporting API and writes to `dbo.CNFREPORTDEF`. A separate save URL is not required: omitted `reportsEndpoint` is inferred from the configured reporting endpoints, or defaults to `/api/reporting/reports`. Absolute URLs (including ports) are supported. Server failures remain visible and never fall back to localStorage.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// src/app/config.ts
|
|
187
|
+
import { BwaysReportConfig } from 'bways-report';
|
|
188
|
+
import { environment } from '../environments/environment';
|
|
189
|
+
|
|
190
|
+
const apiBase = environment.baseUrl;
|
|
191
|
+
export const reportingConfig: BwaysReportConfig = {
|
|
192
|
+
spEndpoint: `${apiBase}/api/reporting/procedures`,
|
|
193
|
+
tablesEndpoint: `${apiBase}/api/reporting/tables`,
|
|
194
|
+
executeEndpoint: `${apiBase}/api/reporting/execute`,
|
|
195
|
+
lookupEndpoint: `${apiBase}/api/reporting/lookup`,
|
|
196
|
+
// Optional override; otherwise inferred automatically:
|
|
197
|
+
// reportsEndpoint: `${apiBase}/api/reporting/reports`,
|
|
198
|
+
autoParameter: true,
|
|
199
|
+
};
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// src/app/app.config.ts
|
|
204
|
+
import { ApplicationConfig } from '@angular/core';
|
|
205
|
+
import { provideHttpClient } from '@angular/common/http';
|
|
206
|
+
import { provideRouter } from '@angular/router';
|
|
207
|
+
import { provideBwaysReport } from 'bways-report';
|
|
208
|
+
import { reportingConfig } from './config';
|
|
209
|
+
import { routes } from './app.routes';
|
|
146
210
|
|
|
147
|
-
|
|
148
|
-
|
|
211
|
+
export const appConfig: ApplicationConfig = {
|
|
212
|
+
providers: [provideHttpClient(), provideRouter(routes), provideBwaysReport(reportingConfig)],
|
|
213
|
+
};
|
|
149
214
|
```
|
|
150
215
|
|
|
151
|
-
|
|
216
|
+
The legacy `apiBaseUrl` option remains available. `backendUrl` is not a configuration property.
|
|
217
|
+
|
|
218
|
+
### Override endpoints for one designer
|
|
219
|
+
|
|
220
|
+
Bind a `BwaysReportApiUrls` object to `[apiUrl]`. Every property is optional. Only supplied properties override the effective page configuration and app defaults; the configuration object and other designer instances stay unchanged.
|
|
221
|
+
|
|
152
222
|
```typescript
|
|
153
223
|
import { Component } from '@angular/core';
|
|
154
|
-
import {
|
|
155
|
-
import { injectReportMenu } from 'bways-report';
|
|
224
|
+
import { BwaysReportApiUrls, DesignerComponent } from 'bways-report';
|
|
156
225
|
|
|
157
226
|
@Component({
|
|
227
|
+
selector: 'app-custom-reports',
|
|
158
228
|
standalone: true,
|
|
159
|
-
imports: [
|
|
160
|
-
template:
|
|
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>
|
|
173
|
-
`,
|
|
229
|
+
imports: [DesignerComponent],
|
|
230
|
+
template: '<rpt-designer [apiUrl]="reportApis" />',
|
|
174
231
|
})
|
|
175
|
-
export class
|
|
176
|
-
|
|
232
|
+
export class CustomReportsComponent {
|
|
233
|
+
reportApis: BwaysReportApiUrls = {
|
|
234
|
+
spEndpoint: '/api/custom/procedures',
|
|
235
|
+
// The other URLs inherit reportingConfig defaults.
|
|
236
|
+
};
|
|
177
237
|
}
|
|
178
238
|
```
|
|
179
239
|
|
|
240
|
+
You may provide only `tablesEndpoint`, only `reportsEndpoint`, or any combination of `spEndpoint`, `tablesEndpoint`, `executeEndpoint`, `lookupEndpoint`, `reportsEndpoint`, and `apisEndpoint`. Omitted or `undefined` properties inherit defaults. Supplied URLs must be non-empty strings.
|
|
241
|
+
|
|
242
|
+
Overrides apply to discovery, lookups, live preview execution, and persistence inside that designer. Replace the object to change URLs; newly requested data uses the new configuration, and local adapter caches are cleared. Set `[apiUrl]="null"` to restore app defaults. Discovery choices refresh when the bound object changes; existing form selections are retained. Per-designer URLs are not stored in report JSON and do not change separately routed report viewers.
|
|
243
|
+
|
|
244
|
+
`<rpt-report-designer>` and `<rpt-designer-view>` forward the same input. For NgModule applications, import the standalone `DesignerComponent` in your module's `imports` and use the same binding:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
@NgModule({ imports: [CommonModule, DesignerComponent] })
|
|
248
|
+
export class ReportsModule {}
|
|
249
|
+
```
|
|
250
|
+
|
|
180
251
|
---
|
|
181
252
|
|
|
182
|
-
|
|
253
|
+
## 3. Declarative Routes (`app.routes.ts`)
|
|
183
254
|
|
|
184
|
-
|
|
255
|
+
The helper supplies child routes. Mount them below your chosen path:
|
|
185
256
|
|
|
186
257
|
```typescript
|
|
187
|
-
// src/app/app.routes.ts
|
|
188
258
|
import { Routes } from '@angular/router';
|
|
189
259
|
import { bwaysReportRoutes } from 'bways-report';
|
|
190
|
-
import { AdminReportsComponent } from './pages/admin-reports/admin-reports.component';
|
|
191
260
|
|
|
192
261
|
export const routes: Routes = [
|
|
193
|
-
// Admin visual designer route
|
|
194
|
-
{ path: 'admin/reports', component: AdminReportsComponent },
|
|
195
|
-
|
|
196
|
-
// Live user reporting pages: /reports/:slug (e.g. /reports/sale-report)
|
|
197
262
|
{
|
|
198
263
|
path: 'reports',
|
|
199
|
-
children: bwaysReportRoutes({
|
|
200
|
-
// includeBuilderRoute: false, // Set true to include /reports/new and /reports/:slug/edit
|
|
201
|
-
}),
|
|
264
|
+
children: bwaysReportRoutes({ includeBuilder: true }),
|
|
202
265
|
},
|
|
203
266
|
];
|
|
204
267
|
```
|
|
205
268
|
|
|
206
|
-
|
|
269
|
+
This creates `/reports/:slug`, `/reports/new`, and `/reports/:slug/edit`. Builder routes are opt-in. The helper does not create a catalog route; embed `<bways-report-menu>` in your own catalog page if needed. Use `runtimeGuards` and `builderGuards` to apply host access policies, and `slugParam` to customize the identifier parameter. Backend authorization is also required.
|
|
207
270
|
|
|
208
271
|
---
|
|
209
272
|
|
|
210
|
-
##
|
|
273
|
+
## 4. Component Usage Patterns
|
|
211
274
|
|
|
212
|
-
|
|
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)`
|
|
275
|
+
If you want to embed reports directly inside your existing dashboards or custom pages, use the standalone components:
|
|
219
276
|
|
|
220
|
-
###
|
|
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.
|
|
277
|
+
### Pattern A: Embed a Saved Report by ID
|
|
222
278
|
|
|
223
|
-
|
|
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.
|
|
279
|
+
Import `RptReportComponent` from `bways-report` and add it to your standalone component's `imports`. The simplest viewer template is:
|
|
227
280
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
- `Escape` cancels edits.
|
|
232
|
-
- `blur` persists the change.
|
|
233
|
-
- Changes are pushed to the undo/redo history stack.
|
|
281
|
+
```html
|
|
282
|
+
<rpt-report [id]="'sales-report-id'" />
|
|
283
|
+
```
|
|
234
284
|
|
|
235
|
-
|
|
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.
|
|
285
|
+
The ID is the saved `CNFREPORTDEF.CODE`. The viewer fetches its definition through the configured reporting API, which reads and parses `DEFJSON`; the browser does not connect to SQL Server directly. Configure `provideHttpClient()` and `provideBwaysReport({ reportsEndpoint: '/api/reporting/reports' })` in application or route providers, along with any required execution/session settings. A viewer does not inherit another designer's local `[config]`.
|
|
238
286
|
|
|
239
|
-
|
|
287
|
+
You can also pass initial parameters and handle row clicks:
|
|
240
288
|
|
|
241
|
-
|
|
289
|
+
```html
|
|
290
|
+
<!-- my-dashboard.component.html -->
|
|
291
|
+
<rpt-report
|
|
292
|
+
[id]="'rpt-sales-summary'"
|
|
293
|
+
[parameters]="{ fromDate: '2026-01-01', branchId: 101 }"
|
|
294
|
+
(rowClick)="onRowClick($event)">
|
|
295
|
+
</rpt-report>
|
|
296
|
+
```
|
|
242
297
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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` |
|
|
298
|
+
```typescript
|
|
299
|
+
// my-dashboard.component.ts
|
|
300
|
+
import { Component } from '@angular/core';
|
|
301
|
+
import { RptReportComponent } from 'bways-report';
|
|
249
302
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
]
|
|
303
|
+
@Component({
|
|
304
|
+
selector: 'app-my-dashboard',
|
|
305
|
+
standalone: true,
|
|
306
|
+
imports: [RptReportComponent],
|
|
307
|
+
templateUrl: './my-dashboard.component.html',
|
|
308
|
+
})
|
|
309
|
+
export class MyDashboardComponent {
|
|
310
|
+
onRowClick(event: { row: Record<string, unknown> }): void {
|
|
311
|
+
console.log('User clicked invoice row:', event.row);
|
|
260
312
|
}
|
|
261
|
-
|
|
313
|
+
}
|
|
262
314
|
```
|
|
263
315
|
|
|
264
|
-
|
|
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
|
-
]
|
|
275
|
-
}
|
|
276
|
-
]
|
|
277
|
-
```
|
|
316
|
+
---
|
|
278
317
|
|
|
279
|
-
###
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
```
|
|
284
|
-
{
|
|
285
|
-
|
|
286
|
-
"name": "usp_GetCustomerList",
|
|
287
|
-
"schema": "dbo",
|
|
288
|
-
"parameters": {
|
|
289
|
-
"BranchId": 1,
|
|
290
|
-
"SearchTerm": "Acme"
|
|
291
|
-
}
|
|
318
|
+
### Pattern B: Embed from In-Memory JSON
|
|
319
|
+
|
|
320
|
+
If your host project retrieves the report definition from its own custom service, database table, or static JSON file, pass the object directly:
|
|
321
|
+
|
|
322
|
+
```html
|
|
323
|
+
@if (reportSchema) {
|
|
324
|
+
<rpt-report [report]="reportSchema"></rpt-report>
|
|
292
325
|
}
|
|
293
326
|
```
|
|
294
327
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
328
|
+
```typescript
|
|
329
|
+
import { Component, OnInit, inject } from '@angular/core';
|
|
330
|
+
import { RptReportComponent, ReportDefinition } from 'bways-report';
|
|
331
|
+
import { MyCustomApiService } from './my-custom-api.service';
|
|
332
|
+
|
|
333
|
+
@Component({
|
|
334
|
+
standalone: true,
|
|
335
|
+
imports: [RptReportComponent],
|
|
336
|
+
template: `@if (reportSchema) { <rpt-report [report]="reportSchema" /> }`
|
|
337
|
+
})
|
|
338
|
+
export class CustomReportLoaderComponent implements OnInit {
|
|
339
|
+
private api = inject(MyCustomApiService);
|
|
340
|
+
reportSchema: ReportDefinition | null = null;
|
|
341
|
+
|
|
342
|
+
ngOnInit(): void {
|
|
343
|
+
this.api.fetchReportFromCustomDb('rpt-sales').subscribe((data) => {
|
|
344
|
+
// If data is stored as JSON text, parse it:
|
|
345
|
+
this.reportSchema = typeof data === 'string' ? JSON.parse(data) : data;
|
|
346
|
+
});
|
|
310
347
|
}
|
|
311
348
|
}
|
|
312
349
|
```
|
|
313
350
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
### Pattern C: Embed the Visual Designer
|
|
354
|
+
|
|
355
|
+
Import `DesignerComponent` and `BwaysReportConfig` from `bways-report`. Add `DesignerComponent` to your standalone component's `imports`, or to the `imports` of your NgModule. These template bindings are component inputs, not additional Angular modules. Keep the page's configuration in its TypeScript file:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// Inside the host component class.
|
|
359
|
+
readonly designerConfig: BwaysReportConfig = {
|
|
360
|
+
reportsEndpoint: '/api/reporting/reports',
|
|
361
|
+
// Add discovery, execution, lookup and session settings as required.
|
|
362
|
+
};
|
|
325
363
|
```
|
|
326
364
|
|
|
327
|
-
|
|
365
|
+
Give the host a definite height, for example `height: calc(100dvh - 64px)`, plus `display: flex; flex-direction: column; min-height: 0`. Inside an existing bounded flex layout, use `flex: 1; height: 100%; min-height: 0` on the page wrapper so canvas, preview and JSON can scroll. Register `provideHttpClient()` once in the application.
|
|
328
366
|
|
|
329
|
-
|
|
367
|
+
#### Template 1: Automatic database-backed report selector
|
|
330
368
|
|
|
331
|
-
|
|
369
|
+
```html
|
|
370
|
+
<!-- rpt-designer.component.html -->
|
|
371
|
+
<rpt-designer
|
|
372
|
+
[config]="designerConfig"
|
|
373
|
+
(save)="onReportSaved($event)"
|
|
374
|
+
/>
|
|
375
|
+
```
|
|
332
376
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
377
|
+
The built-in toolbar selector requests `GET /api/reporting/reports` and displays the returned report summaries. Selecting an entry requests `GET /api/reporting/reports/:id`, where `id` maps to `CNFREPORTDEF.CODE`. The backend returns the parsed `DEFJSON` with authoritative ID, name and version metadata. These routes follow your configured `reportsEndpoint`.
|
|
378
|
+
|
|
379
|
+
To initially open a particular saved report, add `[reportId]`:
|
|
380
|
+
|
|
381
|
+
```html
|
|
382
|
+
<rpt-designer
|
|
383
|
+
[config]="designerConfig"
|
|
384
|
+
[reportId]="'sales-report-id'"
|
|
385
|
+
(save)="onReportSaved($event)"
|
|
386
|
+
/>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### Template 2: Host supplies the selected report definition
|
|
390
|
+
|
|
391
|
+
Use this when your application already fetches the saved report through its own service:
|
|
339
392
|
|
|
340
|
-
Session values can be updated at runtime:
|
|
341
393
|
```typescript
|
|
342
|
-
|
|
343
|
-
|
|
394
|
+
// Import ReportDefinition from bways-report.
|
|
395
|
+
selectedReport: ReportDefinition | null = null;
|
|
396
|
+
// Assign the parsed, validated report returned by your host service.
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
```html
|
|
400
|
+
@if (selectedReport) {
|
|
401
|
+
<rpt-designer
|
|
402
|
+
[config]="designerConfig"
|
|
403
|
+
[initialReport]="selectedReport"
|
|
404
|
+
(save)="onReportSaved($event)"
|
|
405
|
+
/>
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`[initialReport]` accepts a complete `ReportDefinition` object, not a raw SQL row or an unparsed `DEFJSON` string. If your API returns `DEFJSON` as text, parse and validate it in your service and handle invalid JSON before assigning it. The standard backend already returns a parsed object. Use either `[reportId]` or `[initialReport]` as the loading source; when both are supplied, a non-null `[initialReport]` takes precedence.
|
|
410
|
+
|
|
411
|
+
This supported template controls the selected definition; it does not replace the library toolbar's report list. The built-in list still uses `reportsEndpoint`.
|
|
412
|
+
|
|
413
|
+
#### Planned host-controlled toolbar API (not implemented)
|
|
414
|
+
|
|
415
|
+
The following is a proposed extension for applications that also want to supply the toolbar list and handle selection themselves. It will not compile with the current library:
|
|
416
|
+
|
|
417
|
+
```html
|
|
418
|
+
<!-- Proposed API only: reports and reportSelected are not available yet. -->
|
|
419
|
+
<rpt-designer
|
|
420
|
+
[config]="designerConfig"
|
|
421
|
+
[reports]="savedReports"
|
|
422
|
+
[initialReport]="selectedReport"
|
|
423
|
+
(reportSelected)="loadReport($event)"
|
|
424
|
+
(save)="onReportSaved($event)"
|
|
425
|
+
/>
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Under this proposal, `savedReports` would contain `ReportSummary[]`, and `loadReport(reportId: string)` would retrieve the selected definition. Full report JSON would be fetched only after selection. There is currently no `[reportState]` input; use the supported `[initialReport]` input for a parsed definition.
|
|
429
|
+
|
|
430
|
+
For both supported templates, `(save)` runs only after successful persistence. Use the handler for notifications or refreshing host data; do not save the same report a second time. Existing IDs require Update or Cancel confirmation before writing.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
### Pattern D: Toggle View / Edit on the Same Page
|
|
344
435
|
|
|
345
|
-
|
|
346
|
-
private readonly session = inject(ReportSessionContextService);
|
|
436
|
+
You can allow administrators to view a report and switch to edit mode on the same screen:
|
|
347
437
|
|
|
348
|
-
|
|
349
|
-
|
|
438
|
+
```html
|
|
439
|
+
<div class="toolbar">
|
|
440
|
+
@if (isAdmin) {
|
|
441
|
+
<button (click)="isEditMode = !isEditMode">
|
|
442
|
+
{{ isEditMode ? 'Exit Designer (View Mode)' : 'Edit Layout (Designer)' }}
|
|
443
|
+
</button>
|
|
350
444
|
}
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
@if (isEditMode) {
|
|
448
|
+
<rpt-designer [reportId]="reportId" (save)="isEditMode = false"></rpt-designer>
|
|
449
|
+
} @else {
|
|
450
|
+
<rpt-report [id]="reportId"></rpt-report>
|
|
351
451
|
}
|
|
352
452
|
```
|
|
353
453
|
|
|
354
454
|
---
|
|
355
455
|
|
|
356
|
-
## 5.
|
|
456
|
+
## 5. Database Persistence & SQL Schema
|
|
457
|
+
|
|
458
|
+
Reports are stored in `dbo.CNFREPORTDEF`. The backend creates this table at startup when it is absent. Its `dbo` alias types must already be installed in the target ERP database.
|
|
459
|
+
|
|
460
|
+
`UTypeLongText1` must be valid for the `CODE` primary key. The complete report JSON needs `DEFJSON nvarchar(max)`: a finite ERP text alias cannot hold a full definition. Startup widens only this column for existing installations; the shared ERP alias types are retained. Other metadata respects the actual column limits (for example, `NAME` may allow only 10 characters). Oversized values produce a clear error in the review dialog without losing the draft.
|
|
461
|
+
|
|
462
|
+
```sql
|
|
463
|
+
IF OBJECT_ID(N'dbo.CNFREPORTDEF', N'U') IS NULL
|
|
464
|
+
BEGIN
|
|
465
|
+
CREATE TABLE [dbo].[CNFREPORTDEF] (
|
|
466
|
+
[CODE] [dbo].[UTypeLongText1] PRIMARY KEY,
|
|
467
|
+
[NAME] [dbo].[UTypeShortText1] NULL,
|
|
468
|
+
[DESCRIPTION] [dbo].[UTypeLongText1] NULL,
|
|
469
|
+
[VERSION] [int] NULL,
|
|
470
|
+
[STATUS_CODE] [int] NULL,
|
|
471
|
+
[DEFJSON] nvarchar(max) NULL,
|
|
472
|
+
[SYSADD_DATETIME] [dbo].[UTypeDateTime] NULL,
|
|
473
|
+
[SYSCHNG_DATETIME] [dbo].[UTypeDateTime] NULL,
|
|
474
|
+
[SYSADD_LOGIN] [dbo].[UTypeUserLogin] NULL,
|
|
475
|
+
[SYSCHNG_LOGIN] [dbo].[UTypeUserLogin] NULL
|
|
476
|
+
);
|
|
477
|
+
END;
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
The HTTP API keeps using `id`, `name`, `description`, `version`, `isPublished` and the complete report object. The backend maps these to `CODE`, `NAME`, `DESCRIPTION`, `VERSION`, `STATUS_CODE` and `DEFJSON`. Only `STATUS_CODE = 1` means published; `0` or `NULL` is treated as draft. Category, slug, layout, components and data sources stay in `DEFJSON`.
|
|
357
481
|
|
|
358
|
-
|
|
482
|
+
Creation fills both audit timestamps and login columns; updates and publication preserve creation audit values and change only `SYSCHNG_*`. Audit logins come from the backend authorization policy's `userLogin`, or remain `NULL` if no authenticated login is supplied.
|
|
359
483
|
|
|
360
|
-
-
|
|
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.
|
|
484
|
+
For existing installations, run [create-report-definitions.sql](../../backend/sql/create-report-definitions.sql), then [migrate-report-definitions.sql](../../backend/sql/migrate-report-definitions.sql) in each intended ERP database. Migration copies missing records from the legacy tables, retains category in JSON, and preserves existing target records and source tables. It is not run automatically on server startup.
|
|
365
485
|
|
|
366
486
|
---
|
|
367
487
|
|
|
368
|
-
## 6.
|
|
488
|
+
## 6. Save Confirmation & Host Notifications
|
|
489
|
+
|
|
490
|
+
Clicking **Save** opens a review dialog before any write. Review the report ID, name, slug, description, category, menu group and publication status, then choose **Save Report** or **Save and Open**. Cancel leaves the draft unchanged. Each save makes a fresh ID check. If the ID exists, a confirmation panel identifies the saved report and version and offers **Update Report** (or **Update and Open**) and **Cancel**. No write occurs before Update. Cancel returns to the review with all entered details intact. During saving the controls are disabled; errors keep the dialog open for retry. Success and navigation happen only after the server confirms storage.
|
|
491
|
+
|
|
492
|
+
The **Report ID** field accepts a custom ID (up to 100 letters, numbers, underscores or hyphens; start with a letter or number). **Random ID** generates a new value, and **Verify ID** checks for duplicates in the designer's configured storage and company. Editing the ID clears its verification result. Every save checks the ID again, including unchanged IDs and IDs previously checked with Verify ID. Failed verification keeps the draft open and performs no write.
|
|
493
|
+
|
|
494
|
+
An available ID creates a new report using a create-only write. Any existing ID, including the original ID or an occupied custom ID, requires explicit update confirmation. Choosing an available custom ID leaves the original intact; review the URL slug for the new copy too. The bundled backend returns HTTP 409 if a new ID is taken after the check, or if a confirmed update targets a report that changed version or was deleted. The draft remains available and another save requires a fresh check and confirmation.
|
|
495
|
+
|
|
496
|
+
The persistence adapter supplies `checkId(id): Observable<boolean>` for Verify ID and `inspectId(id): Observable<ReportIdStatus>` for save confirmation (`exists`, optional `name` and `version`). Existing host adapters inherit catalog-based fallbacks. Built-in HTTP adapters use `GET /reports/:id/availability`; the bundled backend returns the name/version in this compact response without fetching report JSON. A 404 falls back to the catalog; older responses without a version require a read of that report. Verification does not select another report or replace the draft. Other failures never mean that an ID is available.
|
|
497
|
+
|
|
498
|
+
Custom hosts should honor `save(report, { createOnly: true })` for new IDs and `save(report, { expectedVersion })` for confirmed updates. The latter must update only an existing report at that version and reject missing/changed targets atomically. These options are mutually exclusive. Older hosts that ignore them cannot provide concurrency protection. Pending confirmations are invalidated if the ID or endpoint/session context changes.
|
|
369
499
|
|
|
370
|
-
|
|
500
|
+
The designer's `(save)` output is a notification containing the server-confirmed report, including its version. Do not persist it again in the handler:
|
|
501
|
+
|
|
502
|
+
```html
|
|
503
|
+
<rpt-designer (save)="onReportSaved($event)"></rpt-designer>
|
|
504
|
+
```
|
|
371
505
|
|
|
372
506
|
```typescript
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
persistenceAdapter: MyCustomPersistenceAdapter,
|
|
377
|
-
schemaDiscoveryAdapter: MyCustomSchemaDiscoveryAdapter,
|
|
378
|
-
lookupAdapter: MyCustomLookupAdapter,
|
|
379
|
-
});
|
|
507
|
+
onReportSaved(report: ReportDefinition): void {
|
|
508
|
+
this.currentReport = report;
|
|
509
|
+
}
|
|
380
510
|
```
|
|
381
511
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
- `ReportLookupAdapter`
|
|
512
|
+
The built-in backend handles inserts and updates in `CNFREPORTDEF`; no host save handler is needed. For a different backend contract, provide your own `ReportPersistenceAdapter` implementing load, save, list, delete and publish. Its save method must return the persisted report or propagate the failure. Browser storage remains available only through explicit `withLocalStorage()` for offline development.
|
|
513
|
+
|
|
514
|
+
### Export to `.json` File (Git & Version Control)
|
|
515
|
+
In the designer toolbar, click the **JSON** tab and choose **Download / Export JSON**. This generates a `.json` file that can be committed to Git, reviewed, or imported into staging/production environments.
|
|
387
516
|
|
|
388
517
|
---
|
|
389
518
|
|
|
390
|
-
## 7.
|
|
391
|
-
|
|
392
|
-
`bways-report` uses Vanilla CSS custom properties. Customize the tokens in your application's `styles.css`:
|
|
393
|
-
|
|
394
|
-
```css
|
|
395
|
-
:root {
|
|
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;
|
|
403
|
-
--rpt-font-family: 'Inter', system-ui, sans-serif;
|
|
404
|
-
--rpt-border-radius: 6px;
|
|
405
|
-
--rpt-border-radius-lg: 8px;
|
|
406
|
-
}
|
|
519
|
+
## 7. Data Grid & UltraGrid Integration
|
|
407
520
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
```
|
|
521
|
+
`bways-report` includes a responsive built-in data table. To use the high-performance **UltraGrid** from `bways-grid` (virtual scrolling, Excel-like column filters, column pinning, summary rows):
|
|
522
|
+
|
|
523
|
+
1. Install `bways-grid`:
|
|
524
|
+
```bash
|
|
525
|
+
npm install bways-grid
|
|
526
|
+
```
|
|
527
|
+
2. Pass `UltraGridComponent` to `provideBwaysReport()`:
|
|
528
|
+
```typescript
|
|
529
|
+
import { UltraGridComponent } from 'bways-grid';
|
|
530
|
+
import { provideBwaysReport } from 'bways-report';
|
|
531
|
+
import { reportingConfig } from './config';
|
|
532
|
+
|
|
533
|
+
provideBwaysReport({
|
|
534
|
+
...reportingConfig,
|
|
535
|
+
grid: UltraGridComponent, // Uses UltraGrid for all reports
|
|
536
|
+
})
|
|
537
|
+
```
|
|
416
538
|
|
|
417
539
|
---
|
|
418
540
|
|
|
419
|
-
## 8.
|
|
541
|
+
## 8. Backend API Contract
|
|
420
542
|
|
|
421
|
-
|
|
543
|
+
The reporting engine communicates with the backend via these REST endpoints:
|
|
422
544
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
this.exportService.exportCsv(data, {
|
|
433
|
-
fileName: 'sales-register.csv',
|
|
434
|
-
columns: [
|
|
435
|
-
{ field: 'invoiceNo', header: 'Invoice #' },
|
|
436
|
-
{ field: 'amount', header: 'Amount' },
|
|
437
|
-
],
|
|
438
|
-
addBom: true, // UTF-8 Excel compatibility
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
```
|
|
545
|
+
| Endpoint | Method | Purpose | Payload / Response |
|
|
546
|
+
| :--- | :--- | :--- | :--- |
|
|
547
|
+
| `/api/reporting/procedures` | `GET` | Discovers available stored procedures & parameters | Returns `StoredProcedureSummary[]` |
|
|
548
|
+
| `/api/reporting/tables` | `GET` | Discovers database tables, views, and columns | Returns `TableSummary[]` |
|
|
549
|
+
| `/api/reporting/execute` | `POST` | Executes stored procedure or query with form values | Accepts `{ kind, name, parameters }`, returns `Record<string, unknown>[]` |
|
|
550
|
+
| `/api/reporting/reports` | `GET` | Lists all saved report summaries from database | Returns `ReportSummary[]` |
|
|
551
|
+
| `/api/reporting/reports/:id`| `GET` | Fetches single `ReportDefinition` JSON by ID | Returns `ReportDefinition` |
|
|
552
|
+
| `/api/reporting/reports` | `POST`| Inserts/updates `CNFREPORTDEF` | Accepts `{ id, name, definition }` |
|
|
553
|
+
| `/api/reporting/lookup` | `POST` | Fetches dynamic dropdown master data | Accepts `{ table, valueField, displayField }` |
|
|
443
554
|
|
|
444
555
|
---
|
|
445
556
|
|
|
446
557
|
## License
|
|
447
558
|
|
|
448
|
-
|
|
559
|
+
This package is proprietary software. Copyright (c) 2026 CCPL / BWays Team. All rights reserved.
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
## Reliability and security configuration
|
|
563
|
+
|
|
564
|
+
API data sources forward session credentials, session headers and custom headers only to the application's own origin or an explicitly trusted origin. Public external APIs can still be called without these headers. Cross-origin forwarding requires an exact origin, including the port, configured by the host; report JSON cannot grant this permission. Session secrets interpolated into untrusted URL/body parameters are rejected. Host authentication interceptors must enforce their own matching destination policy.
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
readonly designerConfig: BwaysReportConfig = {
|
|
568
|
+
reportsEndpoint: '/api/reporting/reports',
|
|
569
|
+
executeEndpoint: '/api/reporting/execute',
|
|
570
|
+
trustedApiOrigins: ['https://trusted-api.example.com:9443'],
|
|
571
|
+
lookupTimeoutMs: 300000,
|
|
572
|
+
executionTimeoutMs: 60000,
|
|
573
|
+
};
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
The same settings can be application defaults in `provideBwaysReport(...)`. Page-local settings remain isolated. Use origins without paths, trailing slashes, embedded credentials or wildcards. `executionTimeoutMs` controls execution requests; when omitted it follows the configured lookup timeout. Client timeouts unsubscribe requests; database command timeouts remain a separate backend setting.
|
|
577
|
+
|
|
578
|
+
Loading, switching, execution and drill-down cancel superseded requests and reject late results from an earlier session/endpoint context. Closing a drawer or destroying a component cancels its pending work. The report selector exposes loading, empty and retryable error states. Report definitions are checked before rendering; legacy omitted collections are normalized, while malformed structures and unsupported schema versions produce errors.
|
|
579
|
+
|
|
580
|
+
Viewer preset save/delete operations use `expectedVersion` and update the local report/version only after successful storage. Conflicts retain the current view and show an error. These are report-level shared presets; applications needing private per-user presets should provide separate user-scoped storage rather than embedding them in shared report JSON. Custom persistence adapters must implement atomic version checks.
|
|
581
|
+
|
|
582
|
+
For table/view execution, the data source can specify a projection, row limit and explicit equality filters:
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
const dataSource: ReportDataSourceDefinition = {
|
|
586
|
+
id: 'sales', name: 'Sales', endpoint: 'Sales', type: 'table', schema: 'dbo',
|
|
587
|
+
columns: ['InvoiceNo', 'Total', 'BranchId'],
|
|
588
|
+
top: 500,
|
|
589
|
+
filter: { BranchId: '{{session.branchId}}' },
|
|
590
|
+
};
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
The adapter sends `columns`, `top` and resolved `filter` values to the execution API. Unmapped form parameters are not automatically treated as SQL column names. The supplied backend defaults table/view results to 1,000 rows and caps requests at 10,000; lookups default to 500. Table/view limits run inside SQL. Stored procedure results are capped in the response; expensive procedures should implement their own SQL-side paging/limits.
|
|
594
|
+
|
|
595
|
+
Generic backend execution preserves supplied values and leaves omitted procedure arguments for SQL Server to handle. ERP-specific synthetic defaults, flag coercions and legacy fallback behavior require the explicit `legacyErpDefaults` option. See the [backend compatibility settings](../../backend/README.md#query-limits-and-erp-compatibility).
|