The Sistema de Admisión Web delivers real-time notifications through a two-layer architecture: Firebase Cloud Messaging (FCM) for browser push notifications and Laravel’s database notification channel for persistent in-app badges and history. When a Revisor approves, rejects, or observes an applicant’s documents, both channels fire simultaneously — the applicant receives a push notification on their device and the event is stored in theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ariellukezz/admision-web/llms.txt
Use this file to discover all available pages before exploring further.
notifications table for later retrieval. Revisors, on the other hand, receive their unread count as a server-side Inertia shared prop so the badge is always accurate on page load, with FCM filling in live updates while the tab is open.
Architecture Overview
Firebase Cloud Messaging (Push)
Browser push notifications delivered via the Web Push Protocol. Requires an FCM token registered per device and per user. Tokens are stored in the
fcm_tokens table and managed by the FirebaseService class using the kreait/firebase-php SDK.Laravel Database Notifications
Persistent notifications stored in the
notifications table using Laravel’s built-in Notifiable trait. Both postulantes and revisors use this channel. Records include structured JSON data with message type, applicant details, document lists, and appointment information.How Applicants Receive Notifications
Applicants (id_rol = 8) receive push notifications whenever a Revisor takes an action on their submission. The trigger flows through RevisorDocumentoService, which dispatches a Laravel notification and then calls FirebaseService::sendToTokens() with the applicant’s registered FCM tokens.
Notification types sent to applicants
| Notification class | FCM tipo field | Trigger |
|---|---|---|
RevisionIniciadaNotification | revision_iniciada | Revisor calls POST /revisor/iniciar-revision/{dni} |
RevisionCompletadaNotification | revision_completada | All documents validated — citación scheduled |
RevisionCompletadaNotification | revision_pendiente | Review finalized with pending documents |
| (FCM direct) | revision_renotificar | Revisor sends a manual reminder via POST /revisor/renotificar-postulante/{dni} |
Notification data payload structure
The JSONdata field stored in the notifications table and forwarded over FCM carries the following fields:
PostulanteNotificationController maps these raw fields when returning the notification list to the frontend:
Applicant notification API endpoints
| Method | Path | Description |
|---|---|---|
GET | /notificaciones | List all notifications (default limit: 20) |
GET | /notificaciones/no-leidas | Count of unread notifications |
POST | /notificaciones/{id}/leer | Mark a single notification as read |
POST | /notificaciones/leer-todas | Mark all notifications as read |
How Revisors Receive Notifications
Revisors receive notifications through two complementary mechanisms: a server-side Inertia prop for the badge count, and FCM push for real-time delivery of new submission alerts while the tab is open.Inertia shared prop — notificacionesNoLeidas
Every Inertia page rendered for a Revisor automatically includes the unread notification count. This is computed in HandleInertiaRequests::share():
LayoutDocente.vue) reads $page.props.notificacionesNoLeidas to render the badge in the navigation bar without any additional API call on mount.
Revisor notification API endpoints
| Method | Path | Controller method | Description |
|---|---|---|---|
GET | /revisor/notificaciones | index | List notifications (default limit: 20) |
GET | /revisor/notificaciones/no-leidas | noLeidas | Unread count as { no_leidas: N } |
POST | /revisor/notificaciones/{id}/leer | marcarLeida | Mark one notification as read |
POST | /revisor/notificaciones/leer-todas | marcarTodasLeidas | Mark all notifications as read |
Live FCM updates in SolicitudesRevision.vue
The Revisor/SolicitudesRevision.vue page subscribes to the fcm-message event emitted by the useNotificaciones composable. When a new push message arrives, the page reloads only the solicitudes Inertia prop — preserving scroll position and local UI state:
revisores FCM topic when their device token is registered. This allows the server to broadcast to all active reviewers simultaneously without enumerating individual tokens:
FCM Token Registration Flow
Device tokens are registered on every authenticated page load. TheuseNotificaciones composable (resources/js/composables/useFcm.js) handles the full lifecycle: permission request, service worker registration, token retrieval, and backend sync.
init() function is called on the initial Inertia app setup in app.js. It silently re-registers the token if permission was already granted, ensuring the backend always has a current association between the token and the authenticated user:
fcm.logout() deletes the token from both the backend (DELETE /fcm-token) and the Firebase SDK via deleteToken(messaging), and removes it from localStorage.
The
fcm_tokens table stores one row per unique token value and is updated with the current user_id on each registration via updateOrCreate. The FcmToken model columns are user_id, token, and device_type (web, android, or ios). A user may have multiple rows if they use several devices or browsers.Backend FCM Delivery — FirebaseService
The server-sideApp\Services\FirebaseService class wraps the kreait/firebase-php SDK. It is initialized with a service account credentials file and exposes three delivery methods used by the notification flow:
data values are cast to strings (array_map('strval', $data)) before being attached to the CloudMessage, as the FCM data payload only supports string-typed fields.
Foreground Message Handling
When the browser tab is in the foreground, Firebase does not display a native notification automatically. TheonMessage handler in useFcm.js intercepts the payload and forwards it to the active service worker to render:
fcm-message custom event is listened to by both LayoutDocente.vue (for revisors) and PostulanteAuthenticatedLayout.vue (for applicants), triggering a targeted notification list reload without a full page refresh.
Firebase Configuration
The backend Firebase connection is configured via environment variables. The.env.example defines the following keys:
resources/js/firebase.js with the project’s public config (safe to expose to the browser). The FirebaseConfigController also exposes a dynamic GET /firebase-config endpoint and generates the service worker at GET /firebase-messaging-sw.js with the config values injected server-side:
/firebase-messaging-sw.js by FirebaseConfigController::serviceWorker(), which injects the same environment-driven values so the background message handler always matches the active project.