@unifylib/ui-lib 1.0.65 → 1.0.67

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.
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * @param {{
5
+ * kebab: string,
6
+ * pascal: string,
7
+ * labelsSection: string,
8
+ * importPath: string,
9
+ * }} ctx
10
+ */
11
+ function generate(ctx) {
12
+ const fileBase = ctx.kebab;
13
+
14
+ const componentTs = `import { ChangeDetectionStrategy, Component, inject } from "@angular/core";
15
+ import { Router } from "@angular/router";
16
+ import { TranslateModule } from "@ngx-translate/core";
17
+ import { ActionCardComponent, FeatureItem } from "${ctx.importPath}";
18
+
19
+ @Component({
20
+ selector: "app-${fileBase}",
21
+ standalone: true,
22
+ imports: [ActionCardComponent, TranslateModule],
23
+ templateUrl: "./${fileBase}.component.html",
24
+ styleUrls: ["./${fileBase}.component.scss"],
25
+ changeDetection: ChangeDetectionStrategy.OnPush,
26
+ })
27
+ export class ${ctx.pascal}Component {
28
+ private readonly router = inject(Router);
29
+
30
+ readonly cardAFeatures: FeatureItem[] = [
31
+ { textKey: "${ctx.labelsSection}.FEATURE_1" },
32
+ { textKey: "${ctx.labelsSection}.FEATURE_2" },
33
+ ];
34
+
35
+ readonly cardBFeatures: FeatureItem[] = [
36
+ { textKey: "${ctx.labelsSection}.FEATURE_3" },
37
+ { textKey: "${ctx.labelsSection}.FEATURE_4" },
38
+ ];
39
+
40
+ navigateA(): void {
41
+ void this.router.navigate(["/${ctx.kebab}", "feature-a"]);
42
+ }
43
+
44
+ navigateB(): void {
45
+ void this.router.navigate(["/${ctx.kebab}", "feature-b"]);
46
+ }
47
+ }
48
+ `;
49
+
50
+ const componentHtml = `<div class="dashboard-container">
51
+ <div class="welcome-section">
52
+ <h1 class="welcome-title">{{ '${ctx.labelsSection}.TITLE' | translate }}</h1>
53
+ <p class="welcome-subtitle">{{ '${ctx.labelsSection}.SUBTITLE' | translate }}</p>
54
+ </div>
55
+
56
+ <div class="cards-grid">
57
+ <lib-action-card
58
+ class="action-card-item"
59
+ icon="dashboard"
60
+ [titleKey]="'${ctx.labelsSection}.CARD_A_TITLE'"
61
+ [descriptionKey]="'${ctx.labelsSection}.CARD_A_DESC'"
62
+ [buttonTextKey]="'${ctx.labelsSection}.CARD_A_CTA'"
63
+ iconColor="#0d4261"
64
+ borderColor="#bcd4e6"
65
+ [buttonGradient]="'linear-gradient(90deg, #e3f0ff 0%, #0d4261 100%)'"
66
+ [topBorderHeight]="14"
67
+ variant="extended"
68
+ [features]="cardAFeatures"
69
+ [featuresHeadingKey]="'${ctx.labelsSection}.FEATURES'"
70
+ [showDivider]="true"
71
+ (buttonClick)="navigateA()">
72
+ </lib-action-card>
73
+
74
+ <lib-action-card
75
+ class="action-card-item"
76
+ icon="add_to_queue"
77
+ [titleKey]="'${ctx.labelsSection}.CARD_B_TITLE'"
78
+ [descriptionKey]="'${ctx.labelsSection}.CARD_B_DESC'"
79
+ [buttonTextKey]="'${ctx.labelsSection}.CARD_B_CTA'"
80
+ iconColor="#8067A4"
81
+ borderColor="#BDB3CA"
82
+ [buttonGradient]="'linear-gradient(90deg, #D1CBD8 0%, #8168A5 100%)'"
83
+ [topBorderHeight]="14"
84
+ variant="extended"
85
+ [features]="cardBFeatures"
86
+ [featuresHeadingKey]="'${ctx.labelsSection}.FEATURES'"
87
+ [showDivider]="true"
88
+ (buttonClick)="navigateB()">
89
+ </lib-action-card>
90
+ </div>
91
+ </div>
92
+ `;
93
+
94
+ const componentScss = `.dashboard-container {
95
+ padding: 1.5rem;
96
+ }
97
+
98
+ .welcome-section {
99
+ margin-bottom: 2rem;
100
+ }
101
+
102
+ .welcome-title {
103
+ margin: 0 0 0.5rem;
104
+ }
105
+
106
+ .welcome-subtitle {
107
+ margin: 0;
108
+ opacity: 0.85;
109
+ }
110
+
111
+ .cards-grid {
112
+ display: grid;
113
+ gap: 1.5rem;
114
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
115
+ }
116
+
117
+ .action-card-item {
118
+ min-height: 100%;
119
+ }
120
+ `;
121
+
122
+ return [
123
+ { relative: `${fileBase}.component.ts`, content: componentTs },
124
+ { relative: `${fileBase}.component.html`, content: componentHtml },
125
+ { relative: `${fileBase}.component.scss`, content: componentScss },
126
+ ];
127
+ }
128
+
129
+ function getTranslations(ctx) {
130
+ return {
131
+ [`${ctx.labelsSection}.TITLE`]: `${ctx.pascal} Dashboard`,
132
+ [`${ctx.labelsSection}.SUBTITLE`]: "Choose an option to continue",
133
+ [`${ctx.labelsSection}.FEATURES`]: "Features",
134
+ [`${ctx.labelsSection}.CARD_A_TITLE`]: "Card A",
135
+ [`${ctx.labelsSection}.CARD_A_DESC`]: "Description for card A",
136
+ [`${ctx.labelsSection}.CARD_A_CTA`]: "Open",
137
+ [`${ctx.labelsSection}.CARD_B_TITLE`]: "Card B",
138
+ [`${ctx.labelsSection}.CARD_B_DESC`]: "Description for card B",
139
+ [`${ctx.labelsSection}.CARD_B_CTA`]: "Open",
140
+ [`${ctx.labelsSection}.FEATURE_1`]: "Feature 1",
141
+ [`${ctx.labelsSection}.FEATURE_2`]: "Feature 2",
142
+ [`${ctx.labelsSection}.FEATURE_3`]: "Feature 3",
143
+ [`${ctx.labelsSection}.FEATURE_4`]: "Feature 4",
144
+ };
145
+ }
146
+
147
+ module.exports = { generate, getTranslations };
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+
3
+ const { buildFieldInfoLines } = require("../utils");
4
+
5
+ /**
6
+ * @param {{
7
+ * kebab: string,
8
+ * pascal: string,
9
+ * labelsSection: string,
10
+ * apiUri: string,
11
+ * importPath: string,
12
+ * fieldsCount: number,
13
+ * }} ctx
14
+ */
15
+ function generate(ctx) {
16
+ const fileBase = ctx.kebab;
17
+ const fieldsBlock = buildFieldInfoLines(ctx.fieldsCount, 2, " ");
18
+
19
+ const settings = `import { Validators } from "@angular/forms";
20
+ import { FieldInfo, PageInfo } from "${ctx.importPath}";
21
+
22
+ export const PAGE_INFO: PageInfo = {
23
+ id: "${ctx.kebab}",
24
+ labelsSection: "${ctx.labelsSection}",
25
+ hasAddButton: false,
26
+ apiUri: "${ctx.apiUri}",
27
+ draftSupported: false,
28
+ selfRouting: true,
29
+ };
30
+
31
+ export const FIELDS: FieldInfo[] = [
32
+ ${fieldsBlock}
33
+ ];
34
+ `;
35
+
36
+ const componentTs = `import { ChangeDetectionStrategy, Component } from "@angular/core";
37
+ import { FormGroup, ReactiveFormsModule } from "@angular/forms";
38
+ import { TranslateModule } from "@ngx-translate/core";
39
+ import { BaseFormCanvasComponent } from "${ctx.importPath}";
40
+ import { FIELDS, PAGE_INFO } from "./${fileBase}.settings";
41
+
42
+ @Component({
43
+ selector: "app-${fileBase}",
44
+ standalone: true,
45
+ imports: [BaseFormCanvasComponent, ReactiveFormsModule, TranslateModule],
46
+ templateUrl: "./${fileBase}.component.html",
47
+ styleUrls: ["./${fileBase}.component.scss"],
48
+ changeDetection: ChangeDetectionStrategy.OnPush,
49
+ })
50
+ export class ${ctx.pascal}Component {
51
+ readonly pageInfo = PAGE_INFO;
52
+ readonly fields = FIELDS;
53
+ item: Record<string, unknown> = {};
54
+ editable = true;
55
+ form: FormGroup | null = null;
56
+
57
+ onFormUpdated(form: FormGroup): void {
58
+ this.form = form;
59
+ }
60
+ }
61
+ `;
62
+
63
+ const componentHtml = `<app-base-form-canvas
64
+ *ngIf="fields?.length"
65
+ [pageInfo]="pageInfo"
66
+ [fields]="fields"
67
+ [editable]="editable"
68
+ [item]="item"
69
+ (formUpdated)="onFormUpdated($event)">
70
+ </app-base-form-canvas>
71
+ `;
72
+
73
+ const componentScss = `:host {
74
+ display: block;
75
+ }
76
+ `;
77
+
78
+ return [
79
+ { relative: `${fileBase}.settings.ts`, content: settings },
80
+ { relative: `${fileBase}.component.ts`, content: componentTs },
81
+ { relative: `${fileBase}.component.html`, content: componentHtml },
82
+ { relative: `${fileBase}.component.scss`, content: componentScss },
83
+ ];
84
+ }
85
+
86
+ function getTranslations(ctx) {
87
+ const out = {};
88
+ out[`${ctx.labelsSection}.list_title`] = `${ctx.pascal} List`;
89
+ for (let i = 1; i <= ctx.fieldsCount; i++) {
90
+ out[`${ctx.labelsSection}.field${i}`] = `Field ${i}`;
91
+ }
92
+ return out;
93
+ }
94
+
95
+ module.exports = { generate, getTranslations };
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+
3
+ const { buildFieldInfoLines } = require("../utils");
4
+
5
+ /**
6
+ * @param {{
7
+ * kebab: string,
8
+ * pascal: string,
9
+ * labelsSection: string,
10
+ * importPath: string,
11
+ * fieldsCount: number,
12
+ * }} ctx
13
+ */
14
+ function generate(ctx) {
15
+ const fileBase = ctx.kebab;
16
+ const fieldsBlock = buildFieldInfoLines(ctx.fieldsCount, 2, " ");
17
+
18
+ const settings = `import { Validators } from "@angular/forms";
19
+ import { FieldInfo, PageInfo } from "${ctx.importPath}";
20
+
21
+ export const PAGE_INFO: PageInfo = {
22
+ id: "${ctx.kebab}",
23
+ labelsSection: "${ctx.labelsSection}",
24
+ hasAddButton: false,
25
+ draftSupported: false,
26
+ };
27
+
28
+ /** Shared schema for every line row; adjust \`row\` / \`visible\` for header vs body fields in your feature. */
29
+ export const LINE_FIELDS: FieldInfo[] = [
30
+ ${fieldsBlock}
31
+ ];
32
+
33
+ /** Grid columns for the line header row (length matches number of line fields). */
34
+ export const COLUMN_HEADER_WIDTHS: string[] = [
35
+ ${Array.from({ length: ctx.fieldsCount }, () => ' "minmax(0,1fr)"').join(",\n")}
36
+ ];
37
+ `;
38
+
39
+ const componentTs = `import { ChangeDetectionStrategy, Component } from "@angular/core";
40
+ import { TranslateModule } from "@ngx-translate/core";
41
+ import {
42
+ ItemLineEditorComponent,
43
+ ItemsTotal,
44
+ LineItem,
45
+ } from "${ctx.importPath}";
46
+ import { COLUMN_HEADER_WIDTHS, LINE_FIELDS, PAGE_INFO } from "./${fileBase}.settings";
47
+
48
+ @Component({
49
+ selector: "app-${fileBase}",
50
+ standalone: true,
51
+ imports: [ItemLineEditorComponent, TranslateModule],
52
+ templateUrl: "./${fileBase}.component.html",
53
+ styleUrls: ["./${fileBase}.component.scss"],
54
+ changeDetection: ChangeDetectionStrategy.OnPush,
55
+ })
56
+ export class ${ctx.pascal}Component {
57
+ readonly pageInfo = PAGE_INFO;
58
+ readonly lineFields = LINE_FIELDS;
59
+ readonly columnHeaderWidths = COLUMN_HEADER_WIDTHS;
60
+ lines: LineItem[] = [];
61
+ currency = "QAR";
62
+
63
+ onLinesChange(items: LineItem[]): void {
64
+ this.lines = items;
65
+ }
66
+
67
+ onTotalsChange(_totals: ItemsTotal): void {
68
+ // Optional: patch a parent form or show summary.
69
+ }
70
+ }
71
+ `;
72
+
73
+ const componentHtml = `<app-item-line-editor
74
+ [pageInfo]="pageInfo"
75
+ [fields]="lineFields"
76
+ [columnHeaderWidths]="columnHeaderWidths"
77
+ [items]="lines"
78
+ [currency]="currency"
79
+ [title]="'${ctx.labelsSection}.linesTitle'"
80
+ [subtitle]="'${ctx.labelsSection}.linesSubtitle'"
81
+ (itemsChange)="onLinesChange($event)"
82
+ (totalsChange)="onTotalsChange($event)">
83
+ </app-item-line-editor>
84
+ `;
85
+
86
+ const componentScss = `:host {
87
+ display: block;
88
+ }
89
+ `;
90
+
91
+ return [
92
+ { relative: `${fileBase}.settings.ts`, content: settings },
93
+ { relative: `${fileBase}.component.ts`, content: componentTs },
94
+ { relative: `${fileBase}.component.html`, content: componentHtml },
95
+ { relative: `${fileBase}.component.scss`, content: componentScss },
96
+ ];
97
+ }
98
+
99
+ function getTranslations(ctx) {
100
+ const out = {
101
+ [`${ctx.labelsSection}.linesTitle`]: `${ctx.pascal} Lines`,
102
+ [`${ctx.labelsSection}.linesSubtitle`]: "Review and edit line items",
103
+ };
104
+ for (let i = 1; i <= ctx.fieldsCount; i++) {
105
+ out[`${ctx.labelsSection}.field${i}`] = `Field ${i}`;
106
+ }
107
+ return out;
108
+ }
109
+
110
+ module.exports = { generate, getTranslations };
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * @param {{
5
+ * kebab: string,
6
+ * pascal: string,
7
+ * labelsSection: string,
8
+ * importPath: string,
9
+ * }} ctx
10
+ */
11
+ function generate(ctx) {
12
+ const fileBase = ctx.kebab;
13
+
14
+ const settings = `import {
15
+ PermissionConfig,
16
+ PermissionMatrixItem,
17
+ } from "${ctx.importPath}";
18
+
19
+ export const PERMISSION_COLUMNS: PermissionConfig[] = [
20
+ { key: "view", labelKey: "${ctx.labelsSection}.view" },
21
+ { key: "edit", labelKey: "${ctx.labelsSection}.edit" },
22
+ {
23
+ key: "completeAndTerminate",
24
+ labelKey: "${ctx.labelsSection}.completeAndTerminate",
25
+ combinedKeys: ["completePermission", "terminatePermission"],
26
+ },
27
+ ];
28
+
29
+ export const SAMPLE_GROUPS: PermissionMatrixItem[] = [
30
+ {
31
+ id: "sample-group",
32
+ title: "Sample group",
33
+ description: "Replace with data from your API",
34
+ expanded: true,
35
+ permissions: {
36
+ view: false,
37
+ edit: false,
38
+ completePermission: false,
39
+ terminatePermission: false,
40
+ },
41
+ },
42
+ ];
43
+ `;
44
+
45
+ const componentTs = `import { ChangeDetectionStrategy, Component } from "@angular/core";
46
+ import { TranslateModule } from "@ngx-translate/core";
47
+ import {
48
+ PermissionChangeEvent,
49
+ PermissionMatrixComponent,
50
+ PermissionMatrixItem,
51
+ } from "${ctx.importPath}";
52
+ import { PERMISSION_COLUMNS, SAMPLE_GROUPS } from "./${fileBase}.settings";
53
+
54
+ @Component({
55
+ selector: "app-${fileBase}",
56
+ standalone: true,
57
+ imports: [PermissionMatrixComponent, TranslateModule],
58
+ templateUrl: "./${fileBase}.component.html",
59
+ styleUrls: ["./${fileBase}.component.scss"],
60
+ changeDetection: ChangeDetectionStrategy.OnPush,
61
+ })
62
+ export class ${ctx.pascal}Component {
63
+ readonly permissionConfig = PERMISSION_COLUMNS;
64
+ groups: PermissionMatrixItem[] = SAMPLE_GROUPS;
65
+ readonly canEdit = true;
66
+
67
+ onMatrixChange(items: PermissionMatrixItem[]): void {
68
+ this.groups = items;
69
+ }
70
+
71
+ onCell(_event: PermissionChangeEvent): void {
72
+ // Optional: audit / incremental API
73
+ }
74
+ }
75
+ `;
76
+
77
+ const componentHtml = `<lib-permission-matrix
78
+ [items]="groups"
79
+ [permissions]="permissionConfig"
80
+ [titleKey]="'${ctx.labelsSection}.groupMatrix'"
81
+ [selectAllLabelKey]="'${ctx.labelsSection}.selectAll'"
82
+ [expandAllLabelKey]="'${ctx.labelsSection}.expandAll'"
83
+ [collapseAllLabelKey]="'${ctx.labelsSection}.collapseAll'"
84
+ [readonly]="!canEdit"
85
+ (itemsChange)="onMatrixChange($event)"
86
+ (permissionChange)="onCell($event)">
87
+ </lib-permission-matrix>
88
+ `;
89
+
90
+ const componentScss = `:host {
91
+ display: block;
92
+ }
93
+ `;
94
+
95
+ return [
96
+ { relative: `${fileBase}.settings.ts`, content: settings },
97
+ { relative: `${fileBase}.component.ts`, content: componentTs },
98
+ { relative: `${fileBase}.component.html`, content: componentHtml },
99
+ { relative: `${fileBase}.component.scss`, content: componentScss },
100
+ ];
101
+ }
102
+
103
+ function getTranslations(ctx) {
104
+ return {
105
+ [`${ctx.labelsSection}.groupMatrix`]: "Group matrix",
106
+ [`${ctx.labelsSection}.selectAll`]: "Select all",
107
+ [`${ctx.labelsSection}.expandAll`]: "Expand all",
108
+ [`${ctx.labelsSection}.collapseAll`]: "Collapse all",
109
+ [`${ctx.labelsSection}.view`]: "View",
110
+ [`${ctx.labelsSection}.edit`]: "Edit",
111
+ [`${ctx.labelsSection}.completeAndTerminate`]: "Complete and terminate",
112
+ };
113
+ }
114
+
115
+ module.exports = { generate, getTranslations };
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+
3
+ const { buildFieldInfoLines } = require("../utils");
4
+
5
+ /**
6
+ * @param {{
7
+ * kebab: string,
8
+ * pascal: string,
9
+ * labelsSection: string,
10
+ * apiUri: string,
11
+ * importPath: string,
12
+ * fieldsCount: number,
13
+ * }} ctx
14
+ */
15
+ function generate(ctx) {
16
+ const fileBase = ctx.kebab;
17
+ const fieldsBlock = buildFieldInfoLines(ctx.fieldsCount, 2, " ");
18
+
19
+ const settings = `import { Validators } from "@angular/forms";
20
+ import { FieldInfo, PageInfo } from "${ctx.importPath}";
21
+
22
+ export const PAGE_INFO: PageInfo = {
23
+ id: "${ctx.kebab}",
24
+ labelsSection: "${ctx.labelsSection}",
25
+ hasAddButton: false,
26
+ apiUri: "${ctx.apiUri}",
27
+ draftSupported: false,
28
+ selfRouting: true,
29
+ };
30
+
31
+ export const FIELDS: FieldInfo[] = [
32
+ ${fieldsBlock}
33
+ ];
34
+ `;
35
+
36
+ const componentTs = `import { ChangeDetectionStrategy, Component } from "@angular/core";
37
+ import { FormGroup, ReactiveFormsModule } from "@angular/forms";
38
+ import { TranslateModule } from "@ngx-translate/core";
39
+ import { SectionFormCanvasComponent } from "${ctx.importPath}";
40
+ import { FIELDS, PAGE_INFO } from "./${fileBase}.settings";
41
+
42
+ @Component({
43
+ selector: "app-${fileBase}",
44
+ standalone: true,
45
+ imports: [SectionFormCanvasComponent, ReactiveFormsModule, TranslateModule],
46
+ templateUrl: "./${fileBase}.component.html",
47
+ styleUrls: ["./${fileBase}.component.scss"],
48
+ changeDetection: ChangeDetectionStrategy.OnPush,
49
+ })
50
+ export class ${ctx.pascal}Component {
51
+ readonly pageInfo = PAGE_INFO;
52
+ readonly fields = FIELDS;
53
+ item: Record<string, unknown> = {};
54
+ editable = true;
55
+ form: FormGroup | null = null;
56
+ readonly sectionKey = 1;
57
+
58
+ onFormEdit(form: FormGroup): void {
59
+ this.form = form;
60
+ }
61
+ }
62
+ `;
63
+
64
+ const componentHtml = `<lib-section-form-canvas
65
+ [pageInfo]="pageInfo"
66
+ [fields]="fields"
67
+ [editable]="editable"
68
+ [item]="item"
69
+ [title]="'sectionTitle'"
70
+ [isTranslateTitle]="true"
71
+ [key]="sectionKey"
72
+ (formEdit)="onFormEdit($event)">
73
+ </lib-section-form-canvas>
74
+ `;
75
+
76
+ const componentScss = `:host {
77
+ display: block;
78
+ }
79
+
80
+ .location-card {
81
+ margin-bottom: 1rem;
82
+ }
83
+ `;
84
+
85
+ return [
86
+ { relative: `${fileBase}.settings.ts`, content: settings },
87
+ { relative: `${fileBase}.component.ts`, content: componentTs },
88
+ { relative: `${fileBase}.component.html`, content: componentHtml },
89
+ { relative: `${fileBase}.component.scss`, content: componentScss },
90
+ ];
91
+ }
92
+
93
+ function getTranslations(ctx) {
94
+ const out = {
95
+ [`${ctx.labelsSection}.sectionTitle`]: `${ctx.pascal} Section`,
96
+ };
97
+ for (let i = 1; i <= ctx.fieldsCount; i++) {
98
+ out[`${ctx.labelsSection}.field${i}`] = `Field ${i}`;
99
+ }
100
+ return out;
101
+ }
102
+
103
+ module.exports = { generate, getTranslations };