![]()
@@ -67,14 +74,14 @@ import { BadgeAchievementDialogComponent } from '../badge-achievement-dialog/bad
{{ notification.badge.milestoneDays }}-day streak achieved!
-
- {{ getRelativeTime(notification.createdAt) }}
+
+ {{ getFormattedDate(notification.badge.dateAwarded) }}
@@ -85,11 +92,11 @@ import { BadgeAchievementDialogComponent } from '../badge-achievement-dialog/bad
}
- @if (notifications().length > 0) {
+ @if (hasNotifications()) {
}
@@ -99,7 +106,7 @@ import { BadgeAchievementDialogComponent } from '../badge-achievement-dialog/bad
`,
styleUrls: ['./notification-bell.component.scss']
})
-export class NotificationBellComponent {
+export class NotificationBellComponent implements OnInit {
@Input() isModalOpen = false;
private dialog = inject(MatDialog);
@@ -108,7 +115,9 @@ export class NotificationBellComponent {
notifications = this.badgeNotificationService.notifications;
unreadCount = this.badgeNotificationService.unreadCount;
+ hasNotifications = this.badgeNotificationService.hasNotifications;
isDropdownOpen = this.badgeNotificationService.isDropdownOpen;
+ isLoading = this.badgeNotificationService.isLoading;
constructor() {
// Close dropdown when modal opens
@@ -119,6 +128,11 @@ export class NotificationBellComponent {
});
}
+ ngOnInit(): void {
+ // Load badge notifications from backend on component init
+ this.badgeNotificationService.loadBadgeNotifications();
+ }
+
@HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent): void {
if (!this.elementRef.nativeElement.contains(event.target)) {
@@ -145,11 +159,17 @@ export class NotificationBellComponent {
return;
}
event.stopPropagation();
+ // Refresh badges when opening dropdown
+ this.badgeNotificationService.loadBadgeNotifications();
this.badgeNotificationService.toggleDropdown();
}
openBadgeDialog(notification: BadgeNotification): void {
- this.badgeNotificationService.markAsRead(notification.id);
+ // Mark this notification as read when clicked
+ if (notification.isNew) {
+ this.badgeNotificationService.markAsRead(notification.id);
+ }
+
this.badgeNotificationService.closeDropdown();
this.dialog.open(BadgeAchievementDialogComponent, {
@@ -160,16 +180,16 @@ export class NotificationBellComponent {
});
}
- markAllAsRead(): void {
- this.badgeNotificationService.markAllAsRead();
+ closeDropdown(): void {
+ this.badgeNotificationService.closeDropdown();
}
- clearAll(): void {
- this.badgeNotificationService.clearAll();
+ markAllAsRead(): void {
+ this.badgeNotificationService.markAllAsRead();
}
- closeDropdown(): void {
- this.badgeNotificationService.closeDropdown();
+ clearAllNotifications(): void {
+ this.badgeNotificationService.clearAllNotifications();
}
getBadgeImage(milestoneDays: number): string {
@@ -180,17 +200,20 @@ export class NotificationBellComponent {
return this.badgeNotificationService.getBadgeInfo(milestoneDays).badgeName;
}
- getRelativeTime(date: Date): string {
+ getFormattedDate(dateStr: string): string {
+ const date = new Date(dateStr);
const now = new Date();
- const diffMs = now.getTime() - new Date(date).getTime();
- const diffMins = Math.floor(diffMs / 60000);
- const diffHours = Math.floor(diffMs / 3600000);
- const diffDays = Math.floor(diffMs / 86400000);
-
- if (diffMins < 1) return 'Just now';
- if (diffMins < 60) return `${diffMins}m ago`;
- if (diffHours < 24) return `${diffHours}h ago`;
- if (diffDays === 1) return '1 day ago';
- return `${diffDays} days ago`;
+ const diffTime = now.getTime() - date.getTime();
+ const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
+
+ if (diffDays === 0) return 'Today';
+ if (diffDays === 1) return 'Yesterday';
+ if (diffDays < 7) return `${diffDays} days ago`;
+
+ return date.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined
+ });
}
}
diff --git a/pomodify-frontend/src/styles.scss b/pomodify-frontend/src/styles.scss
index 59d0b705..8a4f3230 100644
--- a/pomodify-frontend/src/styles.scss
+++ b/pomodify-frontend/src/styles.scss
@@ -15,6 +15,26 @@ body {
padding: 0;
}
+/* Hide browser's native password reveal icon globally */
+input[type="password"]::-ms-reveal,
+input[type="password"]::-ms-clear,
+input[type="password"]::-webkit-credentials-auto-fill-button,
+input[type="password"]::-webkit-clear-button {
+ display: none !important;
+ visibility: hidden !important;
+ pointer-events: none !important;
+}
+
+/* Also hide for text inputs that might be toggled password fields */
+.password-group input::-ms-reveal,
+.password-group input::-ms-clear,
+.password-group input::-webkit-credentials-auto-fill-button,
+.password-group input::-webkit-clear-button {
+ display: none !important;
+ visibility: hidden !important;
+ pointer-events: none !important;
+}
+
.verify-email-dialog {
.mat-mdc-dialog-surface {
border-radius: 16px !important;
@@ -2151,10 +2171,13 @@ html.theme-dark .mat-mdc-option.mdc-list-item--selected {
.mat-mdc-dialog-container {
padding: 0 !important;
}
-
- @media (max-width: 600px) {
- max-width: 75% !important;
- width: 75% !important;
+}
+
+/* Responsive dialog sizing for all screen sizes */
+@media (max-width: 600px) {
+ .cdk-overlay-pane.create-activity-dialog-panel {
+ max-width: calc(100vw - 32px) !important;
+ width: calc(100vw - 32px) !important;
margin: 0 auto !important;
.mat-mdc-dialog-surface {
@@ -2169,15 +2192,18 @@ html.theme-dark .mat-mdc-option.mdc-list-item--selected {
}
}
-/* Force dialog to have side margins on mobile */
-@media (max-width: 600px) {
+@media (max-width: 400px) {
.cdk-overlay-pane.create-activity-dialog-panel {
- max-width: 75% !important;
- margin-left: auto !important;
- margin-right: auto !important;
+ max-width: calc(100vw - 24px) !important;
+ width: calc(100vw - 24px) !important;
}
}
+/* Ensure dialog is centered and visible on all devices */
+.cdk-overlay-pane.create-activity-dialog-panel {
+ max-height: 90vh !important;
+}
+
/* ========================================================================
SESSION TIMER NOTES PANEL - FINAL OVERRIDES
Ensures proper styling for notes panel in both light and dark modes