Skip to content

Commit 78a3bd4

Browse files
committed
add ui
1 parent 0a81323 commit 78a3bd4

2 files changed

Lines changed: 107 additions & 37 deletions

File tree

src/app/component/income-components/saving/saving.component.html

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -201,19 +201,22 @@ <h3 class="text-xl font-bold">
201201
<i class="fa-solid fa-xmark"></i>
202202
</button>
203203
</div>
204+
204205
<div class="space-y-4 mb-6">
205206
<div>
206207
<label class="block mb-2 text-sm opacity-60 font-medium">Amount (₹)</label>
207208
<div class="relative">
208209
<span class="absolute left-4 top-3.5 opacity-40"><i class="fa-solid fa-money-bill-wave"></i></span>
209-
<input type="number" [(ngModel)]="savingForm.amount" placeholder="0.00" autofocus
210+
<input type="number" [(ngModel)]="savingForm.amount" placeholder="0.00" autofocus min="1"
211+
max="100000000"
210212
class="w-full py-3 pl-10 pr-4 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500 text-lg font-medium"
213+
[class.border-red-500]="savingFormSubmitted && savingAmountError"
211214
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
212215
</div>
213-
@if (savingForm.amount !== null && savingForm.amount !== undefined && savingForm.amount <= 0) { <p
214-
class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
215-
class="fa-solid fa-circle-exclamation mr-1"></i>Amount must be greater than zero.</p>
216-
}
216+
@if (savingFormSubmitted && savingAmountError) {
217+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
218+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ savingAmountError }}</p>
219+
}
217220
</div>
218221

219222
<div>
@@ -222,9 +225,10 @@ <h3 class="text-xl font-bold">
222225
<span class="absolute left-4 top-3.5 opacity-40"><i class="fa-solid fa-calendar-day"></i></span>
223226
<input type="date" [(ngModel)]="savingForm.date" [max]="todayDateStr"
224227
class="w-full py-3 pl-10 pr-4 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500 font-medium color-scheme-dynamic"
228+
[class.border-red-500]="savingFormSubmitted && savingDateError"
225229
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
226230
</div>
227-
@if (savingDateError) {
231+
@if (savingFormSubmitted && savingDateError) {
228232
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
229233
class="fa-solid fa-circle-exclamation mr-1"></i>{{ savingDateError }}</p>
230234
}
@@ -235,16 +239,23 @@ <h3 class="text-xl font-bold">
235239
<div class="relative">
236240
<span class="absolute left-4 top-3.5 opacity-40"><i class="fa-solid fa-pen"></i></span>
237241
<input type="text" [(ngModel)]="savingForm.note" placeholder="e.g. Salary, Bonus, Extra cash"
242+
maxlength="100"
238243
class="w-full py-3 pl-10 pr-4 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500"
244+
[class.border-red-500]="savingFormSubmitted && savingNoteError"
239245
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
240246
</div>
247+
@if (savingFormSubmitted && savingNoteError) {
248+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
249+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ savingNoteError }}</p>
250+
}
241251
</div>
242252
</div>
243253

244254
<div class="flex gap-3">
245-
<button (click)="closeModals()" class="flex-1 py-4 font-medium transition rounded-xl hover:opacity-80"
246-
style="background-color: var(--color-bg-500); color: inherit;">Cancel</button>
247-
<button (click)="saveSavingsData()" [disabled]="!isSavingFormValid" [class.opacity-50]="!isSavingFormValid"
255+
<button (click)="closeModals()"
256+
class="flex-1 py-4 font-medium transition rounded-xl hover:opacity-80 border"
257+
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">Cancel</button>
258+
<button (click)="saveSavingsData()"
248259
class="flex-1 py-4 font-medium text-white transition bg-indigo-500 rounded-xl shadow-lg active:scale-95">
249260
{{ editingSavingId ? 'Update' : 'Save' }}
250261
</button>
@@ -337,66 +348,89 @@ <h3 class="text-xl font-medium">
337348
<i class="fa-solid fa-xmark"></i>
338349
</button>
339350
</div>
351+
340352
<div class="space-y-4 mb-6">
341353
<div>
342354
<label class="block mb-2 text-sm opacity-60 font-medium">Goal Name</label>
343355
<div class="relative">
344356
<span class="absolute left-4 top-3.5 opacity-40"><i class="fa-solid fa-flag-checkered"></i></span>
345357
<input type="text" [(ngModel)]="goalForm.goal_name" placeholder="e.g. Dream Car, Vacation"
358+
maxlength="50"
346359
class="w-full py-3 pl-10 pr-4 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500 font-medium"
360+
[class.border-red-500]="goalFormSubmitted && goalNameError"
347361
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
348362
</div>
363+
@if (goalFormSubmitted && goalNameError) {
364+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
365+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ goalNameError }}</p>
366+
}
349367
</div>
350368

351369
<div>
352370
<label class="block mb-2 text-sm opacity-60 font-medium">Target Amount (₹)</label>
353371
<div class="relative">
354372
<span class="absolute left-4 top-3.5 opacity-40"><i class="fa-solid fa-bullseye"></i></span>
355-
<input type="number" [(ngModel)]="goalForm.target_amount" placeholder="0.00"
373+
<input type="number" [(ngModel)]="goalForm.target_amount" placeholder="0.00" min="1" max="100000000"
356374
class="w-full py-3 pl-10 pr-4 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500 text-lg font-medium"
375+
[class.border-red-500]="goalFormSubmitted && goalAmountError"
357376
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
358377
</div>
378+
@if (goalFormSubmitted && goalAmountError) {
379+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
380+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ goalAmountError }}</p>
381+
}
359382
</div>
360383

361384
<div class="grid grid-cols-2 gap-3">
362385
<div>
363386
<label class="block mb-2 text-sm opacity-60 font-medium">Start Date</label>
364387
<input type="date" [(ngModel)]="goalForm.start_date" [max]="goalForm.target_date" required
365388
class="w-full py-3 px-3 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500 font-medium text-sm color-scheme-dynamic"
389+
[class.border-red-500]="goalFormSubmitted && goalStartDateError"
366390
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
391+
392+
@if (goalFormSubmitted && goalStartDateError) {
393+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
394+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ goalStartDateError }}</p>
395+
}
367396
</div>
368397

369398
<div>
370399
<label class="block mb-2 text-sm opacity-60 font-medium">Target Date</label>
371400
<input type="date" [(ngModel)]="goalForm.target_date" [min]="goalForm.start_date" required
372401
class="w-full py-3 px-3 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500 font-medium text-sm color-scheme-dynamic"
402+
[class.border-red-500]="goalFormSubmitted && goalTargetDateError"
373403
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
374-
</div>
375-
</div>
376404

377-
@if (goalDateError) {
378-
<div
379-
class="p-3 text-xs text-red-500 rounded-lg bg-red-500/10 animate-fade-in border border-red-500/20 flex items-center gap-2">
380-
<i class="fa-solid fa-circle-exclamation"></i>
381-
{{ goalDateError }}
405+
@if (goalFormSubmitted && goalTargetDateError) {
406+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
407+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ goalTargetDateError }}</p>
408+
}
409+
</div>
382410
</div>
383-
}
384411

385412
<div>
386413
<label class="block mb-2 text-sm opacity-60 font-medium">Note</label>
387414
<div class="relative">
388415
<span class="absolute left-4 top-3.5 opacity-40"><i class="fa-solid fa-pen"></i></span>
389416
<input type="text" [(ngModel)]="goalForm.note" placeholder="Any additional details..."
417+
maxlength="100"
390418
class="w-full py-3 pl-10 pr-4 transition-colors border rounded-xl focus:outline-none focus:border-indigo-500"
419+
[class.border-red-500]="goalFormSubmitted && goalNoteError"
391420
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">
392421
</div>
422+
@if (goalFormSubmitted && goalNoteError) {
423+
<p class="text-xs text-red-500 mt-1.5 animate-fade-in"><i
424+
class="fa-solid fa-circle-exclamation mr-1"></i>{{ goalNoteError }}</p>
425+
}
393426
</div>
394427
</div>
395428

396429
<div class="flex gap-3">
397-
<button (click)="closeModals()" class="flex-1 py-4 font-medium transition rounded-xl hover:opacity-80 border"
430+
<button (click)="closeModals()"
431+
class="flex-1 py-4 font-medium transition rounded-xl hover:opacity-80 border"
398432
style="background-color: var(--color-bg-500); border-color: var(--color-bg-600); color: inherit;">Cancel</button>
399-
<button (click)="saveGoalData()" [disabled]="!isGoalFormValid" [class.opacity-50]="!isGoalFormValid"
433+
<button (click)="saveGoalData()"
400434
class="flex-1 py-4 font-medium text-white transition bg-indigo-500 rounded-xl shadow-lg active:scale-95">
401435
Save Goal
402436
</button>

src/app/component/income-components/saving/saving.component.ts

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export class SavingComponent implements OnInit {
2121
showGoalDetailsModal = false;
2222
editingSavingId: string | null = null;
2323

24+
// Submission flags to trigger error messages only when trying to save
25+
savingFormSubmitted = false;
26+
goalFormSubmitted = false;
27+
2428
todayDateStr = '';
2529
savingForm: Partial<Saving> = {};
2630
goalForm: Partial<Goal> = {};
@@ -72,45 +76,70 @@ export class SavingComponent implements OnInit {
7276
}
7377
}
7478

75-
/* ---------- Validation Getters ---------- */
79+
/* ---------- Saving Form Validation Getters ---------- */
80+
81+
get savingAmountError(): string | null {
82+
if (!this.savingForm.amount || this.savingForm.amount <= 0) return "Amount must be greater than zero.";
83+
if (this.savingForm.amount > 100000000) return "Amount cannot exceed ₹100,000,000.";
84+
return null;
85+
}
7686

7787
get savingDateError(): string | null {
7888
if (!this.savingForm.date) return "Date is required.";
7989
if (new Date(this.savingForm.date) > new Date(this.todayDateStr)) return "Date cannot be in the future.";
8090
return null;
8191
}
8292

93+
get savingNoteError(): string | null {
94+
if (this.savingForm.note && this.savingForm.note.length > 100) return "Note cannot exceed 100 characters.";
95+
return null;
96+
}
97+
8398
get isSavingFormValid(): boolean {
84-
return !!this.savingForm.amount && this.savingForm.amount > 0 && !this.savingDateError;
99+
return !this.savingAmountError && !this.savingDateError && !this.savingNoteError;
85100
}
86101

87-
get goalDateError(): string | null {
88-
const { start_date, target_date } = this.goalForm;
102+
/* ---------- Goal Form Validation Getters ---------- */
89103

90-
if (!start_date || !target_date) {
91-
return "Both Start Date and Target Date are required.";
92-
}
104+
get goalNameError(): string | null {
105+
if (!this.goalForm.goal_name || this.goalForm.goal_name.trim() === '') return "Goal Name is required.";
106+
if (this.goalForm.goal_name.length > 50) return "Goal Name cannot exceed 50 characters.";
107+
return null;
108+
}
93109

94-
if (new Date(start_date) > new Date(target_date)) {
95-
return "Start date cannot be after target date.";
110+
get goalAmountError(): string | null {
111+
if (!this.goalForm.target_amount || this.goalForm.target_amount <= 0) return "Target Amount must be greater than zero.";
112+
if (this.goalForm.target_amount > 100000000) return "Target Amount cannot exceed ₹100,000,000.";
113+
return null;
114+
}
115+
116+
get goalStartDateError(): string | null {
117+
if (!this.goalForm.start_date) return "Start Date is required.";
118+
if (this.goalForm.start_date && this.goalForm.target_date && new Date(this.goalForm.start_date) > new Date(this.goalForm.target_date)) {
119+
return "Start Date cannot be after the Target Date.";
96120
}
121+
return null;
122+
}
123+
124+
get goalTargetDateError(): string | null {
125+
if (!this.goalForm.target_date) return "Target Date is required.";
126+
return null;
127+
}
97128

129+
get goalNoteError(): string | null {
130+
if (this.goalForm.note && this.goalForm.note.length > 100) return "Note cannot exceed 100 characters.";
98131
return null;
99132
}
100133

101134
get isGoalFormValid(): boolean {
102-
return (
103-
!!this.goalForm.goal_name &&
104-
!!this.goalForm.target_amount &&
105-
this.goalForm.target_amount > 0 &&
106-
!!this.goalForm.start_date && // Ensure start date exists
107-
!!this.goalForm.target_date && // Ensure target date exists
108-
!this.goalDateError
109-
);
135+
return !this.goalNameError && !this.goalAmountError && !this.goalStartDateError && !this.goalTargetDateError && !this.goalNoteError;
110136
}
137+
111138
/* ---------- Actions ---------- */
112139

113140
saveSavingsData() {
141+
this.savingFormSubmitted = true;
142+
114143
if (!this.isSavingFormValid) return;
115144

116145
const savingData: Saving = {
@@ -131,6 +160,8 @@ export class SavingComponent implements OnInit {
131160
}
132161

133162
saveGoalData() {
163+
this.goalFormSubmitted = true;
164+
134165
if (!this.isGoalFormValid) return;
135166

136167
const goalData: Goal = {
@@ -172,18 +203,21 @@ export class SavingComponent implements OnInit {
172203

173204
// --- Modal Control ---
174205
openAddModal() {
206+
this.savingFormSubmitted = false;
175207
this.editingSavingId = null;
176208
this.savingForm = { amount: undefined, date: this.todayDateStr, note: '' };
177209
this.showSavingModal = true;
178210
}
179211

180212
editSaving(saving: Saving) {
213+
this.savingFormSubmitted = false;
181214
this.editingSavingId = saving.saving_id;
182215
this.savingForm = { ...saving };
183216
this.showSavingModal = true;
184217
}
185218

186219
openGoalModal() {
220+
this.goalFormSubmitted = false;
187221
this.showGoalDetailsModal = false;
188222
this.goalForm = this.currentGoal ? { ...this.currentGoal } : {
189223
goal_name: '', target_amount: undefined, start_date: this.todayDateStr, target_date: '', note: ''
@@ -199,5 +233,7 @@ export class SavingComponent implements OnInit {
199233
this.showSavingModal = false;
200234
this.showGoalModal = false;
201235
this.showGoalDetailsModal = false;
236+
this.savingFormSubmitted = false;
237+
this.goalFormSubmitted = false;
202238
}
203239
}

0 commit comments

Comments
 (0)