Overview
Reseñas Gastronómicas features a robust review management system that allows multiple reviewers to collaborate on restaurant dish reviews. The system supports creating, editing, and deleting reviews with real-time synchronization across all connected clients.
All reviews are stored in Firebase Firestore and synchronized in real-time, ensuring that all users see the latest updates immediately.
Core Architecture
The review system is built around the DataStore module, which handles all data operations and maintains the application state.
DataStore Module
The DataStore serves as the central hub for all review operations:
export const DataStore = {
reviews: [],
isLoading: false ,
unsubscribe: null ,
async init () {
FirebaseService . init ();
await this . loadReviews ();
this . setupRealTimeListener ();
}
};
Key Features:
Centralized state management
Automatic real-time synchronization
Fallback to sample data when offline
Event-driven UI updates
Creating Reviews
Multi-Reviewer Support
Reviews support contributions from multiple reviewers (Gian and Yami). Each reviewer can independently rate and comment on dishes.
const reviewData = {
restaurant: 'El Emperador' ,
dish: 'Pizza Margherita' ,
photo: 'https://example.com/photo.jpg' ,
date: '15/09/2025' ,
reviewers: {
gian: {
rating: 9 ,
review: '¡Increíble! La masa estaba perfecta.'
},
yami: {
rating: 8 ,
review: 'Me encantó, aunque le faltó un poquito más de queso.'
}
}
};
Adding a New Review
Reviews are added through the Form module, which integrates with the star rating system:
async handleSubmit ( e ) {
e . preventDefault ();
const gianActive = document . getElementById ( 'gianActive' ). checked ;
const yamiActive = document . getElementById ( 'yamiActive' ). checked ;
const reviewData = {
restaurant: document . getElementById ( 'restaurant' ). value ,
dish: document . getElementById ( 'dish' ). value ,
photo: document . getElementById ( 'photo' ). value ,
date: visitDate ? Utils . formatDate ( new Date ( visitDate )) : Utils . formatDate (),
reviewers: {}
};
if ( gianActive ) {
reviewData . reviewers . gian = {
rating: StarRating . ratings . gian ,
review: document . getElementById ( 'gianReview' ). value
};
}
await DataStore . addReview ( reviewData );
}
You can enable or disable individual reviewers using the checkbox toggles in the form. This allows for single-reviewer or collaborative reviews.
Editing Reviews
The system supports full editing capabilities for existing reviews. When editing, the form is pre-populated with existing data:
async updateReview ( id , review ) {
try {
const updatedReview = await FirebaseService . updateReview ( id , review );
return updatedReview ;
} catch ( error ) {
console . error ( 'Error actualizando reseña:' , error );
throw error ;
}
}
Edit Process:
User clicks edit button on a review card
Modal opens with form pre-filled with existing data
Form tracks edit mode via data-edit-id attribute
On submit, updateReview() is called instead of addReview()
Real-time listener updates all connected clients
Deleting Reviews
Reviews can be permanently deleted from the system:
async deleteReview ( id ) {
try {
await FirebaseService . deleteReview ( id );
return true ;
} catch ( error ) {
console . error ( 'Error eliminando reseña:' , error );
throw error ;
}
}
Deleting a review is permanent and cannot be undone. The review is immediately removed from Firebase and all connected clients.
Real-Time Synchronization
One of the most powerful features is real-time synchronization across all connected clients.
Setting Up the Listener
setupRealTimeListener () {
// Cancel previous listener if exists
if ( this . unsubscribe ) {
this . unsubscribe ();
}
this . unsubscribe = FirebaseService . onReviewsChange (( reviews ) => {
this . reviews = reviews ;
// Dispatch event to update UI
document . dispatchEvent ( new CustomEvent ( 'reviewsUpdated' ));
});
}
How It Works
User Action : A user creates, edits, or deletes a review
Firebase Update : The change is written to Firestore
Snapshot Listener : All connected clients receive the update
DataStore Update : The local reviews array is updated
DOM Event : A reviewsUpdated event is dispatched
UI Refresh : Components listening to the event re-render
Firebase Integration
The system uses Firebase Firestore for data persistence and real-time updates.
Adding Reviews to Firebase
async addReview ( review ) {
try {
const reviewWithTimestamp = {
... review ,
timestamp: firebase . firestore . FieldValue . serverTimestamp (),
createdAt: new Date (). toISOString ()
};
const docRef = await this . db . collection ( 'reviews' ). add ( reviewWithTimestamp );
return { id: docRef . id , ... reviewWithTimestamp };
} catch ( error ) {
console . error ( 'Error agregando reseña:' , error );
throw error ;
}
}
Updating Reviews in Firebase
async updateReview ( id , review ) {
const reviewWithTimestamp = {
... review ,
updatedAt: new Date (). toISOString ()
};
await this . db . collection ( 'reviews' ). doc ( id ). update ( reviewWithTimestamp );
return { id , ... reviewWithTimestamp };
}
The system automatically adds timestamp, createdAt, and updatedAt fields to track review history.
Review Data Structure
Each review follows a consistent data structure:
{
id : "unique-firebase-id" ,
restaurant : "Restaurant Name" ,
dish : "Dish Name" ,
photo : "https://image-url.com/photo.jpg" ,
date : "15/09/2025" ,
timestamp : Firestore . Timestamp ,
createdAt : "2025-09-15T10:30:00.000Z" ,
updatedAt : "2025-09-16T14:20:00.000Z" ,
reviewers : {
gian : {
rating : 9 ,
review : "Review text from Gian"
},
yami : {
rating : 8 ,
review : "Review text from Yami"
}
}
}
Utility Functions
The system includes several utility functions for working with reviews:
Calculate Average Rating
calculateAverageRating ( review ) {
const ratings = Object . values ( review . reviewers ). map ( r => r . rating );
const sum = ratings . reduce (( a , b ) => a + b , 0 );
return ( sum / ratings . length ). toFixed ( 1 );
}
Get Reviews by Restaurant
getReviews ( filter = 'all' ) {
return filter === 'all'
? this . reviews
: this . reviews . filter ( r => r . restaurant === filter );
}
Get Unique Restaurants
getRestaurants () {
return [ ... new Set ( this . reviews . map ( r => r . restaurant ))];
}
Offline Support
The system gracefully handles offline scenarios:
async loadReviews () {
this . isLoading = true ;
try {
this . reviews = await FirebaseService . getReviews ();
} catch ( error ) {
console . error ( 'Error cargando reseñas:' , error );
// Load sample data if Firebase fails
this . loadSampleData ();
}
this . isLoading = false ;
}
If Firebase is unavailable, the app loads sample data to demonstrate functionality. This ensures users can always explore the interface.
Best Practices
Validate Input Always validate review data before submission to ensure data integrity
Handle Errors Implement proper error handling for all Firebase operations
Loading States Show loading indicators during async operations for better UX
Cleanup Listeners Always unsubscribe from Firebase listeners when components unmount
Code Reference
Key files for review management:
datastore.js:8-24 - DataStore initialization and review loading
datastore.js:39-68 - Add, update, and delete operations
form.js:12-68 - Form submission and review creation
firebase.js:24-64 - Firebase CRUD operations
firebase.js:68-78 - Real-time listener setup