Skip to content

Comments

feat: Implement Notification Page + Utility Notification Formatter Function#126

Merged
aviralsaxena16 merged 3 commits intoOpenLake:mainfrom
Pratyush2240:feat/notification-system
Dec 12, 2025
Merged

feat: Implement Notification Page + Utility Notification Formatter Function#126
aviralsaxena16 merged 3 commits intoOpenLake:mainfrom
Pratyush2240:feat/notification-system

Conversation

@Pratyush2240
Copy link
Contributor

@Pratyush2240 Pratyush2240 commented Dec 8, 2025


name: "📦 Pull Request"
about: Submit changes for review
title: "PR: Implement Notification Page + Utility Notification Formatter Function"
labels: "enhancement, feature"
assignees: ""

📌 Related Issue

Closes #123


✨ Changes Introduced

  • Added: Complete notification system with dedicated /notifications page
  • Added: generateNotificationMessage() utility function in /src/utils/notifications.ts
  • Added: NotificationBell component with unread count badge in header
  • Added: API endpoints for notifications CRUD operations (GET, POST, mark as read, mark all as read)
  • Added: Notification service layer with helper functions for easy integration
  • Added: Support for 12+ notification types (follow, contest, quiz, achievement, etc.)
  • Added: Comprehensive documentation (4 markdown files) and integration examples
  • Added: Test page at /test-notifications for manual testing
  • Updated: Header component to include notification bell icon
  • Updated: Routes constants to include /notifications route

🤔 Why This Change?

  • Problem: Users had no way to receive or view notifications about important events (follows, problem solving, contests, achievements, etc.). The app lacked a centralized notification system.
  • Solution: Implemented a full-stack notification system with:
    • Dedicated notifications page with unread indicators
    • Reusable utility function for formatting notification messages
    • Bell icon in header showing unread count
    • Complete API layer for managing notifications
    • Service layer for easy integration into existing features
  • Impact:
    • Users can now stay informed about important events in the app
    • Developers can easily trigger notifications from any feature
    • Improved user engagement and retention
    • Better user experience with real-time feedback

🧪 Testing

  • Ran unit tests (npm test) - No new test failures
  • Ran integration tests (npm run test:integration) - N/A
  • Tested manually (describe below):
    • Test case 1: Create notification via test page
      • Steps: Login → Visit /test-notifications → Click "Follow Notification" button
      • Expected: Notification created, bell badge shows count, notification appears on /notifications page ✅
    • Test case 2: Mark notification as read
      • Steps: Visit /notifications → Click on unread notification
      • Expected: Notification background changes from blue to white, badge count decreases, navigates to linked page ✅
    • Test case 3: Mark all as read
      • Steps: Visit /notifications with multiple unread → Click "Mark all as read"
      • Expected: All notifications marked as read, badge disappears ✅
    • Test case 4: Bell icon auto-refresh
      • Steps: Create notification in another tab, wait 30 seconds
      • Expected: Bell badge updates automatically ✅
    • Test case 5: API endpoints
      • Steps: Test all API endpoints via test page and browser network tab
      • Expected: All endpoints return correct responses ✅
  • Tested on different browsers/devices (if UI change)
    • Chrome ✅
    • Firefox ✅
    • Edge ✅
    • Mobile responsive ✅
  • Verified no console warnings/errors

📝 Documentation Updates

  • Updated README/docs
    • Added NOTIFICATION_SYSTEM.md - Complete API documentation
    • Added IMPLEMENTATION_SUMMARY.md - Feature overview
    • Added NOTIFICATION_ARCHITECTURE.md - System architecture
    • Added SETUP_CHECKLIST.md - Post-merge setup guide
    • Added firestore-notifications-rules.txt - Firestore security rules
  • Added code comments - All functions have JSDoc comments
  • Updated API documentation (if applicable) - Complete API docs in NOTIFICATION_SYSTEM.md
  • Added JSDoc comments for new functions - All utility and service functions documented

✅ Checklist

  • Created a new branch for PR (feat/notification-system)
  • Have starred the repository ⭐
  • Follows JavaScript Styleguide
  • No console warnings/errors
  • Commit messages follow Git Guidelines
  • Self-reviewed my code
  • Added tests for new functionality (to be added in future PR)
  • All tests pass locally
  • Code follows project conventions
  • No breaking changes (or clearly documented if intentional)

👥 Reviewers

  • Requested review from maintainers
  • Tag specific reviewers if needed: @username

🚀 Deployment Notes

  • Requires environment variables update - No
  • Requires database migration - Yes (Firestore indexes will auto-create)
  • Requires third-party service configuration - Yes (Firestore security rules need to be added)
  • Breaking changes that need coordination - No

Post-Merge Setup Required:

  1. Firestore Security Rules (Required):

    • Add rules from firestore-notifications-rules.txt to Firebase Console
    • Location: Firebase Console → Firestore Database → Rules
    • Copy the notification rules and publish
  2. Firestore Indexes (Auto-created):

    • Indexes will be created automatically on first use
    • Or create manually: userId + createdAt and userId + read + createdAt
    • Wait 1-2 minutes for indexes to build
  3. Integration (Optional):

    • Integrate into follow/unfollow feature
    • Integrate into problem submission handler
    • Integrate into quiz completion
    • See src/examples/notification-integration-examples.ts for code examples
  4. Testing:

    • Visit /test-notifications to create test notifications
    • Verify bell icon shows unread count
    • Test mark as read functionality

Detailed instructions in SETUP_CHECKLIST.md


💬 Community & Support

Need help or want to discuss this PR?
Join our Discord community 👉 CanonForces Discord


💡 Additional Notes (If any)

Files Created (18 new files):

Core System:

  • src/types/notification.ts
  • src/utils/notifications.ts ⭐
  • src/services/notifications.ts

API Endpoints:

  • src/pages/api/v1/notifications.ts
  • src/pages/api/v1/notifications/read/[id].ts
  • src/pages/api/v1/notifications/read-all.ts

UI Components:

  • src/common/components/Notifications/NotificationItem.tsx
  • src/common/components/Notifications/Notifications.module.css
  • src/common/components/NotificationBell/NotificationBell.tsx
  • src/common/components/NotificationBell/NotificationBell.module.css

Pages:

  • src/pages/notifications.tsx ⭐
  • src/pages/test-notifications.tsx

Documentation:

  • NOTIFICATION_SYSTEM.md
  • IMPLEMENTATION_SUMMARY.md
  • NOTIFICATION_ARCHITECTURE.md
  • SETUP_CHECKLIST.md
  • firestore-notifications-rules.txt
  • src/examples/notification-integration-examples.ts

Modified Files:

  • src/constants/routes.tsx - Added NOTIFICATIONS route
  • src/common/components/Header/Header.tsx - Added bell icon

Supported Notification Types:

  • follow / unfollow - User follow notifications
  • problem_solved - Problem completion
  • quiz_completed - Quiz completion
  • contest_reminder / contest_started / contest_ended - Contest updates
  • achievement - Achievement unlocked
  • new_potd - New Problem of the Day
  • friend_solved - Friend activity
  • leaderboard_rank - Rank updates
  • system - General system notifications

Quick Integration Example:

import { notifyUserFollowed } from '../services/notifications';

// When someone follows a user
await notifyUserFollowed(targetUserId, followerUsername, followerUserId);

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Full notification subsystem: header bell with unread badge, notifications page, create/list/mark-read flows, test/demo UI, multiple notification types, polling and optimistic updates.

* **Documentation**
  * Complete docs, architecture, setup checklist, integration examples, and security/config guidance.

* **Bug Fixes**
  * Reliability and auth/read permission improvements for fetching and marking notifications; Firestore rules and deployment notes added.

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

…icated page

- Add notification type definitions and interfaces
- Implement generateNotificationMessage() utility function for formatting
- Create API endpoints for CRUD operations (GET, POST, mark as read)
- Build dedicated /notifications page with unread indicators
- Add NotificationBell component with unread count badge in header
- Implement auto-refresh polling every 30 seconds
- Add notification service layer with helper functions
- Support 12+ notification types (follow, contest, quiz, achievement, etc.)
- Include smooth animations and modern UI design
- Add comprehensive documentation and integration examples

Closes #<123>
@vercel
Copy link

vercel bot commented Dec 8, 2025

@Pratyush2240 is attempting to deploy a commit to the aviralsaxena16's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions
Copy link

github-actions bot commented Dec 8, 2025

🎉 Thanks for Your Contribution to CanonForces! ☺️

We'll review it as soon as possible. In the meantime, please:

  • ✅ Double-check the file changes.
  • ✅ Ensure that all commits are clean and meaningful.
  • ✅ Link the PR to its related issue (e.g., Closes #123).
  • ✅ Resolve any unaddressed review comments promptly.

💬 Need help or want faster feedback?
Join our Discord 👉 CanonForces Discord

Thanks again for contributing 🙌 – @Pratyush2240!
cc: @aviralsaxena16

@coderabbitai
Copy link

coderabbitai bot commented Dec 8, 2025

Walkthrough

Adds a full notification subsystem: typed models, message/link utilities, service APIs, Next.js API routes, Firestore rules/index guidance, React UI components (bell, item), pages for viewing/testing notifications, integration examples, and supporting docs and setup guides.

Changes

Cohort / File(s) Summary
Types & Utilities
src/types/notification.ts, src/utils/notifications.ts
New Notification, NotificationPayload, NotificationType; generateNotificationMessage() and getNotificationLink() to produce messages and navigation links.
Service Layer
src/services/notifications.ts
Client-side service: createNotification, getNotifications, markNotificationAsRead, markAllNotificationsAsRead plus typed helpers (e.g., notifyUserFollowed, notifyProblemSolved, notifyQuizCompleted, notifyContestReminder, notifyAchievement).
API Routes
src/pages/api/v1/notifications.ts, src/pages/api/v1/notifications/read/[id].ts, src/pages/api/v1/notifications/read-all.ts
GET list and POST create endpoints; POST endpoints to mark one or all notifications read; Firestore-backed, includes validation and error handling.
UI Components & Styles
src/common/components/NotificationBell/NotificationBell.tsx, .../NotificationBell.module.css, src/common/components/Notifications/NotificationItem.tsx, .../Notifications.module.css
Auth-aware bell with polling and unread badge; NotificationItem renders icon, message, time-ago, unread state, optional link; CSS modules for styling and animations.
Pages
src/pages/notifications.tsx, src/pages/test-notifications.tsx
/notifications authenticated page with fetch, mark-as-read (single/all), optimistic updates; test page to create/demo notifications and custom system messages.
Header & Routes
src/common/components/Header/Header.tsx, src/constants/routes.tsx
Adds NOTIFICATIONS route constant and integrates NotificationBell into the header.
Examples & Integration
src/examples/notification-integration-examples.ts
Example handlers demonstrating integration points (follow, problem solved, quiz completion, contest reminders, achievements, custom notifications).
Docs, Rules & Setup
NOTIFICATION_SYSTEM.md, NOTIFICATION_ARCHITECTURE.md, SETUP_CHECKLIST.md, IMPLEMENTATION_SUMMARY.md, CODERABBIT_FIXES.md, firestore-notifications-rules.txt
Architecture, usage, setup checklist, Firestore security rules and index guidance, troubleshooting, and implementation notes.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as NotificationBell / NotificationsPage
    participant API as /api/v1/notifications
    participant DB as Firestore
    participant Auth as Firebase Auth

    Note over User,DB: Producer flow (create notification)
    User->>UI: perform action (follow/solve/quiz)
    UI->>API: POST /api/v1/notifications (userId, type, metadata)
    API->>DB: create notification doc (userId,type,message,read:false,createdAt)
    DB-->>API: doc created (id)
    API-->>UI: 201 Created

    Note over UI,DB: Consumer flow (fetch & badge)
    UI->>Auth: subscribe auth state
    Auth-->>UI: userId
    UI->>API: GET /api/v1/notifications?userId=XXX (polled or on load)
    API->>DB: query notifications by userId ordered by createdAt
    DB-->>API: list of notifications
    API-->>UI: notifications payload
    UI->>UI: render list and compute unread badge

    Note over User,API: Mark as read
    User->>UI: click notification or "Mark all as read"
    UI->>API: POST /api/v1/notifications/read/{id} or /read-all (userId in body)
    API->>DB: update notification(s) set read:true
    DB-->>API: update confirmed
    API-->>UI: 200 OK
    UI->>UI: update local state, refresh badge
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to focus during review:
    • src/pages/api/v1/notifications.ts — input validation, message/link generation use, and production auth restrictions/comments.
    • src/common/components/NotificationBell/NotificationBell.tsx — auth subscription logic, polling setup/cleanup, and useEffect dependencies.
    • Firestore rules/index guidance in firestore-notifications-rules.txt — correctness vs queries and security semantics.
    • Optimistic updates and error handling in src/pages/notifications.tsx.
    • Type consistency between src/types/notification.ts, services, API responses, and UI components.

"A bell I code with nimble paws,
Messages hop through digital moss.
Unread lights blink, then fade away,
I twitch my whiskers — notifications play.
Hop on, celebrate — a rabbit's tiny toss!" 🐇🔔

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.38% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main changes: implementing a notification page and utility formatter function, which are the primary objectives of the PR.
Description check ✅ Passed Description is comprehensive, following the template structure with clear sections (Related Issue, Changes, Why, Testing, Documentation, Checklists, Deployment Notes). All major sections are addressed with detailed information.
Linked Issues check ✅ Passed PR meets all objectives from issue #123: notification page implemented, unread status distinction added, mark-as-read functionality present, generateNotificationMessage utility created, and API endpoints (GET, POST, read, read-all) are fully implemented.
Out of Scope Changes check ✅ Passed All changes are within scope. PR implements the notification system and utility function as required by #123. Additional features like NotificationBell, test page, documentation, and service layer are extensions that directly support the main objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (15)
src/common/components/NotificationBell/NotificationBell.module.css (1)

37-66: Consider respecting prefers-reduced-motion for the pulsing badge

The infinite pulse animation on .badge is fine visually but can be wrapped in a prefers-reduced-motion media query to be more accessible.

Example:

 .badge {
   position: absolute;
   top: 0;
   right: 0;
   background: #ef4444;
   color: white;
   font-size: 0.65rem;
   font-weight: 600;
   padding: 0.15rem 0.35rem;
   border-radius: 10px;
   min-width: 18px;
   height: 18px;
   display: flex;
   align-items: center;
   justify-content: center;
   border: 2px solid white;
-  animation: pulse 2s ease-in-out infinite;
 }

+@media (prefers-reduced-motion: no-preference) {
+  .badge {
+    animation: pulse 2s ease-in-out infinite;
+  }
+}
NOTIFICATION_ARCHITECTURE.md (1)

5-226: Add languages to fenced code blocks for better tooling support

Several code fences (diagrams, flows, file tree) don’t specify a language, which triggers MD040 and reduces readability in some editors.

For example:

-```
+```text
┌─────────────────────────────────────────────────────────────────────┐
│                         USER ACTIONS                                 │
...

and for the file tree / command examples, use appropriate languages like text, bash, or typescript.

src/common/components/Notifications/NotificationItem.tsx (2)

23-34: Consider memoizing time-ago calculation for performance.

The getTimeAgo function recalculates on every render. For notification lists with many items, this could impact performance.

If performance becomes a concern, consider using useMemo:

+const timeAgo = React.useMemo(() => {
     const now = Date.now();
     const diff = now - notification.createdAt;
     const minutes = Math.floor(diff / 60000);
     const hours = Math.floor(diff / 3600000);
     const days = Math.floor(diff / 86400000);

     if (minutes < 1) return "Just now";
     if (minutes < 60) return `${minutes}m ago`;
     if (hours < 24) return `${hours}h ago`;
     return `${days}d ago`;
+}, [notification.createdAt]);

Then use {timeAgo} instead of {getTimeAgo(notification.createdAt)} on Line 74.


36-60: Consider moving getNotificationIcon outside the component.

This function doesn't depend on props or state, so it's recreated on every render unnecessarily.

Move it outside the component:

+const getNotificationIcon = (type: string) => {
+    switch (type) {
+        case "follow":
+        case "unfollow":
+            return "👤";
+        // ... rest of cases
+        default:
+            return "🔔";
+    }
+};
+
 export default function NotificationItem({
     notification,
     onMarkAsRead,
 }: NotificationItemProps) {
-    const getNotificationIcon = (type: string) => {
-        switch (type) {
-            // ...
-        }
-    };
src/common/components/NotificationBell/NotificationBell.tsx (1)

37-51: Consider server-side filtering for unread count.

The current implementation fetches all notifications (up to 50) and filters client-side. This sends unnecessary data over the network.

Add a query parameter to the API to return only unread count:

Option 1: Add count-only endpoint:

// New API route: /api/v1/notifications/unread-count
const response = await fetch(`/api/v1/notifications/unread-count?userId=${userId}`);
const data = await response.json();
setUnreadCount(data.count);

Option 2: Add query parameter to existing endpoint:

const response = await fetch(`/api/v1/notifications?userId=${userId}&unreadOnly=true&countOnly=true`);

This would reduce bandwidth, especially for users with many notifications.

src/pages/api/v1/notifications.ts (1)

72-80: Use Firestore serverTimestamp() for consistency.

Line 79 uses Date.now(), which relies on the client/server system clock and can be inconsistent or manipulated.

Use Firestore's serverTimestamp() for reliable, server-side timestamps:

+import { serverTimestamp } from "firebase/firestore";

 const notificationData = {
     userId,
     type,
     message,
     metadata: metadata || {},
     link,
     read: false,
-    createdAt: Date.now(),
+    createdAt: serverTimestamp(),
 };

Note: If you need to return the timestamp immediately, you can use Date.now() in the response while storing serverTimestamp():

+const now = Date.now();
 const notificationData = {
     userId,
     type,
     message,
     metadata: metadata || {},
     link,
     read: false,
-    createdAt: Date.now(),
+    createdAt: serverTimestamp(),
 };

 const docRef = await addDoc(collection(db, "notifications"), notificationData);

 return res.status(201).json({
     success: true,
-    notification: { id: docRef.id, ...notificationData },
+    notification: { id: docRef.id, ...notificationData, createdAt: now },
 });
src/utils/notifications.ts (1)

21-64: Consider stricter typing with NotificationType.

The type parameter is string, but could be NotificationType for better type safety. However, the function also needs to handle unknown types (line 61-62 default case).

You could create a type guard and use a discriminated union for better type safety:

export function generateNotificationMessage(notification: NotificationPayload): string {
    const { type, metadata = {} } = notification;

    // Type guard for known types
    const knownTypes: NotificationType[] = ["follow", "contest_reminder", /* ... */];
    if (!knownTypes.includes(type as NotificationType)) {
        return metadata.message || "You have a new notification";
    }

    // Now TypeScript knows type is NotificationType
    switch (type as NotificationType) {
        case "follow":
            return `${metadata.username || "Someone"} started following you`;
        // ... rest of cases
    }
}

Alternatively, keep the current approach for flexibility if notification types can be extended dynamically.

src/pages/test-notifications.tsx (1)

32-51: Handle notification creation result more gracefully.

The loading state is only shown while the request is in flight. Consider showing immediate feedback when the create button is clicked.

The current implementation is acceptable, but you could improve UX by disabling all buttons while any request is in flight:

 <button
     onClick={() => handleCreateNotification(test.type, test.metadata)}
     disabled={loading}
     className="..."
 >
     {loading ? "Creating..." : "Create Notification"}
 </button>

This already disables all buttons when loading is true, which is good.

src/pages/notifications.tsx (3)

37-56: Avoid duplicating notifications fetch logic already provided by the service

fetchNotifications is manually calling /api/v1/notifications and shaping the response, while src/services/notifications.ts already exposes getNotifications(userId) with similar behavior. To keep the API contract and error‑handling in one place, consider delegating to that helper here and only managing loading/error state in this component.

For example:

import { getNotifications } from "../services/notifications";

const fetchNotifications = async () => {
    try {
        setLoading(true);
        setError(null);

        const list = await getNotifications(userId!);
        setNotifications(list);
    } catch (err) {
        console.error("Error fetching notifications:", err);
        setError("Failed to load notifications");
    } finally {
        setLoading(false);
    }
};

58-96: Reuse mark‑as‑read helpers from the notifications service for consistency

handleMarkAsRead and handleMarkAllAsRead re‑implement the same API calls that markNotificationAsRead and markAllNotificationsAsRead already wrap in src/services/notifications.ts. Centralizing these calls there reduces duplication and keeps paths/HTTP details in one place.

Within this component you could keep the optimistic UI update but delegate the network work:

-    const handleMarkAsRead = async (notificationId: string) => {
-        try {
-            const response = await fetch(`/api/v1/notifications/read/${notificationId}`, {
-                method: "POST",
-            });
-
-            if (response.ok) {
-                setNotifications((prev) =>
-                    prev.map((notif) =>
-                        notif.id === notificationId ? { ...notif, read: true } : notif
-                    )
-                );
-            }
-        } catch (err) {
-            console.error("Error marking notification as read:", err);
-        }
-    };
+    const handleMarkAsRead = async (notificationId: string) => {
+        const ok = await markNotificationAsRead(notificationId);
+        if (ok) {
+            setNotifications((prev) =>
+                prev.map((notif) =>
+                    notif.id === notificationId ? { ...notif, read: true } : notif
+                )
+            );
+        }
+    };
@@
-    const handleMarkAllAsRead = async () => {
-        if (!userId) return;
-
-        try {
-            const response = await fetch("/api/v1/notifications/read-all", {
-                method: "POST",
-                headers: {
-                    "Content-Type": "application/json",
-                },
-                body: JSON.stringify({ userId }),
-            });
-
-            if (response.ok) {
-                setNotifications((prev) =>
-                    prev.map((notif) => ({ ...notif, read: true }))
-                );
-            }
-        } catch (err) {
-            console.error("Error marking all notifications as read:", err);
-        }
-    };
+    const handleMarkAllAsRead = async () => {
+        if (!userId) return;
+
+        const ok = await markAllNotificationsAsRead(userId);
+        if (ok) {
+            setNotifications((prev) =>
+                prev.map((notif) => ({ ...notif, read: true }))
+            );
+        }
+    };

And add the corresponding import at the top:

import {
    markNotificationAsRead,
    markAllNotificationsAsRead,
} from "../services/notifications";

134-169: Optional: add ARIA roles/live regions for loading and error states

The loading/error/empty states are visually clear; for screen‑reader users you can make them more accessible by adding roles/ARIA hints, e.g.:

  • role="status" and aria-live="polite" on the loading container.
  • role="alert" on the error container.

This is purely a UX/A11y enhancement; behavior is otherwise solid.

src/examples/notification-integration-examples.ts (2)

41-52: Align example helper stubs with real Firebase service shapes to avoid confusion

The commented friend_solved example and checkAndNotifyAchievements use a local getUserByUserId helper that returns a single user, whereas the real getUserByUserId in src/services/firebase.ts returns an array of UserProfile objects. That mismatch (and reusing the same function name) can be confusing when someone wires these examples into production code.

Consider either:

  • Importing and demonstrating use of the real helpers (e.g., const [user] = await getUserByUserId(userId);), or
  • Renaming these local stubs (e.g., exampleGetUserByUserId) and clarifying in comments that real implementations should come from src/services/firebase.ts.

Same applies for getAllUsers/getAllActiveUsers: pointing readers to the actual service implementations will help keep examples and production behavior in sync.

Also applies to: 105-129, 177-200


81-98: Be mindful of fan‑out and rate limits in reminder/POTD examples

sendContestReminders and notifyNewPOTD currently loop over all contests × all users or all active users, sending one notification per combination. That’s fine for illustrative code, but in production you’ll likely want batching, filtering (e.g., per‑user preferences), and some throttling/backoff to avoid hitting Firestore/API write limits.

When you implement these for real, consider:

  • Chunking users and using batched writes where possible.
  • Respecting user notification settings.
  • Adding logging/metrics around how many notifications are sent per run.

Also applies to: 161-171

src/services/notifications.ts (2)

12-40: Tighten type/metadata typing using NotificationPayload (or NotificationType)

createNotification currently accepts type: string and metadata?: Record<string, any>, even though you already have NotificationPayload (and likely a NotificationType union) defined. You can get better compile‑time safety by tying these parameters to your existing payload type:

-export async function createNotification(
-    userId: string,
-    type: string,
-    metadata?: Record<string, any>
-): Promise<Notification | null> {
+export async function createNotification(
+    userId: string,
+    type: NotificationPayload["type"],
+    metadata?: NotificationPayload["metadata"]
+): Promise<Notification | null> {

(Adjust the property names if NotificationPayload is shaped differently.) This prevents callers from passing unsupported notification types or malformed metadata.


68-101: Optional: log non‑OK HTTP responses in mark‑as‑read helpers

markNotificationAsRead and markAllNotificationsAsRead return response.ok but only log when an exception is thrown (e.g., network failure). If the server responds with a 4xx/5xx, you silently get false with no context.

You could add lightweight logging for non‑OK responses, e.g.:

export async function markNotificationAsRead(notificationId: string): Promise<boolean> {
    try {
        const response = await fetch(`/api/v1/notifications/read/${notificationId}`, {
            method: "POST",
        });
-
-        return response.ok;
+        if (!response.ok) {
+            console.error("Failed to mark notification as read", {
+                status: response.status,
+            });
+            return false;
+        }
+        return true;
    } catch (error) {
        console.error("Error marking notification as read:", error);
        return false;
    }
}

and similarly for markAllNotificationsAsRead.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3e7885 and 5ae69e1.

📒 Files selected for processing (20)
  • IMPLEMENTATION_SUMMARY.md (1 hunks)
  • NOTIFICATION_ARCHITECTURE.md (1 hunks)
  • NOTIFICATION_SYSTEM.md (1 hunks)
  • SETUP_CHECKLIST.md (1 hunks)
  • firestore-notifications-rules.txt (1 hunks)
  • src/common/components/Header/Header.tsx (1 hunks)
  • src/common/components/NotificationBell/NotificationBell.module.css (1 hunks)
  • src/common/components/NotificationBell/NotificationBell.tsx (1 hunks)
  • src/common/components/Notifications/NotificationItem.tsx (1 hunks)
  • src/common/components/Notifications/Notifications.module.css (1 hunks)
  • src/constants/routes.tsx (1 hunks)
  • src/examples/notification-integration-examples.ts (1 hunks)
  • src/pages/api/v1/notifications.ts (1 hunks)
  • src/pages/api/v1/notifications/read-all.ts (1 hunks)
  • src/pages/api/v1/notifications/read/[id].ts (1 hunks)
  • src/pages/notifications.tsx (1 hunks)
  • src/pages/test-notifications.tsx (1 hunks)
  • src/services/notifications.ts (1 hunks)
  • src/types/notification.ts (1 hunks)
  • src/utils/notifications.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
src/common/components/Notifications/NotificationItem.tsx (1)
src/types/notification.ts (1)
  • Notification (8-17)
src/common/components/NotificationBell/NotificationBell.tsx (1)
src/lib/firebase.ts (1)
  • auth (21-21)
src/utils/notifications.ts (1)
src/types/notification.ts (1)
  • NotificationPayload (3-6)
src/pages/test-notifications.tsx (2)
src/lib/firebase.ts (1)
  • auth (21-21)
src/services/notifications.ts (1)
  • createNotification (12-40)
src/pages/api/v1/notifications/read/[id].ts (3)
src/pages/api/v1/notifications.ts (1)
  • handler (27-95)
src/pages/api/v1/notifications/read-all.ts (1)
  • handler (17-61)
src/lib/firebase.ts (1)
  • db (21-21)
src/pages/api/v1/notifications/read-all.ts (3)
src/pages/api/v1/notifications.ts (1)
  • handler (27-95)
src/pages/api/v1/notifications/read/[id].ts (1)
  • handler (11-36)
src/lib/firebase.ts (1)
  • db (21-21)
src/pages/api/v1/notifications.ts (5)
src/pages/api/v1/notifications/read-all.ts (1)
  • handler (17-61)
src/pages/api/v1/notifications/read/[id].ts (1)
  • handler (11-36)
src/lib/firebase.ts (1)
  • db (21-21)
src/types/notification.ts (1)
  • Notification (8-17)
src/utils/notifications.ts (2)
  • generateNotificationMessage (21-64)
  • getNotificationLink (72-101)
src/pages/notifications.tsx (3)
src/types/notification.ts (1)
  • Notification (8-17)
src/lib/firebase.ts (1)
  • auth (21-21)
src/common/components/Notifications/NotificationItem.tsx (1)
  • NotificationItem (13-86)
src/services/notifications.ts (1)
src/types/notification.ts (1)
  • Notification (8-17)
src/examples/notification-integration-examples.ts (2)
src/services/notifications.ts (6)
  • notifyUserFollowed (105-114)
  • notifyProblemSolved (129-138)
  • notifyQuizCompleted (140-153)
  • notifyAchievement (155-162)
  • notifyContestReminder (116-127)
  • createNotification (12-40)
src/services/firebase.ts (2)
  • getAllUsers (129-136)
  • getUserByUserId (67-78)
src/common/components/Header/Header.tsx (1)
src/common/components/NotificationBell/NotificationBell.tsx (1)
  • NotificationBell (9-80)
🪛 LanguageTool
firestore-notifications-rules.txt

[style] ~43-~43: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ... update their own notifications * - Only the 'read' field can be modified * ...

(ADVERB_REPETITION_PREMIUM)

NOTIFICATION_ARCHITECTURE.md

[style] ~248-~248: Using many exclamation marks might seem excessive (in this case: 3 exclamation marks for a text that’s 1309 characters long)
Context: ...ard_rank | 📊 | "You've reached rank #42!" | --- Last Updated: December 9, ...

(EN_EXCESSIVE_EXCLAMATION)

SETUP_CHECKLIST.md

[grammar] ~64-~64: Ensure spelling is correct
Context: ...unning at http://localhost:3000 2. ⚠️ Login to your account 3. ⚠️ Visit `http://loc...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~276-~276: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ...ts. --- Last Updated: December 9, 2025 Status: ✅ Implementation Complete...

(MISSING_COMMA_AFTER_YEAR)

🪛 markdownlint-cli2 (0.18.1)
NOTIFICATION_ARCHITECTURE.md

5-5: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


77-77: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


131-131: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


181-181: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (14)
SETUP_CHECKLIST.md (1)

1-276: Checklist is clear and actionable

The setup checklist aligns well with the implemented notification system (rules, indexes, testing, integration points) and should make onboarding straightforward. Aside from the allow create tightening already noted in the rules file, this doc looks good.

src/constants/routes.tsx (1)

10-13: Notifications route constant is consistent and minimal

NOTIFICATIONS = "/notifications" fits the existing route naming convention and matches the new notifications page.

src/common/components/Header/Header.tsx (1)

4-29: Header integration of NotificationBell looks clean

The bell is added in a dedicated right-side actions column alongside Signup/Login, and the layout remains simple and readable. Given NotificationBell renders null for unauthenticated users, this should degrade gracefully.

src/common/components/Notifications/Notifications.module.css (1)

3-108: Notification list styling covers key states and looks robust

The CSS cleanly handles unread state, hover feedback, icons, message/time text, and empty/loading/error views. It should support the NotificationItem and notifications page well.

src/pages/api/v1/notifications/read/[id].ts (1)

11-35: Clarify auth model and enforce ownership in read/:id API

This handler marks a notification as read based solely on the id from the URL:

const notificationRef = doc(db, "notifications", id);
await updateDoc(notificationRef, { read: true });

Without tying the request to the authenticated user and checking that the document's userId matches, this endpoint can be abused if db has elevated privileges (e.g., Admin/service credentials): any caller who can guess/obtain an ID can mark someone else's notifications as read.

Consider:

  • If db is an Admin/privileged instance:
    • Extract the current user from your auth/session layer (e.g., NextAuth, Firebase ID token in headers).
    • Read the doc and verify doc.data().userId === currentUserId before updateDoc, returning 403 otherwise.
  • If db is a client SDK relying on Firestore rules:
    • Confirm that this API route actually runs with the caller's Firebase Auth context; otherwise, security rules will see request.auth == null and deny access.
    • Alternatively, move all Firestore access for this route to the Admin SDK and enforce ownership in the handler.

Also consider returning 404 (or a specific error) when the document doesn't exist rather than a generic 500, once you add a preceding getDoc check.

firestore-notifications-rules.txt (1)

11-27: Tighten allow create rule to deny client-side creation in production

The current rule allow create: if request.auth != null; allows any authenticated user to write arbitrary notifications without validating ownership. This creates a security gap if clients communicate directly with Firestore.

For production, deny client-side creation and route all writes through a trusted server (Admin SDK or Cloud Functions):

-  allow create: if request.auth != null;
+  allow create: if false;

If development environments require client writes, maintain a clearly-marked separate ruleset for local testing only.

src/pages/api/v1/notifications/read-all.ts (1)

32-37: Verify Firestore composite index exists.

This query uses a compound filter (userId == X AND read == false) that requires a composite index in Firestore.

Confirm that the Firestore composite index for the notifications collection has been created with fields userId and read. The index should be defined in your Firestore configuration or will be auto-created on first query (which may cause initial failures).

Check your Firestore console under Indexes, or add to firestore.indexes.json:

{
  "indexes": [
    {
      "collectionGroup": "notifications",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "userId", "order": "ASCENDING" },
        { "fieldPath": "read", "order": "ASCENDING" }
      ]
    }
  ]
}
NOTIFICATION_SYSTEM.md (1)

1-313: Comprehensive documentation for the notification system.

The documentation clearly covers all aspects of the notification system including types, utilities, API endpoints, integration examples, and troubleshooting. The examples are consistent with the implementation.

src/pages/api/v1/notifications.ts (1)

39-45: Verify Firestore composite index for query.

This query combines where and orderBy which requires a composite index in Firestore.

Ensure a composite index exists for the notifications collection with fields userId (==) and createdAt (desc). Add to firestore.indexes.json:

{
  "indexes": [
    {
      "collectionGroup": "notifications",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "userId", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    }
  ]
}
src/utils/notifications.ts (1)

21-101: Functions look good with comprehensive type coverage.

Both generateNotificationMessage and getNotificationLink handle all notification types with sensible fallbacks for missing metadata. The default cases ensure the functions never return undefined.

src/pages/notifications.tsx (1)

12-29: Auth gating and state wiring look good

Using onAuthStateChanged with cleanup plus redirecting unauthenticated users to /login is correct and idiomatic for a protected page; no issues here.

src/examples/notification-integration-examples.ts (1)

18-53: Example handlers are clear and map well to the notification helpers

The follow, problem‑submission, and quiz‑completion examples read cleanly, and the conditions around verdict/score are sensible. They show good patterns for calling notifyUserFollowed, notifyProblemSolved, and notifyQuizCompleted without leaking API details.

src/services/notifications.ts (1)

103-162: Helper wrappers for specific notification types look clean and DRY

The notifyUserFollowed, notifyContestReminder, notifyProblemSolved, notifyQuizCompleted, and notifyAchievement wrappers are straightforward and keep type‑specific details close to their creation sites. No issues here.

IMPLEMENTATION_SUMMARY.md (1)

1-202: Documentation is thorough and matches the implemented surface area

The summary does a good job enumerating types, utilities, services, endpoints, UI components, and example usage. It should give new contributors a clear mental model of how the notification system is wired end‑to‑end.

Comment on lines +203 to +218
## 📊 Database Schema

Notifications are stored in Firestore under the `notifications` collection:

```typescript
{
id: string; // Auto-generated
userId: string; // Recipient
type: string; // Notification type
message: string; // Formatted message
metadata: object; // Additional data
link: string; // Navigation link
read: boolean; // Read status
createdAt: number; // Timestamp
}
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify optional metadata/link fields in schema to match the Notification type

In the Firestore schema snippet, metadata and link are shown as required:

  metadata: object;        // Additional data
  link: string;            // Navigation link

But src/types/notification.ts defines them as optional (metadata?, link?). To avoid confusion for integrators, consider updating the snippet to mirror the type:

-  metadata: object;        // Additional data
-  link: string;            // Navigation link
+  metadata?: object;       // Additional data (optional)
+  link?: string;           // Navigation link (optional)

This keeps docs and runtime types aligned.

🤖 Prompt for AI Agents
In IMPLEMENTATION_SUMMARY.md around lines 203 to 218, the Firestore schema shows
metadata and link as required fields but src/types/notification.ts defines them
as optional; update the schema snippet so metadata and link are marked optional
(or annotated as "optional"/nullable) to match the Notification type, ensuring
the documentation mirrors the runtime types and avoids confusion for
integrators.

Comment on lines +31 to +37
if (req.method === "GET") {
try {
const { userId } = req.query;

if (!userId || typeof userId !== "string") {
return res.status(400).json({ error: "userId is required" });
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add authentication to protect user data.

The GET endpoint allows fetching notifications for any userId without authentication, exposing potentially private notification data.

Apply authentication similar to the read-all endpoint:

     if (req.method === "GET") {
         try {
+            // Verify authentication
+            const session = await getServerSession(req, res, authOptions);
+            if (!session?.user) {
+                return res.status(401).json({ error: "Unauthorized" });
+            }
+
             const { userId } = req.query;

             if (!userId || typeof userId !== "string") {
                 return res.status(400).json({ error: "userId is required" });
             }
+
+            // Ensure user can only fetch their own notifications
+            if (userId !== session.user.id) {
+                return res.status(403).json({ error: "Forbidden" });
+            }
🤖 Prompt for AI Agents
In src/pages/api/v1/notifications.ts around lines 31 to 37, the GET branch
currently accepts any userId query param without auth; update it to require and
validate an authenticated session like the read-all endpoint: fetch the
session/user from the request (e.g., via your auth helper), return 401 if
missing, then ensure the session user id matches the requested userId (or
session has admin scope) and return 403 if it doesn’t; only after those checks
proceed to read notifications and return 400/401/403 as appropriate.

Comment on lines +60 to +66
if (req.method === "POST") {
try {
const { userId, type, metadata } = req.body;

if (!userId || !type) {
return res.status(400).json({ error: "userId and type are required" });
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add authentication to POST endpoint.

Any client can create notifications for any user without authentication.

Add authentication check:

     if (req.method === "POST") {
         try {
+            // Verify authentication - system/admin only
+            const session = await getServerSession(req, res, authOptions);
+            if (!session?.user?.isAdmin) {
+                return res.status(401).json({ error: "Unauthorized" });
+            }
+
             const { userId, type, metadata } = req.body;

             if (!userId || !type) {
                 return res.status(400).json({ error: "userId and type are required" });
             }

Note: The POST endpoint might need system/admin-level auth, or should be called from server-side code only, not directly from client.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +25 to +30
try {
const { userId } = req.body;

if (!userId) {
return res.status(400).json({ error: "userId is required" });
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add authentication and type validation.

The endpoint accepts userId from the request body without authentication, allowing any client to mark any user's notifications as read. Additionally, userId isn't validated as a string.

Apply this diff:

+    // Verify authentication
+    const session = await getServerSession(req, res, authOptions);
+    if (!session?.user) {
+        return res.status(401).json({ error: "Unauthorized" });
+    }
+
     const { userId } = req.body;

-    if (!userId) {
+    if (!userId || typeof userId !== "string") {
         return res.status(400).json({ error: "userId is required" });
     }
+
+    // Ensure user can only modify their own notifications
+    if (userId !== session.user.id) {
+        return res.status(403).json({ error: "Forbidden" });
+    }

Note: Adjust the authentication check to match your project's auth pattern.

Committable suggestion skipped: line range outside the PR's diff.

…icated page

- Add notification type definitions and interfaces
- Implement generateNotificationMessage() utility function for formatting
- Create API endpoints for CRUD operations (GET, POST, mark as read)
- Build dedicated /notifications page with unread indicators
- Add NotificationBell component with unread count badge in header
- Implement auto-refresh polling every 30 seconds
- Add notification service layer with helper functions
- Support 12+ notification types (follow, contest, quiz, achievement, etc.)
- Include smooth animations and modern UI design
- Add comprehensive documentation and integration examples
- Fix useEffect dependency warning with useCallback
- Add security comments for authentication model

Closes #<ISSUE_NUMBER>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/common/components/NotificationBell/NotificationBell.tsx (2)

34-34: Replace any with proper Notification type.

Using any defeats TypeScript's type safety. Define or import a Notification interface with at least a read property.

Example interface (add near the top of the file or import from a types file):

interface Notification {
    id: string;
    read: boolean;
    // ... other properties
}

Then update the filter:

-                const unread = data.notifications.filter((n: any) => !n.read).length;
+                const unread = data.notifications.filter((n: Notification) => !n.read).length;

29-36: Consider handling non-OK responses explicitly.

When response.ok is false, the error is silently ignored. Consider logging the error status or showing a user-facing indicator when fetching fails.

             if (response.ok && data.notifications) {
                 const unread = data.notifications.filter((n: any) => !n.read).length;
                 setUnreadCount(unread);
+            } else {
+                console.error("Failed to fetch notifications:", response.status, data);
             }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ae69e1 and 592ebfd.

📒 Files selected for processing (3)
  • CODERABBIT_FIXES.md (1 hunks)
  • src/common/components/NotificationBell/NotificationBell.tsx (1 hunks)
  • src/pages/api/v1/notifications.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • CODERABBIT_FIXES.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/api/v1/notifications.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/common/components/NotificationBell/NotificationBell.tsx (1)
src/lib/firebase.ts (1)
  • auth (21-21)
🔇 Additional comments (5)
src/common/components/NotificationBell/NotificationBell.tsx (5)

3-11: LGTM!

The imports and component setup are clean and appropriate for the notification bell functionality.


13-24: LGTM!

The authentication state listener is properly implemented with correct cleanup. Resetting the unread count on sign-out is a good UX decision.


26-40: Good fix for the dependency issue from the previous review.

The useCallback wrapper with the userId dependency correctly addresses the stale closure concern raised in the previous review.


42-51: LGTM!

The polling effect correctly includes fetchUnreadCount in its dependency array, ensuring the interval always uses the latest function reference. The 30-second polling interval is reasonable for notification updates.


30-30: Verify that the backend validates userId against the authenticated session.

Passing userId as a query parameter is acceptable only if the API endpoint validates that the requesting user's authentication token matches the requested userId. Otherwise, any authenticated user could query another user's notifications. This requires examination of the backend implementation for the /api/v1/notifications endpoint to confirm proper authorization checks are in place.

- Add missing notification types to NotificationType union (unfollow, contest_started, contest_ended, new_potd, friend_solved, leaderboard_rank)
- Add aria-label to notification bell for screen reader accessibility
- Add userId validation to mark-as-read endpoint
- Add production environment check to test-notifications page
- Fix useEffect dependency warning with useCallback in NotificationBell
- Add security comments explaining Firestore rules authentication model
- Update notifications page to include userId in mark-as-read requests
Addresses all CodeRabbit suggestions for type safety, accessibility, and security
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/pages/api/v1/notifications/read/[id].ts (1)

19-29: Clarify the role of userId in this handler (validate vs. actually use it).

You currently require and validate userId from the body but never use it in the update (security is delegated entirely to Firestore rules). That makes the API contract a bit confusing and forces clients to send a field that has no server-side effect.

Consider either:

  • Removing userId from the request body and its validation here (and updating docs/clients to match), if Firestore auth rules are the sole source of truth, or
  • Using userId for a second-layer check (e.g., fetch the doc and assert doc.userId === userId before updating) if you want explicit server-side enforcement and clearer errors.

Right now the extra parameter is just noise and can lead to misunderstandings about what is actually being authorized.

Also applies to: 34-37

src/types/notification.ts (1)

3-17: Use NotificationType for the type fields to get real type safety.

You’ve defined a good NotificationType union, but NotificationPayload.type and Notification.type are still plain string, so callers don’t actually benefit from the union.

Consider tightening the interfaces like this:

-export interface NotificationPayload {
-    type: string;
-    metadata?: Record<string, any>;
-}
+export interface NotificationPayload {
+    type: NotificationType;
+    metadata?: Record<string, any>;
+}

-export interface Notification {
+export interface Notification {
     id: string;
     userId: string;
-    type: string;
+    type: NotificationType;
     message: string;
     read: boolean;
     createdAt: number;
     metadata?: Record<string, any>;
     link?: string;
 }

This will surface mistakes at compile time if a caller passes an unsupported notification type, keeping usages consistent with the rest of the notification system.

Also applies to: 19-31

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 592ebfd and baee593.

📒 Files selected for processing (5)
  • src/common/components/NotificationBell/NotificationBell.tsx (1 hunks)
  • src/pages/api/v1/notifications/read/[id].ts (1 hunks)
  • src/pages/notifications.tsx (1 hunks)
  • src/pages/test-notifications.tsx (1 hunks)
  • src/types/notification.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/common/components/NotificationBell/NotificationBell.tsx
  • src/pages/notifications.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/pages/test-notifications.tsx (2)
src/lib/firebase.ts (1)
  • auth (21-21)
src/services/notifications.ts (1)
  • createNotification (12-40)
src/pages/api/v1/notifications/read/[id].ts (1)
src/lib/firebase.ts (1)
  • db (21-21)
🔇 Additional comments (1)
src/pages/test-notifications.tsx (1)

21-37: Dev-only test page behavior looks solid; production guard now implemented.

The NODE_ENV === "production" early redirect in useEffect combined with the auth check makes this page effectively dev-only while still protecting it behind login in non-prod. The notification creation flow (handleCreateNotification + testNotifications presets) is straightforward and keeps state transitions (loading, result) clear.

No functional issues from this snippet; this is appropriate for an internal testing surface.

Also applies to: 39-58, 60-111

Copy link
Member

@aviralsaxena16 aviralsaxena16 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Pratyush2240 I have reviewed your PR and its great, am merging it , Thanks for your contribution 😇

@aviralsaxena16 aviralsaxena16 merged commit 7ab87e2 into OpenLake:main Dec 12, 2025
1 of 2 checks passed
@Pratyush2240
Copy link
Contributor Author

@aviralsaxena16 Thank you for the review and merge. Glad to contribute, and looking forward to working on more improvements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HACKTOBERFEST ]Implement Notification Page

2 participants