cloud-ide-fees 0.0.31 → 0.0.33

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,383 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, computed, effect, Component } from '@angular/core';
3
+ import { CommonModule } from '@angular/common';
4
+ import * as i1 from '@angular/forms';
5
+ import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
6
+ import { CideInputComponent, CideSelectComponent, CideTextareaComponent, CideEleButtonComponent, CurrencyPipe } from 'cloud-ide-element';
7
+
8
+ class FeeDiscountFormComponent {
9
+ // Inputs
10
+ feeId = input.required(...(ngDevMode ? [{ debugName: "feeId" }] : []));
11
+ feeName = input.required(...(ngDevMode ? [{ debugName: "feeName" }] : []));
12
+ feeAmount = input.required(...(ngDevMode ? [{ debugName: "feeAmount" }] : []));
13
+ existingDiscount = input(null, ...(ngDevMode ? [{ debugName: "existingDiscount" }] : []));
14
+ discountReasonOptions = input.required(...(ngDevMode ? [{ debugName: "discountReasonOptions" }] : []));
15
+ // Outputs
16
+ saved = output();
17
+ cancelled = output();
18
+ // Form using signals (modern Angular approach)
19
+ discountForm = new FormGroup({
20
+ discount_type: new FormControl('FIXED_AMOUNT', [Validators.required]),
21
+ discount_value: new FormControl(0, [Validators.required, Validators.min(0)]),
22
+ discount_reason_id: new FormControl(''),
23
+ valid_from: new FormControl(''),
24
+ valid_to: new FormControl(''),
25
+ notes: new FormControl('')
26
+ });
27
+ // Computed values
28
+ maxDiscountValue = computed(() => {
29
+ const type = this.discountForm.get('discount_type')?.value;
30
+ return type === 'PERCENTAGE' ? 100 : this.feeAmount();
31
+ }, ...(ngDevMode ? [{ debugName: "maxDiscountValue" }] : []));
32
+ calculatedDiscountAmount = computed(() => {
33
+ const type = this.discountForm.get('discount_type')?.value;
34
+ const value = this.discountForm.get('discount_value')?.value || 0;
35
+ if (!type || value <= 0)
36
+ return 0;
37
+ if (type === 'PERCENTAGE') {
38
+ return (this.feeAmount() * value) / 100;
39
+ }
40
+ else {
41
+ return Math.min(value, this.feeAmount());
42
+ }
43
+ }, ...(ngDevMode ? [{ debugName: "calculatedDiscountAmount" }] : []));
44
+ constructor() {
45
+ // Initialize form with existing discount data
46
+ effect(() => {
47
+ const existing = this.existingDiscount();
48
+ if (existing) {
49
+ this.discountForm.patchValue({
50
+ discount_type: existing.discount_type,
51
+ discount_value: existing.discount_value,
52
+ discount_reason_id: existing.discount_reason_id || '',
53
+ valid_from: this.formatDateForInput(existing.valid_from),
54
+ valid_to: this.formatDateForInput(existing.valid_to),
55
+ notes: existing.notes || ''
56
+ }, { emitEvent: false });
57
+ }
58
+ else {
59
+ // Set default valid_from to today
60
+ this.discountForm.patchValue({
61
+ valid_from: this.getTodayDateString()
62
+ }, { emitEvent: false });
63
+ }
64
+ });
65
+ // Update max value when discount type changes
66
+ effect(() => {
67
+ const type = this.discountForm.get('discount_type')?.value;
68
+ const control = this.discountForm.get('discount_value');
69
+ if (control && type) {
70
+ const max = type === 'PERCENTAGE' ? 100 : this.feeAmount();
71
+ control.setValidators([
72
+ Validators.required,
73
+ Validators.min(0),
74
+ Validators.max(max)
75
+ ]);
76
+ control.updateValueAndValidity();
77
+ }
78
+ });
79
+ }
80
+ onSave() {
81
+ if (!this.discountForm.valid)
82
+ return;
83
+ const formValue = this.discountForm.value;
84
+ const discountData = {
85
+ discount_type: formValue.discount_type,
86
+ discount_value: formValue.discount_value || 0,
87
+ discount_reason_id: formValue.discount_reason_id || undefined,
88
+ discount_reason_title: this.getDiscountReasonTitle(formValue.discount_reason_id || ''),
89
+ valid_from: formValue.valid_from ? new Date(formValue.valid_from) : undefined,
90
+ valid_to: formValue.valid_to ? new Date(formValue.valid_to) : undefined,
91
+ notes: formValue.notes || undefined
92
+ };
93
+ this.saved.emit(discountData);
94
+ }
95
+ onCancel() {
96
+ this.cancelled.emit();
97
+ }
98
+ formatDateForInput(date) {
99
+ if (!date)
100
+ return '';
101
+ const d = new Date(date);
102
+ return d.toISOString().split('T')[0];
103
+ }
104
+ getTodayDateString() {
105
+ return new Date().toISOString().split('T')[0];
106
+ }
107
+ getDiscountReasonTitle(reasonId) {
108
+ if (!reasonId)
109
+ return undefined;
110
+ const reason = this.discountReasonOptions().find(r => r.value === reasonId);
111
+ return reason?.label;
112
+ }
113
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FeeDiscountFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
114
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: FeeDiscountFormComponent, isStandalone: true, selector: "cide-fee-discount-form", inputs: { feeId: { classPropertyName: "feeId", publicName: "feeId", isSignal: true, isRequired: true, transformFunction: null }, feeName: { classPropertyName: "feeName", publicName: "feeName", isSignal: true, isRequired: true, transformFunction: null }, feeAmount: { classPropertyName: "feeAmount", publicName: "feeAmount", isSignal: true, isRequired: true, transformFunction: null }, existingDiscount: { classPropertyName: "existingDiscount", publicName: "existingDiscount", isSignal: true, isRequired: false, transformFunction: null }, discountReasonOptions: { classPropertyName: "discountReasonOptions", publicName: "discountReasonOptions", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { saved: "saved", cancelled: "cancelled" }, ngImport: i0, template: `
115
+ <div class="tw-p-4 tw-space-y-4">
116
+ <div class="tw-mb-4">
117
+ <h3 class="tw-text-lg tw-font-semibold tw-text-gray-900">
118
+ {{ existingDiscount() ? 'Edit Discount' : 'Add Discount' }}
119
+ </h3>
120
+ <p class="tw-text-sm tw-text-gray-600 tw-mt-1">{{ feeName() }}</p>
121
+ </div>
122
+
123
+ <form [formGroup]="discountForm" class="tw-space-y-4">
124
+ <!-- Discount Type -->
125
+ <div>
126
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
127
+ Discount Type *
128
+ </label>
129
+ <cide-ele-select
130
+ formControlName="discount_type"
131
+ [options]="[{value: 'PERCENTAGE', label: 'Percentage'}, {value: 'FIXED_AMOUNT', label: 'Fixed Amount'}]"
132
+ size="sm">
133
+ </cide-ele-select>
134
+ </div>
135
+
136
+ <!-- Discount Value -->
137
+ <div>
138
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
139
+ Discount Value *
140
+ @if (discountForm.get('discount_type')?.value === 'PERCENTAGE') {
141
+ <span class="tw-text-gray-500">(%)</span>
142
+ } @else {
143
+ <span class="tw-text-gray-500">(Amount)</span>
144
+ }
145
+ </label>
146
+ <cide-ele-input
147
+ type="number"
148
+ formControlName="discount_value"
149
+ [min]="0"
150
+ [max]="maxDiscountValue()"
151
+ size="sm">
152
+ </cide-ele-input>
153
+ </div>
154
+
155
+ <!-- Discount Reason -->
156
+ <div>
157
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
158
+ Discount Reason
159
+ </label>
160
+ <cide-ele-select
161
+ formControlName="discount_reason_id"
162
+ [options]="discountReasonOptions()"
163
+ placeholder="Select reason"
164
+ size="sm">
165
+ </cide-ele-select>
166
+ </div>
167
+
168
+ <!-- Valid From / Valid To -->
169
+ <div class="tw-grid tw-grid-cols-2 tw-gap-4">
170
+ <div>
171
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
172
+ Valid From
173
+ </label>
174
+ <cide-ele-input
175
+ type="date"
176
+ formControlName="valid_from"
177
+ size="sm">
178
+ </cide-ele-input>
179
+ </div>
180
+ <div>
181
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
182
+ Valid To
183
+ </label>
184
+ <cide-ele-input
185
+ type="date"
186
+ formControlName="valid_to"
187
+ size="sm">
188
+ </cide-ele-input>
189
+ </div>
190
+ </div>
191
+
192
+ <!-- Notes -->
193
+ <div>
194
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
195
+ Notes
196
+ </label>
197
+ <cide-ele-textarea
198
+ formControlName="notes"
199
+ rows="2"
200
+ size="sm">
201
+ </cide-ele-textarea>
202
+ </div>
203
+
204
+ <!-- Calculated Discount Preview -->
205
+ @if (calculatedDiscountAmount() > 0) {
206
+ <div class="tw-p-3 tw-bg-blue-50 tw-rounded tw-border tw-border-blue-200">
207
+ <p class="tw-text-sm tw-text-gray-700">
208
+ <span class="tw-font-medium">Calculated Discount:</span>
209
+ <span class="tw-ml-2 tw-font-semibold tw-text-blue-600">
210
+ {{ calculatedDiscountAmount() | currency }}
211
+ </span>
212
+ </p>
213
+ </div>
214
+ }
215
+ </form>
216
+
217
+ <!-- Actions -->
218
+ <div class="tw-flex tw-justify-end tw-gap-2 tw-pt-4 tw-border-t tw-border-gray-200">
219
+ <button
220
+ type="button"
221
+ cideEleButton
222
+ variant="ghost"
223
+ size="sm"
224
+ (click)="onCancel()">
225
+ Cancel
226
+ </button>
227
+ <button
228
+ type="button"
229
+ cideEleButton
230
+ variant="primary"
231
+ size="sm"
232
+ [disabled]="!discountForm.valid"
233
+ (click)="onSave()">
234
+ {{ existingDiscount() ? 'Update Discount' : 'Save Discount' }}
235
+ </button>
236
+ </div>
237
+ </div>
238
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: CideInputComponent, selector: "cide-ele-input", inputs: ["fill", "label", "labelHide", "disabled", "clearInput", "labelPlacement", "labelDir", "placeholder", "leadingIcon", "trailingIcon", "helperText", "helperTextCollapse", "hideHelperAndErrorText", "errorText", "maxlength", "minlength", "required", "autocapitalize", "autocomplete", "type", "width", "id", "ngModel", "option", "min", "max", "step", "size"], outputs: ["ngModelChange"] }, { kind: "component", type: CideSelectComponent, selector: "cide-ele-select", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey", "treeView"], outputs: ["ngModelChange", "change", "searchChange"] }, { kind: "component", type: CideTextareaComponent, selector: "cide-ele-textarea", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "minlength", "maxlength", "rows", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput"], outputs: ["ngModelChange"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton], cide-ele-button", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }] });
239
+ }
240
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FeeDiscountFormComponent, decorators: [{
241
+ type: Component,
242
+ args: [{
243
+ selector: 'cide-fee-discount-form',
244
+ standalone: true,
245
+ imports: [
246
+ CommonModule,
247
+ ReactiveFormsModule,
248
+ CideInputComponent,
249
+ CideSelectComponent,
250
+ CideTextareaComponent,
251
+ CideEleButtonComponent,
252
+ CurrencyPipe
253
+ ],
254
+ template: `
255
+ <div class="tw-p-4 tw-space-y-4">
256
+ <div class="tw-mb-4">
257
+ <h3 class="tw-text-lg tw-font-semibold tw-text-gray-900">
258
+ {{ existingDiscount() ? 'Edit Discount' : 'Add Discount' }}
259
+ </h3>
260
+ <p class="tw-text-sm tw-text-gray-600 tw-mt-1">{{ feeName() }}</p>
261
+ </div>
262
+
263
+ <form [formGroup]="discountForm" class="tw-space-y-4">
264
+ <!-- Discount Type -->
265
+ <div>
266
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
267
+ Discount Type *
268
+ </label>
269
+ <cide-ele-select
270
+ formControlName="discount_type"
271
+ [options]="[{value: 'PERCENTAGE', label: 'Percentage'}, {value: 'FIXED_AMOUNT', label: 'Fixed Amount'}]"
272
+ size="sm">
273
+ </cide-ele-select>
274
+ </div>
275
+
276
+ <!-- Discount Value -->
277
+ <div>
278
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
279
+ Discount Value *
280
+ @if (discountForm.get('discount_type')?.value === 'PERCENTAGE') {
281
+ <span class="tw-text-gray-500">(%)</span>
282
+ } @else {
283
+ <span class="tw-text-gray-500">(Amount)</span>
284
+ }
285
+ </label>
286
+ <cide-ele-input
287
+ type="number"
288
+ formControlName="discount_value"
289
+ [min]="0"
290
+ [max]="maxDiscountValue()"
291
+ size="sm">
292
+ </cide-ele-input>
293
+ </div>
294
+
295
+ <!-- Discount Reason -->
296
+ <div>
297
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
298
+ Discount Reason
299
+ </label>
300
+ <cide-ele-select
301
+ formControlName="discount_reason_id"
302
+ [options]="discountReasonOptions()"
303
+ placeholder="Select reason"
304
+ size="sm">
305
+ </cide-ele-select>
306
+ </div>
307
+
308
+ <!-- Valid From / Valid To -->
309
+ <div class="tw-grid tw-grid-cols-2 tw-gap-4">
310
+ <div>
311
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
312
+ Valid From
313
+ </label>
314
+ <cide-ele-input
315
+ type="date"
316
+ formControlName="valid_from"
317
+ size="sm">
318
+ </cide-ele-input>
319
+ </div>
320
+ <div>
321
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
322
+ Valid To
323
+ </label>
324
+ <cide-ele-input
325
+ type="date"
326
+ formControlName="valid_to"
327
+ size="sm">
328
+ </cide-ele-input>
329
+ </div>
330
+ </div>
331
+
332
+ <!-- Notes -->
333
+ <div>
334
+ <label class="tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1">
335
+ Notes
336
+ </label>
337
+ <cide-ele-textarea
338
+ formControlName="notes"
339
+ rows="2"
340
+ size="sm">
341
+ </cide-ele-textarea>
342
+ </div>
343
+
344
+ <!-- Calculated Discount Preview -->
345
+ @if (calculatedDiscountAmount() > 0) {
346
+ <div class="tw-p-3 tw-bg-blue-50 tw-rounded tw-border tw-border-blue-200">
347
+ <p class="tw-text-sm tw-text-gray-700">
348
+ <span class="tw-font-medium">Calculated Discount:</span>
349
+ <span class="tw-ml-2 tw-font-semibold tw-text-blue-600">
350
+ {{ calculatedDiscountAmount() | currency }}
351
+ </span>
352
+ </p>
353
+ </div>
354
+ }
355
+ </form>
356
+
357
+ <!-- Actions -->
358
+ <div class="tw-flex tw-justify-end tw-gap-2 tw-pt-4 tw-border-t tw-border-gray-200">
359
+ <button
360
+ type="button"
361
+ cideEleButton
362
+ variant="ghost"
363
+ size="sm"
364
+ (click)="onCancel()">
365
+ Cancel
366
+ </button>
367
+ <button
368
+ type="button"
369
+ cideEleButton
370
+ variant="primary"
371
+ size="sm"
372
+ [disabled]="!discountForm.valid"
373
+ (click)="onSave()">
374
+ {{ existingDiscount() ? 'Update Discount' : 'Save Discount' }}
375
+ </button>
376
+ </div>
377
+ </div>
378
+ `
379
+ }]
380
+ }], ctorParameters: () => [], propDecorators: { feeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "feeId", required: true }] }], feeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "feeName", required: true }] }], feeAmount: [{ type: i0.Input, args: [{ isSignal: true, alias: "feeAmount", required: true }] }], existingDiscount: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingDiscount", required: false }] }], discountReasonOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "discountReasonOptions", required: true }] }], saved: [{ type: i0.Output, args: ["saved"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
381
+
382
+ export { FeeDiscountFormComponent };
383
+ //# sourceMappingURL=cloud-ide-fees-fee-discount-form.component-R_yZNKM-.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-ide-fees-fee-discount-form.component-R_yZNKM-.mjs","sources":["../../../projects/cloud-ide-fees/src/lib/shared/components/fee-discount-form/fee-discount-form.component.ts"],"sourcesContent":["\r\nimport { Component, inject, signal, computed, input, output, effect } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';\r\nimport { CideInputComponent, CideSelectComponent, CideTextareaComponent, CideEleButtonComponent, CurrencyPipe } from 'cloud-ide-element';\r\n\r\nexport interface FeeDiscountFormData {\r\n discount_type: 'PERCENTAGE' | 'FIXED_AMOUNT';\r\n discount_value: number;\r\n discount_reason_id?: string;\r\n discount_reason_title?: string;\r\n valid_from?: Date;\r\n valid_to?: Date;\r\n notes?: string;\r\n}\r\n\r\nexport interface FeeDiscountFormInputs {\r\n feeId: string;\r\n feeName: string;\r\n feeAmount: number;\r\n existingDiscount?: FeeDiscountFormData | null;\r\n discountReasonOptions: Array<{ value: string; label: string }>;\r\n}\r\n\r\n@Component({\r\n selector: 'cide-fee-discount-form',\r\n standalone: true,\r\n imports: [\r\n CommonModule,\r\n ReactiveFormsModule,\r\n CideInputComponent,\r\n CideSelectComponent,\r\n CideTextareaComponent,\r\n CideEleButtonComponent,\r\n CurrencyPipe\r\n ],\r\n template: `\r\n <div class=\"tw-p-4 tw-space-y-4\">\r\n <div class=\"tw-mb-4\">\r\n <h3 class=\"tw-text-lg tw-font-semibold tw-text-gray-900\">\r\n {{ existingDiscount() ? 'Edit Discount' : 'Add Discount' }}\r\n </h3>\r\n <p class=\"tw-text-sm tw-text-gray-600 tw-mt-1\">{{ feeName() }}</p>\r\n </div>\r\n\r\n <form [formGroup]=\"discountForm\" class=\"tw-space-y-4\">\r\n <!-- Discount Type -->\r\n <div>\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1\">\r\n Discount Type *\r\n </label>\r\n <cide-ele-select\r\n formControlName=\"discount_type\"\r\n [options]=\"[{value: 'PERCENTAGE', label: 'Percentage'}, {value: 'FIXED_AMOUNT', label: 'Fixed Amount'}]\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n </div>\r\n\r\n <!-- Discount Value -->\r\n <div>\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1\">\r\n Discount Value *\r\n @if (discountForm.get('discount_type')?.value === 'PERCENTAGE') {\r\n <span class=\"tw-text-gray-500\">(%)</span>\r\n } @else {\r\n <span class=\"tw-text-gray-500\">(Amount)</span>\r\n }\r\n </label>\r\n <cide-ele-input\r\n type=\"number\"\r\n formControlName=\"discount_value\"\r\n [min]=\"0\"\r\n [max]=\"maxDiscountValue()\"\r\n size=\"sm\">\r\n </cide-ele-input>\r\n </div>\r\n\r\n <!-- Discount Reason -->\r\n <div>\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1\">\r\n Discount Reason\r\n </label>\r\n <cide-ele-select\r\n formControlName=\"discount_reason_id\"\r\n [options]=\"discountReasonOptions()\"\r\n placeholder=\"Select reason\"\r\n size=\"sm\">\r\n </cide-ele-select>\r\n </div>\r\n\r\n <!-- Valid From / Valid To -->\r\n <div class=\"tw-grid tw-grid-cols-2 tw-gap-4\">\r\n <div>\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1\">\r\n Valid From\r\n </label>\r\n <cide-ele-input\r\n type=\"date\"\r\n formControlName=\"valid_from\"\r\n size=\"sm\">\r\n </cide-ele-input>\r\n </div>\r\n <div>\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1\">\r\n Valid To\r\n </label>\r\n <cide-ele-input\r\n type=\"date\"\r\n formControlName=\"valid_to\"\r\n size=\"sm\">\r\n </cide-ele-input>\r\n </div>\r\n </div>\r\n\r\n <!-- Notes -->\r\n <div>\r\n <label class=\"tw-block tw-text-sm tw-font-medium tw-text-gray-700 tw-mb-1\">\r\n Notes\r\n </label>\r\n <cide-ele-textarea\r\n formControlName=\"notes\"\r\n rows=\"2\"\r\n size=\"sm\">\r\n </cide-ele-textarea>\r\n </div>\r\n\r\n <!-- Calculated Discount Preview -->\r\n @if (calculatedDiscountAmount() > 0) {\r\n <div class=\"tw-p-3 tw-bg-blue-50 tw-rounded tw-border tw-border-blue-200\">\r\n <p class=\"tw-text-sm tw-text-gray-700\">\r\n <span class=\"tw-font-medium\">Calculated Discount:</span>\r\n <span class=\"tw-ml-2 tw-font-semibold tw-text-blue-600\">\r\n {{ calculatedDiscountAmount() | currency }}\r\n </span>\r\n </p>\r\n </div>\r\n }\r\n </form>\r\n\r\n <!-- Actions -->\r\n <div class=\"tw-flex tw-justify-end tw-gap-2 tw-pt-4 tw-border-t tw-border-gray-200\">\r\n <button\r\n type=\"button\"\r\n cideEleButton\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n (click)=\"onCancel()\">\r\n Cancel\r\n </button>\r\n <button\r\n type=\"button\"\r\n cideEleButton\r\n variant=\"primary\"\r\n size=\"sm\"\r\n [disabled]=\"!discountForm.valid\"\r\n (click)=\"onSave()\">\r\n {{ existingDiscount() ? 'Update Discount' : 'Save Discount' }}\r\n </button>\r\n </div>\r\n </div>\r\n `\r\n})\r\nexport class FeeDiscountFormComponent {\r\n // Inputs\r\n readonly feeId = input.required<string>();\r\n readonly feeName = input.required<string>();\r\n readonly feeAmount = input.required<number>();\r\n readonly existingDiscount = input<FeeDiscountFormData | null>(null);\r\n readonly discountReasonOptions = input.required<Array<{ value: string; label: string }>>();\r\n\r\n // Outputs\r\n readonly saved = output<FeeDiscountFormData>();\r\n readonly cancelled = output<void>();\r\n\r\n // Form using signals (modern Angular approach)\r\n discountForm = new FormGroup({\r\n discount_type: new FormControl<'PERCENTAGE' | 'FIXED_AMOUNT'>('FIXED_AMOUNT', [Validators.required]),\r\n discount_value: new FormControl<number>(0, [Validators.required, Validators.min(0)]),\r\n discount_reason_id: new FormControl<string>(''),\r\n valid_from: new FormControl<string>(''),\r\n valid_to: new FormControl<string>(''),\r\n notes: new FormControl<string>('')\r\n });\r\n\r\n // Computed values\r\n readonly maxDiscountValue = computed(() => {\r\n const type = this.discountForm.get('discount_type')?.value;\r\n return type === 'PERCENTAGE' ? 100 : this.feeAmount();\r\n });\r\n\r\n readonly calculatedDiscountAmount = computed(() => {\r\n const type = this.discountForm.get('discount_type')?.value;\r\n const value = this.discountForm.get('discount_value')?.value || 0;\r\n\r\n if (!type || value <= 0) return 0;\r\n\r\n if (type === 'PERCENTAGE') {\r\n return (this.feeAmount() * value) / 100;\r\n } else {\r\n return Math.min(value, this.feeAmount());\r\n }\r\n });\r\n\r\n constructor() {\r\n // Initialize form with existing discount data\r\n effect(() => {\r\n const existing = this.existingDiscount();\r\n if (existing) {\r\n this.discountForm.patchValue({\r\n discount_type: existing.discount_type,\r\n discount_value: existing.discount_value,\r\n discount_reason_id: existing.discount_reason_id || '',\r\n valid_from: this.formatDateForInput(existing.valid_from),\r\n valid_to: this.formatDateForInput(existing.valid_to),\r\n notes: existing.notes || ''\r\n }, { emitEvent: false });\r\n } else {\r\n // Set default valid_from to today\r\n this.discountForm.patchValue({\r\n valid_from: this.getTodayDateString()\r\n }, { emitEvent: false });\r\n }\r\n });\r\n\r\n // Update max value when discount type changes\r\n effect(() => {\r\n const type = this.discountForm.get('discount_type')?.value;\r\n const control = this.discountForm.get('discount_value');\r\n if (control && type) {\r\n const max = type === 'PERCENTAGE' ? 100 : this.feeAmount();\r\n control.setValidators([\r\n Validators.required,\r\n Validators.min(0),\r\n Validators.max(max)\r\n ]);\r\n control.updateValueAndValidity();\r\n }\r\n });\r\n }\r\n\r\n onSave(): void {\r\n if (!this.discountForm.valid) return;\r\n\r\n const formValue = this.discountForm.value;\r\n const discountData: FeeDiscountFormData = {\r\n discount_type: formValue.discount_type!,\r\n discount_value: formValue.discount_value || 0,\r\n discount_reason_id: formValue.discount_reason_id || undefined,\r\n discount_reason_title: this.getDiscountReasonTitle(formValue.discount_reason_id || ''),\r\n valid_from: formValue.valid_from ? new Date(formValue.valid_from) : undefined,\r\n valid_to: formValue.valid_to ? new Date(formValue.valid_to) : undefined,\r\n notes: formValue.notes || undefined\r\n };\r\n\r\n this.saved.emit(discountData);\r\n }\r\n\r\n onCancel(): void {\r\n this.cancelled.emit();\r\n }\r\n\r\n private formatDateForInput(date?: Date): string {\r\n if (!date) return '';\r\n const d = new Date(date);\r\n return d.toISOString().split('T')[0];\r\n }\r\n\r\n private getTodayDateString(): string {\r\n return new Date().toISOString().split('T')[0];\r\n }\r\n\r\n private getDiscountReasonTitle(reasonId: string): string | undefined {\r\n if (!reasonId) return undefined;\r\n const reason = this.discountReasonOptions().find(r => r.value === reasonId);\r\n return reason?.label;\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}