From 0535ece02a06d94f445f0a42b1bb5c398a1fa31b Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:41:36 +0200 Subject: [PATCH 01/25] docs: add comprehensive documentation to main.jsx --- src/main.jsx | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main.jsx b/src/main.jsx index c1767716..ff6dfa98 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,18 +1,64 @@ +/** + * @fileoverview Main entry point for the MyWorld React application. + * + * This file is responsible for bootstrapping the React application by: + * - Importing necessary dependencies and stylesheets + * - Setting up the React root element + * - Wrapping the application with required context providers + * - Rendering the application in React Strict Mode for development checks + * + * @module main + * @requires react + * @requires react-dom/client + * @requires leaflet - Map library styles for navigation features + * @requires ./App - Root application component + * @requires ./context - Authentication context provider + */ + import React from 'react'; import ReactDOM from 'react-dom/client'; + +// Third-party library styles +// Leaflet CSS is required for map components in navigation features import 'leaflet/dist/leaflet.css'; + +// Application styles - imported in order of specificity +// Design tokens define CSS custom properties (colors, spacing, typography) import './styles/design-tokens.css'; +// Global styles apply base styling and resets import './styles/global.css'; +// Component styles provide reusable component-level styling import './styles/components.css'; +// Index CSS contains any additional application-wide styles import './index.css'; + +// Application components import App from './App.jsx'; + +// Context providers for global state management +// AuthProvider manages user authentication state throughout the app import { AuthProvider } from './context'; +/** + * Creates the React root and renders the application. + * + * The application is wrapped in: + * 1. React.StrictMode - Enables additional development checks and warnings + * for detecting potential problems (double-rendering, deprecated APIs, etc.) + * 2. AuthProvider - Provides authentication context to all child components, + * enabling access to user state, login/logout functions, and auth status + * + * @see {@link https://react.dev/reference/react/StrictMode} React Strict Mode + */ const root = ReactDOM.createRoot(document.getElementById('root')); + +// Render the application tree root.render( + {/* AuthProvider wraps App to provide authentication context globally */} ); + From 0c6e8dea61eca4528a4c8e3febef186d77022496 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:42:10 +0200 Subject: [PATCH 02/25] docs: add comprehensive documentation to LanguageSwitcher.jsx --- src/components/LanguageSwitcher.jsx | 37 +++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/components/LanguageSwitcher.jsx b/src/components/LanguageSwitcher.jsx index 8858ba14..aaf7db91 100644 --- a/src/components/LanguageSwitcher.jsx +++ b/src/components/LanguageSwitcher.jsx @@ -1,19 +1,52 @@ +/** + * @fileoverview Language Switcher Component for internationalization support. + * + * Provides a button that allows users to toggle between English and Greek + * languages in the application. Uses the translation context to manage + * the current language state. + * + * @module components/LanguageSwitcher + * @requires react + * @requires ../i18n - Translation context and hooks + */ + import React from 'react'; import { useTranslation } from '../i18n'; import './LanguageSwitcher.css'; +/** + * LanguageSwitcher Component + * + * A button component that toggles the application language between + * English (EN) and Greek (EL). The button displays the opposite language + * code from the currently active language, indicating what language + * the user will switch to upon clicking. + * + * @component + * @example + * // Basic usage in a navigation bar + * + * + * @returns {React.ReactElement} A styled button for language toggling + * + * @see {@link module:i18n/LanguageContext} for language context implementation + */ const LanguageSwitcher = () => { + // Get current language and toggle function from translation context const { language, toggleLanguage } = useTranslation(); return ( - ); }; export default LanguageSwitcher; + From 43ee5f5a71fd012106ef83b56f615caad5276fce Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:42:34 +0200 Subject: [PATCH 03/25] docs: add comprehensive documentation to context/index.jsx --- src/context/index.jsx | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/context/index.jsx b/src/context/index.jsx index ee9006cf..750c3582 100644 --- a/src/context/index.jsx +++ b/src/context/index.jsx @@ -1,2 +1,29 @@ +/** + * @fileoverview Context Providers Barrel Export Module. + * + * This module serves as a centralized export point for all React context + * providers and their associated hooks. It simplifies imports throughout + * the application by allowing consumers to import from a single location. + * + * Available contexts: + * - AuthContext: Manages user authentication state (login, logout, user data) + * - ThemeContext: Manages application theme (light/dark mode) + * + * @module context + * @example + * // Import providers for wrapping the application + * import { AuthProvider, ThemeProvider } from './context'; + * + * @example + * // Import hooks for consuming context in components + * import { useAuthContext, useThemeContext } from './context'; + */ + +// Authentication context exports +// Provides user authentication state and methods (login, logout, registration) export { AuthProvider, useAuthContext } from './AuthContext.jsx'; + +// Theme context exports +// Provides theme state and toggle functionality for light/dark mode export { ThemeProvider, useThemeContext } from './ThemeContext.jsx'; + From 183434a123325a19f5335316fecc18da0638ed8b Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:42:59 +0200 Subject: [PATCH 04/25] docs: add comprehensive documentation to hooks/index.js --- src/hooks/index.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/hooks/index.js b/src/hooks/index.js index 60549bf8..4541b90a 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -1,3 +1,30 @@ +/** + * @fileoverview Custom React Hooks Barrel Export Module. + * + * This module serves as a centralized export point for all custom React hooks + * used throughout the application. It simplifies imports by allowing consumers + * to import multiple hooks from a single location. + * + * Available hooks: + * - useAuth: Authentication hook for managing user sessions + * - useApi: API communication hook for making HTTP requests + * - useTheme: Theme management hook for light/dark mode toggling + * + * @module hooks + * @example + * // Import multiple hooks in a single statement + * import { useAuth, useApi, useTheme } from './hooks'; + */ + +// Authentication hook +// Provides login, logout, and authentication state management export { useAuth } from './useAuth'; + +// API hook +// Provides utilities for making API requests with loading and error states export { useApi } from './useApi'; + +// Theme hook +// Provides theme state and toggle functionality for theming export { default as useTheme } from './useTheme'; + From d5a9c20c5983e68e4e46e0c16b3f743a406abb19 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:43:26 +0200 Subject: [PATCH 05/25] docs: add comprehensive documentation to i18n/index.js --- src/i18n/index.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/i18n/index.js b/src/i18n/index.js index a506d570..3f8646c1 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -1,2 +1,37 @@ +/** + * @fileoverview Internationalization (i18n) Barrel Export Module. + * + * This module serves as the centralized export point for all internationalization + * functionality in the application. It provides access to the language context, + * translation hook, and translation data. + * + * Available exports: + * - LanguageProvider: Context provider for wrapping components that need translation + * - useTranslation: Hook for accessing current language and translation functions + * - translations: Object containing all translated strings for supported languages + * + * Supported languages: + * - English (en) + * - Greek (el) + * + * @module i18n + * @example + * // Wrap your app with LanguageProvider + * import { LanguageProvider } from './i18n'; + * + * + * @example + * // Use translations in a component + * import { useTranslation } from './i18n'; + * const { t, language } = useTranslation(); + */ + +// Language context and hook exports +// LanguageProvider wraps the app to provide translation context +// useTranslation hook provides access to translation functions and current language export { LanguageProvider, useTranslation } from './LanguageContext'; + +// Translation data exports +// Contains all translated strings organized by language code export { translations } from './translations'; + From f48981fbdf3202d386c5b9dcf5467539d02c7252 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:44:29 +0200 Subject: [PATCH 06/25] docs: add comprehensive documentation to FavouritesPage.jsx --- src/pages/FavouritesPage.jsx | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/pages/FavouritesPage.jsx b/src/pages/FavouritesPage.jsx index 3d5c5900..f048dbba 100644 --- a/src/pages/FavouritesPage.jsx +++ b/src/pages/FavouritesPage.jsx @@ -1,3 +1,24 @@ +/** + * @fileoverview Favourites Page Component for managing user's saved places. + * + * This page displays the user's favourite and disliked places in a tabbed + * interface. Users can view, manage, and remove places from their lists. + * The component handles loading states, error display, and empty states + * for both tabs. + * + * Features: + * - Tabbed interface for switching between favourites and disliked lists + * - Loading skeleton display during data fetch + * - Error banner for API failures + * - Empty state with call-to-action for each list type + * - Remove functionality for both favourite and disliked items + * + * @module pages/FavouritesPage + * @requires react + * @requires ../i18n - Translation support + * @requires ../hooks/useFavourites - Favourites management hook + */ + import React, { useState } from 'react'; import { useTranslation } from '../i18n'; import PlaceCardSkeleton from '../components/PlaceCardSkeleton.jsx'; @@ -7,26 +28,51 @@ import FavouriteListItem from '../components/favourites/FavouriteListItem'; import { useFavourites } from '../hooks/useFavourites'; import './FavouritesPage.css'; +/** + * FavouritesPage Component + * + * Displays a user's favourite and disliked places with a tabbed interface. + * Allows users to manage their saved places by removing items from either list. + * + * @component + * @example + * // Usage in router + * } /> + * + * @returns {React.ReactElement} The favourites page with tabbed content + */ const FavouritesPage = () => { + // Get translation function for internationalized text const { t } = useTranslation(); + + // Toast notification for error display const { error: showError } = useToast(); + + // Track which tab is currently active ('favourites' or 'disliked') const [activeTab, setActiveTab] = useState('favourites'); + + // Fetch favourites data and handlers from custom hook const { favourites, disliked, loading, error, handleRemoveFavourite, handleRemoveDisliked } = useFavourites(t, showError); return (
+ {/* Hero section with page title and subtitle */}
+ {/* Error banner - displays when API call fails */} {error &&

{error}

} + {/* Tab navigation for switching between lists */}
+ {/* Favourites tab with heart icon and count badge */} + {/* Disliked tab with thumbs down icon and count badge */}
+ {/* Tab content area - conditionally renders based on loading and active tab */}
{loading ? ( + // Loading state: show skeleton placeholders
) : activeTab === 'favourites' ? ( + // Favourites tab content favourites.length > 0 ? ( + // Display favourites grid when items exist
{favourites.map((fav, i) => ( { ))}
) : ( + // Empty state for no favourites
❤️

{t('noFavourites')}

{t('startExploring')}

) ) : ( + // Disliked tab content disliked.length > 0 ? ( + // Display disliked grid when items exist
{disliked.map((dis, i) => ( { ))}
) : ( + // Empty state for no disliked places
👎

{t('noDisliked')}

{t('dislikedWillAppearHere')}

@@ -73,3 +127,4 @@ const FavouritesPage = () => { }; export default FavouritesPage; + From cd93a35581b2d623f749fe6b501be42b1cb2e144 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:46:05 +0200 Subject: [PATCH 07/25] docs: add comprehensive documentation to NavigationPage.jsx --- src/pages/NavigationPage.jsx | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/pages/NavigationPage.jsx b/src/pages/NavigationPage.jsx index 8c5882b1..be7f0471 100644 --- a/src/pages/NavigationPage.jsx +++ b/src/pages/NavigationPage.jsx @@ -1,3 +1,27 @@ +/** + * @fileoverview Navigation Page Component for route planning and navigation. + * + * This page provides route planning functionality allowing users to calculate + * routes between two locations. It supports multiple transportation modes and + * displays route information including distance, estimated time, and map visualization. + * + * Features: + * - Location input with geocoding support + * - Multiple transportation modes (driving, walking, cycling) + * - Route visualization on an interactive map + * - Quick route presets for popular destinations + * - Real-time route calculations via OSRM and backend API + * - URL parameter support for destination pre-filling + * + * @module pages/NavigationPage + * @requires react + * @requires react-router-dom - For URL search params + * @requires ../api - Navigation API calls + * @requires ../i18n - Translation support + * @requires ../utils/geocoding - Location geocoding utilities + * @requires ../utils/routing - Route calculation utilities + */ + import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useSearchParams } from 'react-router-dom'; import { navigationAPI } from '../api'; @@ -10,51 +34,104 @@ import { reverseGeocode, forwardGeocode, formatCoordinates } from '../utils/geoc import { fetchRoute } from '../utils/routing'; import './NavigationPage.css'; +/** + * NavigationPage Component + * + * Provides a complete route planning interface with form inputs, map display, + * and route results. Handles geocoding of location names to coordinates and + * fetches route data from multiple sources. + * + * @component + * @example + * // Basic usage in router + * } /> + * + * @example + * // With destination pre-filled via URL + * // URL: /navigation?dest=Santorini, Greece + * + * @returns {React.ReactElement} The navigation page with route planning interface + */ const NavigationPage = () => { + // Translation function for internationalized text const { t } = useTranslation(); + + // URL search params for pre-filling destination from deep links const [searchParams] = useSearchParams(); + + // Route calculation result from the API const [routeData, setRouteData] = useState(null); + + // Loading state for API calls const [loading, setLoading] = useState(false); + + // Flag indicating geocoding is in progress for input locations const [geocodingInput, setGeocodingInput] = useState(false); + + // General error state for form submission const [error, setError] = useState(null); + // Extract destination from URL query params if present const destFromUrl = searchParams.get('dest'); + + // Form state for route inputs const [formData, setFormData] = useState({ fromLocation: 'Athens, Greece', toLocation: destFromUrl || 'Thessaloniki, Greece', transportMode: 'DRIVING', }); + // Human-readable names for start and end points (resolved from coordinates) const [startName, setStartName] = useState(null); const [endName, setEndName] = useState(null); + + // Loading states for individual geocoding operations const [geocodingStart, setGeocodingStart] = useState(false); const [geocodingEnd, setGeocodingEnd] = useState(false); + + // Geometry data for route visualization on the map const [routeGeometry, setRouteGeometry] = useState(null); + + // Route-specific error messages (e.g., ferry required) const [routeError, setRouteError] = useState(null); + + // Ref for scrolling to results after route calculation const resultsRef = useRef(null); + /** + * Performs reverse geocoding on route start and end points. + * Converts coordinates to human-readable place names for display. + * Falls back to formatted coordinates if geocoding fails. + * + * @function geocodeLocations + * @async + */ const geocodeLocations = useCallback(async () => { if (!routeData) return; const { startPoint, endPoint } = routeData; + // Geocode start point if coordinates are available if (startPoint?.latitude && startPoint?.longitude) { setGeocodingStart(true); try { const result = await reverseGeocode(startPoint.latitude, startPoint.longitude); setStartName(result.name); } catch { + // Fallback to formatted coordinates on error setStartName(formatCoordinates(startPoint.latitude, startPoint.longitude)); } finally { setGeocodingStart(false); } } + // Geocode end point if coordinates are available if (endPoint?.latitude && endPoint?.longitude) { setGeocodingEnd(true); try { const result = await reverseGeocode(endPoint.latitude, endPoint.longitude); setEndName(result.name); } catch { + // Fallback to formatted coordinates on error setEndName(formatCoordinates(endPoint.latitude, endPoint.longitude)); } finally { setGeocodingEnd(false); @@ -62,8 +139,10 @@ const NavigationPage = () => { } }, [routeData]); + // Trigger geocoding when route data changes useEffect(() => { geocodeLocations(); }, [geocodeLocations]); + // Reset display states when loading begins useEffect(() => { if (loading) { setStartName(null); @@ -73,6 +152,15 @@ const NavigationPage = () => { } }, [loading]); + /** + * Handles form submission for route calculation. + * Performs forward geocoding on input locations, fetches route from OSRM, + * and retrieves additional route info from the backend API. + * + * @function handleSubmit + * @async + * @param {React.FormEvent} e - Form submit event + */ const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); @@ -82,22 +170,26 @@ const NavigationPage = () => { setRouteGeometry(null); try { + // Step 1: Forward geocode both locations to get coordinates const [fromResult, toResult] = await Promise.all([ forwardGeocode(formData.fromLocation), forwardGeocode(formData.toLocation) ]); + // Validate geocoding results if (!fromResult) throw new Error(t('locationNotFound', { location: formData.fromLocation }) || `Could not find: ${formData.fromLocation}`); if (!toResult) throw new Error(t('locationNotFound', { location: formData.toLocation }) || `Could not find: ${formData.toLocation}`); setGeocodingInput(false); + // Step 2: Fetch route geometry from OSRM routing service const osrmResult = await fetchRoute({ startLat: fromResult.lat, startLng: fromResult.lng, endLat: toResult.lat, endLng: toResult.lng, transportMode: formData.transportMode, }); + // Handle OSRM result - may include ferry warnings if (!osrmResult.success) { setRouteError(osrmResult.message); } else { @@ -107,12 +199,14 @@ const NavigationPage = () => { } } + // Step 3: Fetch additional route information from backend API const response = await navigationAPI.getRoute({ userLatitude: fromResult.lat, userLongitude: fromResult.lng, placeLatitude: toResult.lat, placeLongitude: toResult.lng, transportationMode: formData.transportMode, }); + // Merge OSRM distance/duration with backend route info const routeInfo = response.data.route; if (osrmResult.success && osrmResult.distance && osrmResult.duration) { routeInfo.distance = osrmResult.distance; @@ -121,6 +215,7 @@ const NavigationPage = () => { setRouteData(routeInfo); + // Smooth scroll to results after a short delay setTimeout(() => { if (resultsRef.current) { resultsRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); @@ -134,6 +229,12 @@ const NavigationPage = () => { } }; + /** + * Predefined quick route options for popular Greek destinations. + * Each route includes a display name, icon, and from/to locations. + * + * @constant {Array} quickRoutes + */ const quickRoutes = [ { name: 'Athens → Thessaloniki', icon: '🏛️', from: 'Athens, Greece', to: 'Thessaloniki, Greece' }, { name: 'Thessaloniki → Athens', icon: '🏛️', from: 'Thessaloniki, Greece', to: 'Athens, Greece' }, @@ -142,13 +243,17 @@ const NavigationPage = () => { return (
+ {/* Hero section with page title */}
+ {/* Main navigation layout with form and results columns */}
+ {/* Route input form component */} + {/* Route results display with map and details */}
{
+ {/* Quick routes section for popular destination shortcuts */}

{t('quickRoutes')}

@@ -175,3 +281,4 @@ const NavigationPage = () => { }; export default NavigationPage; + From 98adab6a98efdfeec6c5f643cc20e2749416fbe4 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:47:23 +0200 Subject: [PATCH 08/25] docs: add comprehensive documentation to PlaceDetailsPage.jsx --- src/pages/PlaceDetailsPage.jsx | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/pages/PlaceDetailsPage.jsx b/src/pages/PlaceDetailsPage.jsx index 3486da30..0d2217b5 100644 --- a/src/pages/PlaceDetailsPage.jsx +++ b/src/pages/PlaceDetailsPage.jsx @@ -1,3 +1,21 @@ +/** + * @fileoverview Place Details Page Component for displaying place information. + * + * This page provides a comprehensive view of a specific place including: + * - Hero section with place name, category, and rating + * - Address and description information + * - Photo gallery + * - User reviews section with submission form + * - Place actions (favourite, dislike, navigation) + * - Report functionality for inappropriate content + * + * @module pages/PlaceDetailsPage + * @requires react + * @requires react-router-dom - For URL params and navigation + * @requires ../hooks/usePlaceDetails - Place data fetching hook + * @requires ../hooks/usePlaceActions - Place interaction actions hook + */ + import React, { useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useTranslation } from '../i18n'; @@ -9,31 +27,63 @@ import ReportModal from '../components/place/ReportModal'; import PlaceActionsBar from '../components/place/PlaceActionsBar'; import './PlaceDetailsPage.css'; +/** + * PlaceDetailsPage Component + * + * Displays detailed information about a specific place identified by URL param. + * Handles loading and error states, and provides interactive features like + * reviews, favourites, and reporting. + * + * @component + * @example + * // Usage in router with dynamic placeId param + * } /> + * + * @returns {React.ReactElement} The place details page or loading/error state + */ const PlaceDetailsPage = () => { + // Translation function for internationalized text const { t } = useTranslation(); + + // Navigation hook for handling back button const navigate = useNavigate(); + + // Toast notifications for user feedback const toast = useToast(); + + // Extract placeId from URL parameters const { placeId } = useParams(); + // Fetch place details and reviews using custom hook const placeState = usePlaceDetails(placeId, t); const { place, reviews, loading, error, isFavourite, isDisliked } = placeState; + + // Get action handlers for place interactions const { handleReviewSubmit, handleReportSubmit, toggleFavourite, toggleDisliked } = usePlaceActions( placeId, t, { showSuccess: toast.success, showWarning: toast.warning, showError: toast.error }, placeState ); + // Form visibility states const [showReviewForm, setShowReviewForm] = useState(false); const [showReportForm, setShowReportForm] = useState(false); + + // Review form data state const [reviewForm, setReviewForm] = useState({ rating: 5, comment: '' }); + + // Report form data state const [reportForm, setReportForm] = useState({ reason: '', description: '' }); + // Calculate average rating from all reviews const averageRating = reviews.length > 0 ? reviews.reduce((acc, r) => acc + r.rating, 0) / reviews.length : null; + // Loading state UI if (loading) return (

{t('loadingDetails')}

); + // Error state UI - shown when place fetch fails or place doesn't exist if (error || !place) return (

{t('placeNotFound')}

{error}

@@ -43,15 +93,20 @@ const PlaceDetailsPage = () => { return (
+ {/* Hero section with place image background and key information */}
+ {/* Back navigation button */} + {/* Category badge display */}
{t(place.category) || place.category}
+ {/* Place title */}

{place.name}

+ {/* Meta information: location and rating */}
📍{place.city}, {place.country} @@ -64,23 +119,29 @@ const PlaceDetailsPage = () => {
+ {/* Actions bar with favourite, dislike, and navigation buttons */} + {/* Place information card with address and description */}

{t('information')}

📍{place.address}

{place.description}

+ {/* Report button for flagging inappropriate content */}
+ {/* Photo gallery section */}
+ {/* Reviews section with user reviews and submission form */} handleReviewSubmit(e, reviewForm, setShowReviewForm, setReviewForm)} /> + {/* Report modal for submitting place reports */} handleReportSubmit(e, reportForm, setShowReportForm, setReportForm)} />
@@ -89,3 +150,4 @@ const PlaceDetailsPage = () => { }; export default PlaceDetailsPage; + From 9da72f2bc25a683a108e79d83e39c796c82eacb7 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:48:53 +0200 Subject: [PATCH 09/25] docs: add comprehensive documentation to PreferencesPage.jsx --- src/pages/PreferencesPage.jsx | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/pages/PreferencesPage.jsx b/src/pages/PreferencesPage.jsx index 9a3070d3..b34f6e26 100644 --- a/src/pages/PreferencesPage.jsx +++ b/src/pages/PreferencesPage.jsx @@ -1,3 +1,23 @@ +/** + * @fileoverview Preferences Page Component for managing user preference profiles. + * + * This page allows users to create, view, edit, and delete preference profiles + * that customize their recommendation experience. Users can have multiple profiles + * and set one as active to personalize place recommendations. + * + * Features: + * - Create new preference profiles with name and category selections + * - Edit existing profile names and categories + * - Delete profiles with confirmation + * - Set an active profile for personalized recommendations + * - Loading and error state handling + * + * @module pages/PreferencesPage + * @requires react + * @requires ../api - API calls for preference and user data + * @requires ../i18n - Translation support + */ + import React, { useState, useEffect, useCallback } from 'react'; import { preferenceAPI, userAPI, getAuthenticatedUserId } from '../api'; import { useTranslation } from '../i18n'; @@ -7,29 +27,72 @@ import PreferenceForm from '../components/preferences/PreferenceForm'; import ProfileCard from '../components/preferences/ProfileCard'; import './PreferencesPage.css'; +/** + * PreferencesPage Component + * + * Provides a complete interface for managing user preference profiles. + * Allows creating, editing, deleting, and activating profiles that + * influence the recommendation algorithm. + * + * @component + * @example + * // Usage in router + * } /> + * + * @returns {React.ReactElement} The preferences management page + */ const PreferencesPage = () => { + // Translation function for internationalized text const { t } = useTranslation(); + + // Toast notification for error display const { error: showError } = useToast(); + + // List of user's preference profiles const [profiles, setProfiles] = useState([]); + + // Loading state for profile fetching const [loading, setLoading] = useState(true); + + // Error message for fetch failures const [error, setError] = useState(null); + + // Controls visibility of create profile form const [showCreateForm, setShowCreateForm] = useState(false); + + // Currently editing profile (null when not editing) const [editingProfile, setEditingProfile] = useState(null); + + // ID of the currently active profile const [activeProfileId, setActiveProfileId] = useState(null); + + // Form data for create/edit operations const [formData, setFormData] = useState({ name: '', categories: [] }); + /** + * Fetches user's preference profiles and active profile from API. + * Updates profiles and activeProfileId states on success. + * + * @function fetchProfiles + * @async + */ const fetchProfiles = useCallback(async () => { setLoading(true); try { const userId = getAuthenticatedUserId(); + + // Fetch both profiles and user data in parallel for efficiency const [profilesResponse, userResponse] = await Promise.all([ preferenceAPI.getPreferenceProfiles(userId), userAPI.getUserProfile(userId) ]); + + // Extract active profile ID from user data (handles different response formats) const userActiveProfile = userResponse.data.user?.activeProfile || userResponse.data.activeProfile; setActiveProfileId(userActiveProfile); setProfiles(profilesResponse.data.profiles || []); } catch (err) { + // Handle authentication errors specifically if (err.message === 'User not authenticated') setError(t('notAuthenticated')); console.error('Error fetching profiles:', err); } finally { @@ -37,8 +100,17 @@ const PreferencesPage = () => { } }, [t]); + // Fetch profiles on component mount useEffect(() => { fetchProfiles(); }, [fetchProfiles]); + /** + * Handles creation of a new preference profile. + * Automatically sets as active if no active profile exists. + * + * @function handleCreate + * @async + * @param {React.FormEvent} e - Form submit event + */ const handleCreate = async (e) => { e.preventDefault(); try { @@ -46,6 +118,7 @@ const PreferencesPage = () => { const payload = { profileName: formData.name, categories: formData.categories }; const response = await preferenceAPI.createPreferenceProfile(userId, payload); + // Auto-activate first profile if none is currently active if (!activeProfileId && response.data.profiles?.length > 0) { const newProfile = response.data.profiles[response.data.profiles.length - 1]; const newProfileId = newProfile?.profileId || newProfile?.id; @@ -59,13 +132,23 @@ const PreferencesPage = () => { } }; + /** + * Handles updating an existing preference profile. + * + * @function handleUpdate + * @async + * @param {React.FormEvent} e - Form submit event + */ const handleUpdate = async (e) => { e.preventDefault(); try { const userId = getAuthenticatedUserId(); const profileId = editingProfile.profileId || editingProfile.id; + + // Validate profile ID exists if (!profileId) { showError(t('errorProfileIdNotFound')); return; } + // Build payload with categories and optional name const payload = { categories: formData.categories || [] }; if (formData.name) payload.profileName = formData.name; @@ -77,7 +160,16 @@ const PreferencesPage = () => { } }; + /** + * Handles deletion of a preference profile. + * Shows confirmation dialog before deleting. + * + * @function handleDelete + * @async + * @param {string} profileId - ID of the profile to delete + */ const handleDelete = async (profileId) => { + // Confirm deletion with user if (!window.confirm(t('confirmDeleteProfile'))) return; try { const userId = getAuthenticatedUserId(); @@ -88,30 +180,55 @@ const PreferencesPage = () => { } }; + /** + * Sets a profile as the active profile for recommendations. + * Uses optimistic UI update with rollback on error. + * + * @function handleSetActive + * @async + * @param {string} profileId - ID of the profile to activate + */ const handleSetActive = async (profileId) => { + // Store previous state for rollback const previousActive = activeProfileId; + + // Optimistic update for better UX setActiveProfileId(profileId); try { const userId = getAuthenticatedUserId(); await preferenceAPI.setActiveProfile(userId, profileId); fetchProfiles(); } catch (err) { + // Rollback on failure setActiveProfileId(previousActive); showError(t('errorActivatingProfile')); } }; + /** + * Initializes edit mode for a profile. + * Populates form with existing profile data. + * + * @function startEdit + * @param {Object} profile - Profile object to edit + */ const startEdit = (profile) => { setEditingProfile(profile); setFormData({ name: profile.name || profile.profileName || '', categories: profile.categories || [] }); }; + /** + * Closes create/edit form and resets form state. + * + * @function closeForm + */ const closeForm = () => { setShowCreateForm(false); setEditingProfile(null); setFormData({ name: '', categories: [] }); }; + // Loading state UI if (loading) { return (
@@ -123,6 +240,7 @@ const PreferencesPage = () => { ); } + // Error state UI if (error) { return (
@@ -136,23 +254,28 @@ const PreferencesPage = () => { return (
+ {/* Page hero section */}
+ {/* Create new profile button */}
+ {/* Create/Edit form modal - shown when creating or editing */} {(showCreateForm || editingProfile) && ( )} + {/* Profiles grid or empty state */} {profiles.length > 0 ? (
{profiles.map((profile, index) => { + // Check if this profile is the active one const isActive = profile.profileId === activeProfileId || profile.id === activeProfileId; return ( { })}
) : ( + // Empty state when no profiles exist
⚙️

{t('noPreferenceProfiles')}

@@ -175,3 +299,4 @@ const PreferencesPage = () => { }; export default PreferencesPage; + From f8f71d94ea7a9bfc822706d1973d9e19c2676050 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:50:06 +0200 Subject: [PATCH 10/25] docs: add comprehensive documentation to UserProfilePage.jsx --- src/pages/UserProfilePage.jsx | 102 ++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/pages/UserProfilePage.jsx b/src/pages/UserProfilePage.jsx index 9893e850..33aad511 100644 --- a/src/pages/UserProfilePage.jsx +++ b/src/pages/UserProfilePage.jsx @@ -1,3 +1,23 @@ +/** + * @fileoverview User Profile Page Component for managing user information. + * + * This page displays and allows editing of user profile information and + * application settings. It provides forms for updating personal details + * (name, email, etc.) and user preferences (notifications, language, etc.). + * + * Features: + * - Display user profile information + * - Edit profile details with inline form + * - Manage application settings + * - Success/error message feedback + * - Loading and error state handling + * + * @module pages/UserProfilePage + * @requires react + * @requires ../api - User API calls + * @requires ../i18n - Translation support + */ + import React, { useState, useEffect, useCallback } from 'react'; import { userAPI, getAuthenticatedUserId } from '../api'; import { useTranslation } from '../i18n'; @@ -7,22 +27,60 @@ import ProfileInfoCard from '../components/profile/ProfileInfoCard'; import SettingsCard from '../components/profile/SettingsCard'; import './UserProfilePage.css'; +/** + * UserProfilePage Component + * + * Provides a complete interface for viewing and editing user profile + * information and application settings. Uses inline edit mode for + * seamless user experience. + * + * @component + * @example + * // Usage in router + * } /> + * + * @returns {React.ReactElement} The user profile management page + */ const UserProfilePage = () => { + // Translation function for internationalized text const { t } = useTranslation(); + + // User profile data const [profile, setProfile] = useState(null); + + // User settings data const [settings, setSettings] = useState(null); + + // Loading state for initial data fetch const [loading, setLoading] = useState(true); + + // Error message for fetch failures const [error, setError] = useState(null); + + // Edit mode toggle for profile info const [editMode, setEditMode] = useState(false); + + // Edit mode toggle for settings const [editSettingsMode, setEditSettingsMode] = useState(false); + + // Temporary message display for success/error feedback const [message, setMessage] = useState({ text: '', type: '' }); + /** + * Fetches user profile data from API. + * Updates profile state on success, sets error on authentication failure. + * + * @function fetchProfile + * @async + */ const fetchProfile = useCallback(async () => { try { const userId = getAuthenticatedUserId(); const response = await userAPI.getUserProfile(userId); + // Handle different response formats setProfile(response.data.user || response.data); } catch (err) { + // Handle authentication errors specifically if (err.message === 'User not authenticated') setError(t('notAuthenticated')); console.error('Error fetching profile:', err); } finally { @@ -30,28 +88,55 @@ const UserProfilePage = () => { } }, [t]); + /** + * Fetches user settings from API. + * Updates settings state on success. + * + * @function fetchSettings + * @async + */ const fetchSettings = useCallback(async () => { try { const userId = getAuthenticatedUserId(); const response = await userAPI.getUserSettings(userId); + // Handle different response formats setSettings(response.data.settings || response.data); } catch (err) { console.error('Error fetching settings:', err); } }, []); + // Fetch profile and settings on component mount useEffect(() => { fetchProfile(); fetchSettings(); }, [fetchProfile, fetchSettings]); + /** + * Displays a temporary message toast to the user. + * Message auto-dismisses after 3 seconds. + * + * @function showMessage + * @param {string} text - Message text to display + * @param {string} [type='success'] - Message type ('success' or 'error') + */ const showMessage = (text, type = 'success') => { setMessage({ text, type }); + // Auto-dismiss after 3 seconds setTimeout(() => setMessage({ text: '', type: '' }), 3000); }; + /** + * Handles profile update form submission. + * Sends updated profile data to API and shows feedback. + * + * @function handleProfileUpdate + * @async + * @param {React.FormEvent} e - Form submit event + */ const handleProfileUpdate = async (e) => { e.preventDefault(); try { const userId = getAuthenticatedUserId(); const response = await userAPI.updateUserProfile(userId, profile); + // Update local state with response data setProfile(response.data.user || response.data); setEditMode(false); showMessage(t('profileUpdatedSuccess'), 'success'); @@ -60,11 +145,20 @@ const UserProfilePage = () => { } }; + /** + * Handles settings update form submission. + * Sends updated settings data to API and shows feedback. + * + * @function handleSettingsUpdate + * @async + * @param {React.FormEvent} e - Form submit event + */ const handleSettingsUpdate = async (e) => { e.preventDefault(); try { const userId = getAuthenticatedUserId(); const response = await userAPI.updateUserSettings(userId, settings); + // Update local state with response data setSettings(response.data.settings || response.data); setEditSettingsMode(false); showMessage(t('settingsUpdatedSuccess'), 'success'); @@ -73,6 +167,7 @@ const UserProfilePage = () => { } }; + // Loading state UI if (loading) { return (
@@ -82,6 +177,7 @@ const UserProfilePage = () => { ); } + // Error state UI if (error) { return (
@@ -93,18 +189,23 @@ const UserProfilePage = () => { return (
+ {/* Page hero section */}
+ {/* Message toast for success/error feedback */} {message.text && (
{message.text}
)} + {/* Profile grid containing info and settings cards */}
+ {/* Profile information card with edit functionality */} + {/* Settings card with edit functionality */}
@@ -114,3 +215,4 @@ const UserProfilePage = () => { }; export default UserProfilePage; + From e85a532809c7fe08188a2e43da45cafb24bdd048 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:50:47 +0200 Subject: [PATCH 11/25] docs: add comprehensive documentation to LoadingSpinner3D.jsx --- src/components/ui/LoadingSpinner3D.jsx | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/components/ui/LoadingSpinner3D.jsx b/src/components/ui/LoadingSpinner3D.jsx index 1d13c8d2..dc66d05f 100644 --- a/src/components/ui/LoadingSpinner3D.jsx +++ b/src/components/ui/LoadingSpinner3D.jsx @@ -1,30 +1,84 @@ +/** + * @fileoverview 3D Loading Spinner Component using Three.js/React Three Fiber. + * + * Provides an animated 3D torus spinner for loading states. Uses React Three + * Fiber for WebGL rendering with @react-three/drei for convenient 3D primitives. + * The spinner features a continuously rotating torus shape. + * + * @module components/ui/LoadingSpinner3D + * @requires react + * @requires @react-three/fiber - React renderer for Three.js + * @requires @react-three/drei - Useful helpers for React Three Fiber + */ + import React, { useRef } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import { Torus } from '@react-three/drei'; +/** + * SpinningTorus Component + * + * Internal component that renders an animated torus mesh. + * Uses useFrame hook for animation loop integration. + * Rotates on both X and Y axes at different speeds for visual interest. + * + * @component + * @private + * @returns {React.ReactElement} A rotating torus 3D mesh + */ const SpinningTorus = () => { + // Ref to access the mesh for animation const meshRef = useRef(); + // Animation loop - updates rotation on each frame useFrame((_, delta) => { if (meshRef.current) { + // Rotate on X axis at 1x speed meshRef.current.rotation.x += delta * 1; + // Rotate on Y axis at 2x speed for asymmetric motion meshRef.current.rotation.y += delta * 2; } }); return ( + // Torus geometry: [radius, tube radius, radial segments, tubular segments] + {/* Indigo/purple color matching app theme */} ); }; +/** + * LoadingSpinner3D Component + * + * A 3D animated loading spinner using WebGL rendering. + * Displays a rotating torus shape with ambient and point lighting. + * + * @component + * @example + * // Basic usage + * + * + * @example + * // With custom className for positioning + * + * + * @param {Object} props - Component props + * @param {string} [props.className] - Optional CSS class name for the container + * @returns {React.ReactElement} A 3D canvas with animated spinner + */ const LoadingSpinner3D = ({ className }) => { return ( + // Container div with fixed dimensions for the 3D canvas
+ {/* Three.js canvas with positioned camera */} + {/* Ambient light for overall illumination */} + {/* Point light for highlights and depth */} + {/* Animated torus spinner */}
@@ -32,3 +86,4 @@ const LoadingSpinner3D = ({ className }) => { }; export default LoadingSpinner3D; + From 800ce1415d0d2dc0f29793eb3bd33ec360b663f9 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:52:07 +0200 Subject: [PATCH 12/25] docs: add comprehensive documentation to HomePage.jsx --- src/pages/HomePage.jsx | 72 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index abde2b99..62661648 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -1,3 +1,20 @@ +/** + * @fileoverview Home Page Component - Main landing page for the application. + * + * This is the primary entry page for users, featuring: + * - Hero section with search functionality + * - Quick navigation links to key features + * - Personalized place recommendations (for authenticated users) + * - Features overview section + * - Call-to-action section + * + * @module pages/HomePage + * @requires react + * @requires react-router-dom - For navigation links + * @requires ../api - API calls for places and recommendations + * @requires ../i18n - Translation support + */ + import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { placeAPI, recommendationAPI, getAuthenticatedUserId } from '../api'; @@ -11,33 +28,72 @@ import CTASection from '../components/home/CTASection'; import { useTranslation } from '../i18n'; import './HomePage.css'; +/** + * HomePage Component + * + * The main landing page providing search, recommendations, and navigation + * to key application features. Displays personalized content for authenticated + * users and a general overview for guests. + * + * @component + * @example + * // Usage in router + * } /> + * + * @returns {React.ReactElement} The home page with hero, search, and recommendations + */ const HomePage = () => { + // Translation function for internationalized text const { t } = useTranslation(); + + // Loading state for search operations const [loading, setLoading] = useState(false); + + // Current search query text const [searchQuery, setSearchQuery] = useState(''); + + // Search results from keyword search const [searchResults, setSearchResults] = useState([]); + + // Personalized recommendations for authenticated users const [featuredPlaces, setFeaturedPlaces] = useState([]); + + // Loading state for recommendations fetch const [loadingFeatured, setLoadingFeatured] = useState(true); + /** + * Fetches personalized recommendations for authenticated users. + * Uses default Athens coordinates as the reference point. + * Runs on component mount. + */ useEffect(() => { const fetchFeaturedPlaces = async () => { try { const userId = getAuthenticatedUserId(); + // Only fetch if user is authenticated if (userId) { setLoadingFeatured(true); const response = await recommendationAPI.getRecommendations(userId, { + // Default to Athens, Greece coordinates latitude: '37.9838', longitude: '23.7275', maxDistance: '100', }); setFeaturedPlaces(response.data.recommendations || []); } - } catch (error) { /* silent fail */ } + } catch (error) { /* silent fail - recommendations are optional */ } finally { setLoadingFeatured(false); } }; fetchFeaturedPlaces(); }, []); + /** + * Handles search form submission. + * Searches for places matching the query keywords. + * + * @param {React.FormEvent} e - Form submit event + */ const handleSearch = async (e) => { e.preventDefault(); + // Clear results if query is empty if (!searchQuery.trim()) { setSearchResults([]); return; } setLoading(true); try { @@ -47,16 +103,22 @@ const HomePage = () => { finally { setLoading(false); } }; + /** + * Clears the search query and results. + */ const clearSearch = () => { setSearchQuery(''); setSearchResults([]); }; return (
+ {/* Hero section with search form and quick links */} + {/* Search form */}
setSearchQuery(e.target.value)} className="search-input" data-cy="search-input" /> + {/* Clear button - shown when there's a query */} {searchQuery && (
+ {/* Quick navigation links to key features */}
{t('personalizedRecommendations')} {t('favouritesFeature')}
+ {/* Search results section - shown when results exist */} {searchResults.length > 0 && (
@@ -97,6 +161,7 @@ const HomePage = () => {
)} + {/* Empty state for no search results */} {searchResults.length === 0 && searchQuery && !loading && (
@@ -109,6 +174,7 @@ const HomePage = () => {
)} + {/* Featured/Recommended places carousel - shown when not searching */} {!searchQuery && featuredPlaces.length > 0 && (
@@ -128,10 +194,14 @@ const HomePage = () => {
)} + {/* Features overview section */} + + {/* Call-to-action section */}
); }; export default HomePage; + From 4f094869fbc18152ccc2b8737aa3766b83192ebd Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 22:54:16 +0200 Subject: [PATCH 13/25] docs: add comprehensive documentation to RecommendationsPage.jsx and App.jsx --- src/App.jsx | 60 ++++++++++++++++- src/pages/RecommendationsPage.jsx | 105 ++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/src/App.jsx b/src/App.jsx index f9981ff4..5aa8ef3e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,26 @@ +/** + * @fileoverview Root Application Component. + * + * This is the main application component that sets up the entire application + * structure including: + * - Context providers (Theme, Language, Toast) + * - React Router configuration + * - Layout structure (Header, Main, Footer, Bottom Navigation) + * - Authentication state management + * + * The component hierarchy ensures all child components have access to: + * - Theme context for light/dark mode + * - Language context for i18n translations + * - Toast context for notifications + * - Router context for navigation + * + * @module App + * @requires react + * @requires react-router-dom + * @requires ./i18n - Language provider + * @requires ./context - Theme provider + */ + import React, { useState, useEffect } from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import { LanguageProvider } from './i18n'; @@ -9,26 +32,51 @@ import ScrollToTop from './components/ScrollToTop.jsx'; import AppRoutes from './router/index.jsx'; import './App.css'; +/** + * App Component + * + * The root component that wraps the entire application with necessary + * providers and sets up the main layout structure. Tracks authentication + * state for conditional rendering of navigation elements. + * + * Provider hierarchy (outermost to innermost): + * 1. ThemeProvider - Dark/light mode + * 2. LanguageProvider - Internationalization + * 3. ToastProvider - Notification system + * 4. Router - Navigation + * + * @function App + * @returns {React.ReactElement} The complete application structure + */ function App() { + // Track user authentication state for conditional UI const [isAuthenticated, setIsAuthenticated] = useState(false); + /** + * Sets up authentication state tracking on mount. + * Listens for login/logout events and storage changes. + */ useEffect(() => { // Check for existing token on mount const token = localStorage.getItem('token'); setIsAuthenticated(!!token); - // Listen for login/logout events + // Event handlers for auth state changes const handleLogin = () => setIsAuthenticated(true); const handleLogout = () => setIsAuthenticated(false); + // Listen for custom login/logout events window.addEventListener('user:login', handleLogin); window.addEventListener('user:logout', handleLogout); + + // Listen for storage changes (handles logout from other tabs) window.addEventListener('storage', (e) => { if (e.key === 'token') { setIsAuthenticated(!!e.newValue); } }); + // Cleanup event listeners on unmount return () => { window.removeEventListener('user:login', handleLogin); window.removeEventListener('user:logout', handleLogout); @@ -36,17 +84,26 @@ function App() { }, []); return ( + // Theme provider for dark/light mode support + {/* Language provider for internationalization */} + {/* Toast provider for notification system */} + {/* React Router for client-side navigation */} + {/* Scroll restoration on route change */}
+ {/* Site header with navigation */}
+ {/* Main content area with routes */}
+ {/* Site footer */}
+ {/* Mobile bottom navigation bar */}
@@ -57,3 +114,4 @@ function App() { } export default App; + diff --git a/src/pages/RecommendationsPage.jsx b/src/pages/RecommendationsPage.jsx index f3990f78..965cbb93 100644 --- a/src/pages/RecommendationsPage.jsx +++ b/src/pages/RecommendationsPage.jsx @@ -1,3 +1,26 @@ +/** + * @fileoverview Recommendations Page Component for personalized place discovery. + * + * This page displays personalized place recommendations based on: + * - User's active preference profile categories + * - User's location (specified or detected) + * - Maximum distance filter + * - Sorting preferences (distance or rating) + * + * Features: + * - Location-based filtering with autocomplete + * - Distance radius slider + * - Sort by distance or rating + * - Loading skeleton animations + * - Error and empty state handling + * + * @module pages/RecommendationsPage + * @requires react + * @requires ../api - Recommendation API calls + * @requires ../i18n - Translation support + * @requires ../utils/geocoding - Location geocoding utilities + */ + import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { recommendationAPI, getAuthenticatedUserId } from '../api'; import { useTranslation } from '../i18n'; @@ -9,41 +32,100 @@ import RecommendationFilters from '../components/recommendations/RecommendationF import { forwardGeocode } from '../utils/geocoding'; import './RecommendationsPage.css'; +/** + * RecommendationsPage Component + * + * Displays personalized place recommendations with filtering and sorting options. + * Fetches recommendations based on user preferences and location. + * + * @component + * @example + * // Usage in router + * } /> + * + * @returns {React.ReactElement} The recommendations page with filters and results + */ const RecommendationsPage = () => { + // Translation function for internationalized text const { t } = useTranslation(); + + // Array of recommendation results const [recommendations, setRecommendations] = useState([]); + + // Loading state for API calls const [loading, setLoading] = useState(true); + + // Error message for API failures const [error, setError] = useState(null); + + // Info message from API (e.g., "No active profile") const [message, setMessage] = useState(null); + + // Toggle for showing/hiding filter panel const [showFilters, setShowFilters] = useState(false); + + // Current sort method: 'distance' or 'rating' const [sortBy, setSortBy] = useState('distance'); + + // Human-readable location name for display const [locationName, setLocationName] = useState('Athens, Greece'); + + // Filter parameters for API call const [filters, setFilters] = useState({ latitude: '37.9838', longitude: '23.7275', maxDistance: '50' }); + + // Pending max distance value (applied on filter submit) const [pendingMaxDistance, setPendingMaxDistance] = useState('50'); + /** + * Calculates the effective rating for a place. + * Computes average from reviews array or uses direct rating property. + * + * @param {Object} place - Place object + * @returns {number|null} Calculated rating or null if no rating available + */ const getEffectiveRating = (place) => { + // Check if place has reviews array if (place.reviews && Array.isArray(place.reviews)) { if (place.reviews.length > 0) { + // Calculate average from reviews return place.reviews.reduce((acc, r) => acc + (r.rating || 0), 0) / place.reviews.length; } return null; } + // Fallback to direct rating property return typeof place.rating === 'number' ? place.rating : null; }; + /** + * Memoized sorted recommendations based on current sort method. + * Sorts by distance or rating with null/undefined handling. + */ const sortedRecommendations = useMemo(() => { if (!recommendations.length) return recommendations; return [...recommendations].sort((a, b) => { + // Distance sort: closest first if (sortBy === 'distance') return (a.distance ?? Infinity) - (b.distance ?? Infinity); + + // Rating sort: highest rated first with null handling const rA = getEffectiveRating(a), rB = getEffectiveRating(b); const hasA = rA != null && !isNaN(rA), hasB = rB != null && !isNaN(rB); + + // Places with ratings come before those without if (hasA && !hasB) return -1; if (!hasA && hasB) return 1; + + // If neither has rating, fall back to distance if (!hasA && !hasB) return (a.distance ?? Infinity) - (b.distance ?? Infinity); + + // Both have ratings: highest first return rB - rA; }); }, [recommendations, sortBy]); + /** + * Fetches recommendations from API based on current filters. + * Updates recommendations, loading, error, and message states. + */ const fetchRecommendations = useCallback(async () => { setLoading(true); setError(null); setMessage(null); try { @@ -51,23 +133,37 @@ const RecommendationsPage = () => { const response = await recommendationAPI.getRecommendations(userId, filters); const recs = response.data.recommendations || []; setRecommendations(recs); + // Set message if no recommendations but API returned a message (e.g., "Set up preferences first") if (recs.length === 0 && response.data.message) setMessage(response.data.message); } catch (err) { + // Handle different error types if (err.message === 'User not authenticated') setError(t('notAuthenticated')); else if (err.response?.data?.message) setError(err.response.data.message); else setError(t('errorLoadingRecommendations')); } finally { setLoading(false); } }, [filters, t]); + // Fetch on mount and when filters change useEffect(() => { fetchRecommendations(); }, []); // eslint-disable-line useEffect(() => { fetchRecommendations(); }, [filters, fetchRecommendations]); + /** + * Handles location selection from autocomplete suggestions. + * Updates location name and filter coordinates. + * + * @param {Object} suggestion - Selected location suggestion with lat/lng + */ const handleLocationSelect = (suggestion) => { setLocationName(suggestion.displayName); setFilters(prev => ({ ...prev, latitude: suggestion.lat.toString(), longitude: suggestion.lng.toString() })); }; + /** + * Applies filter changes by geocoding the location name if needed. + * Updates filters with new coordinates and distance. + */ const handleApplyFilters = async () => { + // Check if location name needs geocoding if (locationName && !filters.latitude.includes(locationName)) { try { const result = await forwardGeocode(locationName); @@ -81,19 +177,24 @@ const RecommendationsPage = () => { return (
+ {/* Page hero section */}
+ {/* Filter controls component */} setLocationName(e.target.value)} onLocationSelect={handleLocationSelect} pendingMaxDistance={pendingMaxDistance} onDistanceChange={(e) => setPendingMaxDistance(e.target.value)} sortBy={sortBy} onSortChange={(e) => setSortBy(e.target.value)} onApplyFilters={handleApplyFilters} /> + {/* Results section with conditional rendering */}
{loading ? ( + // Loading state: show skeleton cards
) : error ? ( + // Error state: show error alert with retry button
{error} @@ -103,6 +204,7 @@ const RecommendationsPage = () => {
) : message ? ( + // Info state: API message (e.g., no active profile)
ℹ️

{message}

@@ -110,6 +212,7 @@ const RecommendationsPage = () => {
) : recommendations.length > 0 ? ( + // Success state: show recommendations grid <>

{recommendations.length} {recommendations.length === 1 ? 'Recommendation' : 'Recommendations'}

@@ -124,6 +227,7 @@ const RecommendationsPage = () => {
) : ( + // Empty state: no recommendations found
🔍

{t('noRecommendationsFound')}

@@ -137,3 +241,4 @@ const RecommendationsPage = () => { }; export default RecommendationsPage; + From 3e0a2e4cc0d4797149961145f71410206889073d Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:10:20 +0200 Subject: [PATCH 14/25] docs: add documentation to el/common.js, el/features.js, useCardTilt.js, LanguageContext.jsx --- src/hooks/useCardTilt.js | 11 +++++++++++ src/i18n/LanguageContext.jsx | 7 +++++++ src/i18n/locales/el/common.js | 6 ++++++ src/i18n/locales/el/features.js | 6 ++++++ 4 files changed, 30 insertions(+) diff --git a/src/hooks/useCardTilt.js b/src/hooks/useCardTilt.js index 193dbbd6..63ad8721 100644 --- a/src/hooks/useCardTilt.js +++ b/src/hooks/useCardTilt.js @@ -1,5 +1,16 @@ +/** + * @fileoverview Card tilt effect hook for 3D hover interactions. + * Provides perspective-based rotation on mouse movement. + * @module hooks/useCardTilt + */ + import { useEffect, useRef, useState } from 'react'; +/** + * Hook that provides a 3D tilt effect for card elements. + * @param {Object} options - Configuration options + * @returns {Object} ref and style to apply to the element + */ const useCardTilt = ({ maxTilt = 15, perspective = 1000, scale = 1.05 } = {}) => { const ref = useRef(null); const [style, setStyle] = useState({}); diff --git a/src/i18n/LanguageContext.jsx b/src/i18n/LanguageContext.jsx index 93b454ef..0bd2248a 100644 --- a/src/i18n/LanguageContext.jsx +++ b/src/i18n/LanguageContext.jsx @@ -1,6 +1,13 @@ +/** + * @fileoverview Language context provider for i18n support. + * Provides translation functions and language switching (English/Greek). + * @module i18n/LanguageContext + */ + import React, { createContext, useState, useContext, useEffect } from 'react'; import { translations } from './translations'; +// React context for language state const LanguageContext = createContext(); export const useTranslation = () => { diff --git a/src/i18n/locales/el/common.js b/src/i18n/locales/el/common.js index 34e476a6..e58eb6be 100644 --- a/src/i18n/locales/el/common.js +++ b/src/i18n/locales/el/common.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Greek (el) translations for common UI elements. + * Contains translations for: header, home page, auth, and validation messages. + * @module i18n/locales/el/common + */ + // Common/Header translations - Greek export const common = { home: 'Αρχική', recommendations: 'Προτάσεις', navigation: 'Πλοήγηση', diff --git a/src/i18n/locales/el/features.js b/src/i18n/locales/el/features.js index 6a58d9ad..5c32f090 100644 --- a/src/i18n/locales/el/features.js +++ b/src/i18n/locales/el/features.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Greek (el) translations for feature pages. + * Contains translations for: navigation, recommendations, place details, categories, footer. + * @module i18n/locales/el/features + */ + // Navigation, Recommendations, Places translations - Greek export const navigationPage = { from: 'Από', to: 'Προς', getRoute: 'Εύρεση Διαδρομής', navigationSubtitle: 'Βρείτε τη διαδρομή προς τον προορισμό σας', From 4efc9165d81aef816a132a26fd739a4ef28d8ff6 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:11:10 +0200 Subject: [PATCH 15/25] docs: add documentation to i18n locale files and SignupPage --- src/i18n/locales/el/pages.js | 6 ++++++ src/i18n/locales/en/common.js | 6 ++++++ src/i18n/locales/en/pages.js | 6 ++++++ src/pages/SignupPage.jsx | 7 +++++++ 4 files changed, 25 insertions(+) diff --git a/src/i18n/locales/el/pages.js b/src/i18n/locales/el/pages.js index 1dc9dd75..24b8f0cd 100644 --- a/src/i18n/locales/el/pages.js +++ b/src/i18n/locales/el/pages.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Greek (el) translations for user pages. + * Contains: profile, preferences, and favourites page strings. + * @module i18n/locales/el/pages + */ + // User/Profile/Preferences translations - Greek export const userPages = { userProfile: 'Προφίλ Χρήστη', editProfile: 'Επεξεργασία Προφίλ', saveChanges: 'Αποθήκευση Αλλαγών', diff --git a/src/i18n/locales/en/common.js b/src/i18n/locales/en/common.js index fb8f80d3..4a653322 100644 --- a/src/i18n/locales/en/common.js +++ b/src/i18n/locales/en/common.js @@ -1,3 +1,9 @@ +/** + * @fileoverview English (en) translations for common UI elements. + * Contains translations for: header, home page, auth, and validation messages. + * @module i18n/locales/en/common + */ + // Common/Header translations export const common = { home: 'Home', recommendations: 'Recommendations', navigation: 'Navigation', diff --git a/src/i18n/locales/en/pages.js b/src/i18n/locales/en/pages.js index 07be5eef..df0af11f 100644 --- a/src/i18n/locales/en/pages.js +++ b/src/i18n/locales/en/pages.js @@ -1,3 +1,9 @@ +/** + * @fileoverview English (en) translations for user pages. + * Contains: profile, preferences, and favourites page strings. + * @module i18n/locales/en/pages + */ + // User/Profile/Preferences translations export const userPages = { userProfile: 'User Profile', editProfile: 'Edit Profile', saveChanges: 'Save Changes', diff --git a/src/pages/SignupPage.jsx b/src/pages/SignupPage.jsx index 50217706..55cf0117 100644 --- a/src/pages/SignupPage.jsx +++ b/src/pages/SignupPage.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Signup page for new user registration. + * Uses Formik for form handling with validation. + * @module pages/SignupPage + */ + import React, { useState } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { Formik, Form, Field, ErrorMessage } from 'formik'; @@ -7,6 +13,7 @@ import { useTranslation } from '../i18n'; import { Button, Alert, EmailField, PasswordField } from '../components/ui'; import '../styles/Auth.css'; +/** Signup page component with form validation and API integration */ const SignupPage = () => { const navigate = useNavigate(); const [error, setError] = useState(''); From d59e5f600652d335b9aa023555370690374463b1 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:11:49 +0200 Subject: [PATCH 16/25] docs: add file headers to RouteForm, ProfileInfoCard, ReviewsSection --- src/components/navigation/RouteForm.jsx | 6 ++++++ src/components/place/ReviewsSection.jsx | 6 ++++++ src/components/profile/ProfileInfoCard.jsx | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/components/navigation/RouteForm.jsx b/src/components/navigation/RouteForm.jsx index b635048a..e968782b 100644 --- a/src/components/navigation/RouteForm.jsx +++ b/src/components/navigation/RouteForm.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Route planning form component. + * Used in NavigationPage for entering origin/destination and transport mode. + * @module components/navigation/RouteForm + */ + import React from 'react'; import { Button, Icon, Spinner, AutocompleteInput } from '../ui'; import { searchLocations } from '../../utils/geocoding'; diff --git a/src/components/place/ReviewsSection.jsx b/src/components/place/ReviewsSection.jsx index 7f1163b2..5d89020c 100644 --- a/src/components/place/ReviewsSection.jsx +++ b/src/components/place/ReviewsSection.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Reviews section component for place details. + * Displays review list and review submission form. + * @module components/place/ReviewsSection + */ + import React from 'react'; import { Button, Icon } from '../ui'; diff --git a/src/components/profile/ProfileInfoCard.jsx b/src/components/profile/ProfileInfoCard.jsx index ec7eaf33..4c9862e3 100644 --- a/src/components/profile/ProfileInfoCard.jsx +++ b/src/components/profile/ProfileInfoCard.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview User profile information card component. + * Displays and allows editing of personal information. + * @module components/profile/ProfileInfoCard + */ + import React from 'react'; import { Button, Icon } from '../ui'; From 1a314962749d93cff388e10b56244a89d066d25d Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:12:30 +0200 Subject: [PATCH 17/25] docs: add file headers to usePlaceActions, PreferenceForm, router --- src/components/preferences/PreferenceForm.jsx | 8 ++++ src/hooks/usePlaceActions.js | 6 +++ src/router/index.jsx | 44 +++++++++++-------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/components/preferences/PreferenceForm.jsx b/src/components/preferences/PreferenceForm.jsx index 89be5ac1..039d172e 100644 --- a/src/components/preferences/PreferenceForm.jsx +++ b/src/components/preferences/PreferenceForm.jsx @@ -1,8 +1,16 @@ +/** + * @fileoverview Preference profile form component. + * Form for creating/editing preference profiles with category selection. + * @module components/preferences/PreferenceForm + */ + import React from 'react'; import { Button, Icon } from '../ui'; +// Available place categories for preferences const AVAILABLE_CATEGORIES = ['MUSEUM', 'RESTAURANT', 'BEACH', 'CULTURE', 'PARK', 'NIGHTLIFE', 'SHOPPING', 'SPORTS']; +// Emoji icons for each category const CATEGORY_ICONS = { 'MUSEUM': '🏛️', 'RESTAURANT': '🍽️', 'BEACH': '🏖️', 'CULTURE': '🎭', 'PARK': '🌳', 'NIGHTLIFE': '🌙', 'SHOPPING': '🛍️', 'SPORTS': '⚽', diff --git a/src/hooks/usePlaceActions.js b/src/hooks/usePlaceActions.js index cb3f157f..9052cc68 100644 --- a/src/hooks/usePlaceActions.js +++ b/src/hooks/usePlaceActions.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Place action handlers hook. + * Manages review, report, favourite, and dislike interactions. + * @module hooks/usePlaceActions + */ + import { placeAPI, favouriteAPI, getAuthenticatedUserId } from '../api'; /** diff --git a/src/router/index.jsx b/src/router/index.jsx index a7c5ff52..0301343d 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Application router configuration. + * Defines all routes including public and protected routes. + * @module router/index + */ + import React from 'react'; import { Routes, Route } from 'react-router-dom'; import ProtectedRoute from './ProtectedRoute.jsx'; @@ -24,55 +30,55 @@ const AppRoutes = () => { } /> } /> } /> - + {/* Protected Routes - Require Authentication */} - - } + } /> - - } + } /> - - } + } /> - - } + } /> - - } + } /> - - } + } /> ); From dd077927f63a5e6abf84ccbc2bfd42bcb74060c4 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:13:26 +0200 Subject: [PATCH 18/25] docs: add file headers to RecommendationFilters, SettingsCard, ProfileCard, LoginPage --- src/components/preferences/ProfileCard.jsx | 6 ++++++ src/components/profile/SettingsCard.jsx | 6 ++++++ src/components/recommendations/RecommendationFilters.jsx | 6 ++++++ src/pages/LoginPage.jsx | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/src/components/preferences/ProfileCard.jsx b/src/components/preferences/ProfileCard.jsx index d7836b96..9c08c6e9 100644 --- a/src/components/preferences/ProfileCard.jsx +++ b/src/components/preferences/ProfileCard.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Preference profile card component. + * Displays profile info with activate, edit, and delete actions. + * @module components/preferences/ProfileCard + */ + import React from 'react'; import { Button, Icon } from '../ui'; import { CATEGORY_ICONS } from './PreferenceForm'; diff --git a/src/components/profile/SettingsCard.jsx b/src/components/profile/SettingsCard.jsx index 0cebe6cc..e9263ab0 100644 --- a/src/components/profile/SettingsCard.jsx +++ b/src/components/profile/SettingsCard.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview User settings card component. + * Displays and allows editing of notification settings. + * @module components/profile/SettingsCard + */ + import React from 'react'; import { Button, Icon } from '../ui'; diff --git a/src/components/recommendations/RecommendationFilters.jsx b/src/components/recommendations/RecommendationFilters.jsx index 80ae27ca..84c2f05b 100644 --- a/src/components/recommendations/RecommendationFilters.jsx +++ b/src/components/recommendations/RecommendationFilters.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Recommendation filters component. + * Location, distance, and sort controls for recommendations page. + * @module components/recommendations/RecommendationFilters + */ + import React from 'react'; import { Button, Icon, AutocompleteInput } from '../ui'; import { searchLocations } from '../../utils/geocoding'; diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx index 429a0742..9a4d822b 100644 --- a/src/pages/LoginPage.jsx +++ b/src/pages/LoginPage.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Login page for user authentication. + * Uses Formik for form handling with demo credentials support. + * @module pages/LoginPage + */ + import React, { useState } from 'react'; import { useNavigate, Link, useLocation } from 'react-router-dom'; import { Formik, Form } from 'formik'; From 638a62cf9350be29ced64cd6019883a3d4f211e6 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:14:29 +0200 Subject: [PATCH 19/25] docs: add file headers to FavouriteListItem, FeaturesSection, CTASection, useFavourites --- src/components/favourites/FavouriteListItem.jsx | 6 ++++++ src/components/home/CTASection.jsx | 6 ++++++ src/components/home/FeaturesSection.jsx | 6 ++++++ src/hooks/useFavourites.js | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/src/components/favourites/FavouriteListItem.jsx b/src/components/favourites/FavouriteListItem.jsx index 02c458c0..1bd8b4e4 100644 --- a/src/components/favourites/FavouriteListItem.jsx +++ b/src/components/favourites/FavouriteListItem.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Favourite list item component. + * Renders individual favourite/disliked place with remove action. + * @module components/favourites/FavouriteListItem + */ + import React from 'react'; import PlaceCard from '../PlaceCard.jsx'; import { Button, Icon } from '../ui'; diff --git a/src/components/home/CTASection.jsx b/src/components/home/CTASection.jsx index 35b83ab0..1ef93b53 100644 --- a/src/components/home/CTASection.jsx +++ b/src/components/home/CTASection.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Homepage call-to-action section component. + * Displays CTA banner with link to explore destinations. + * @module components/home/CTASection + */ + import React from 'react'; import { Link } from 'react-router-dom'; import { Button, Icon } from '../ui'; diff --git a/src/components/home/FeaturesSection.jsx b/src/components/home/FeaturesSection.jsx index 5b4f8800..37babcfa 100644 --- a/src/components/home/FeaturesSection.jsx +++ b/src/components/home/FeaturesSection.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Homepage features section component. + * Displays feature highlights: recommendations, reviews, favourites. + * @module components/home/FeaturesSection + */ + import React from 'react'; /** diff --git a/src/hooks/useFavourites.js b/src/hooks/useFavourites.js index 8d4c1169..8017c2a1 100644 --- a/src/hooks/useFavourites.js +++ b/src/hooks/useFavourites.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Favourites management hook. + * Handles fetching and managing favourite/disliked places. + * @module hooks/useFavourites + */ + import { useState, useEffect, useCallback } from 'react'; import { favouriteAPI, getAuthenticatedUserId } from '../api'; From dafad4654025e2ffe838522408b2438d4731b8f0 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:15:18 +0200 Subject: [PATCH 20/25] docs: add file headers to usePlaceDetails, useAutocomplete, Toast --- src/components/ui/Toast.jsx | 6 ++++++ src/hooks/useAutocomplete.js | 6 ++++++ src/hooks/usePlaceDetails.js | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/components/ui/Toast.jsx b/src/components/ui/Toast.jsx index 87d3281f..5b7898bf 100644 --- a/src/components/ui/Toast.jsx +++ b/src/components/ui/Toast.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Toast notification system. + * Provides ToastProvider, useToast hook, and Toast component. + * @module components/ui/Toast + */ + import React, { useState, createContext, useContext, useCallback } from 'react'; import PropTypes from 'prop-types'; import Icon from './Icon'; diff --git a/src/hooks/useAutocomplete.js b/src/hooks/useAutocomplete.js index bc31301f..4feb9ebd 100644 --- a/src/hooks/useAutocomplete.js +++ b/src/hooks/useAutocomplete.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Autocomplete input hook. + * Manages debounced search, suggestions, and keyboard navigation. + * @module hooks/useAutocomplete + */ + import { useState, useRef, useCallback, useEffect } from 'react'; /** diff --git a/src/hooks/usePlaceDetails.js b/src/hooks/usePlaceDetails.js index c1adf859..1aa9f154 100644 --- a/src/hooks/usePlaceDetails.js +++ b/src/hooks/usePlaceDetails.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Place details hook. + * Fetches place info, reviews, and favourite status. + * @module hooks/usePlaceDetails + */ + import { useState, useEffect, useCallback } from 'react'; import { placeAPI, favouriteAPI, getAuthenticatedUserId } from '../api'; From 20a33a420c4e80a5c59cc3e5a04934b58d374e9e Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:16:15 +0200 Subject: [PATCH 21/25] docs: add file headers to PlaceActionsBar, RouteResults, TravelGlobe, AutocompleteInput --- src/components/3d/TravelGlobe.jsx | 6 ++++++ src/components/navigation/RouteResults.jsx | 6 ++++++ src/components/place/PlaceActionsBar.jsx | 6 ++++++ src/components/ui/AutocompleteInput.jsx | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/src/components/3d/TravelGlobe.jsx b/src/components/3d/TravelGlobe.jsx index 07cca5b6..2b6f36fb 100644 --- a/src/components/3d/TravelGlobe.jsx +++ b/src/components/3d/TravelGlobe.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview 3D Travel Globe component. + * Interactive globe with orbiting satellites using Three.js. + * @module components/3d/TravelGlobe + */ + import React, { useRef } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import { Icosahedron, OrbitControls, Stars } from '@react-three/drei'; diff --git a/src/components/navigation/RouteResults.jsx b/src/components/navigation/RouteResults.jsx index aa9c3ccb..233fcaba 100644 --- a/src/components/navigation/RouteResults.jsx +++ b/src/components/navigation/RouteResults.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Route results display component. + * Shows route stats, waypoints, warnings, and interactive map. + * @module components/navigation/RouteResults + */ + import React from 'react'; import { Icon } from '../ui'; import RouteMap from '../RouteMap'; diff --git a/src/components/place/PlaceActionsBar.jsx b/src/components/place/PlaceActionsBar.jsx index 3b0c5130..d17b3809 100644 --- a/src/components/place/PlaceActionsBar.jsx +++ b/src/components/place/PlaceActionsBar.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Place action buttons bar component. + * Favourite, dislike, and navigate actions for place details. + * @module components/place/PlaceActionsBar + */ + import React from 'react'; import { Link } from 'react-router-dom'; diff --git a/src/components/ui/AutocompleteInput.jsx b/src/components/ui/AutocompleteInput.jsx index 39b6cadf..80fe019a 100644 --- a/src/components/ui/AutocompleteInput.jsx +++ b/src/components/ui/AutocompleteInput.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Autocomplete input component. + * Input with debounced search and dropdown suggestions. + * @module components/ui/AutocompleteInput + */ + import React from 'react'; import PropTypes from 'prop-types'; import Icon from './Icon'; From c3aac1a9042af33f7c7be7f9ef066c40471dec6d Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:16:55 +0200 Subject: [PATCH 22/25] docs: add file header to BottomNavigation --- src/components/ui/BottomNavigation.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/ui/BottomNavigation.jsx b/src/components/ui/BottomNavigation.jsx index aa2682ed..472ebf74 100644 --- a/src/components/ui/BottomNavigation.jsx +++ b/src/components/ui/BottomNavigation.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Mobile bottom navigation component. + * Shows on small screens with quick access to main app sections. + * @module components/ui/BottomNavigation + */ + import React from 'react'; import { NavLink, useLocation } from 'react-router-dom'; import PropTypes from 'prop-types'; From b3fed56b0230733fa638a5839c69e77e0010e087 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:17:39 +0200 Subject: [PATCH 23/25] docs: add file header to ReportModal --- src/components/place/ReportModal.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/place/ReportModal.jsx b/src/components/place/ReportModal.jsx index 725773b0..150c66d2 100644 --- a/src/components/place/ReportModal.jsx +++ b/src/components/place/ReportModal.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Report modal for place issues. + * Allows users to submit reports about incorrect/inappropriate listings. + * @module components/place/ReportModal + */ + import React from 'react'; import { Button, Icon } from '../ui'; From 87b2c7d8fe745c8a42b5d85c3c61453f4bcb45c3 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:24:06 +0200 Subject: [PATCH 24/25] docs: add file headers to vite.config, Carousel3D, Footer, Header, UserDropdown, Input, PhotoGallery --- src/components/Carousel3D.jsx | 6 ++++++ src/components/Footer.jsx | 6 ++++++ src/components/Header.jsx | 6 ++++++ src/components/UserDropdown.jsx | 6 ++++++ src/components/ui/Input.jsx | 12 +++++++++--- src/components/ui/PhotoGallery.jsx | 6 ++++++ vite.config.js | 6 ++++++ 7 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/components/Carousel3D.jsx b/src/components/Carousel3D.jsx index 25173fd7..60896744 100644 --- a/src/components/Carousel3D.jsx +++ b/src/components/Carousel3D.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview 3D Coverflow-style carousel component. + * Features smooth animations, drag support, and keyboard navigation. + * @module components/Carousel3D + */ + import React, { useState, useEffect, useRef, useCallback } from 'react'; import PropTypes from 'prop-types'; import Icon from './ui/Icon'; diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index a90fb81c..5d1565a6 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Site footer component. + * Contains branding, navigation links, and copyright. + * @module components/Footer + */ + import React from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from '../i18n'; diff --git a/src/components/Header.jsx b/src/components/Header.jsx index b24d4db9..582d18de 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Site header component. + * Professional navigation with glassmorphism, user dropdown, and scroll effects. + * @module components/Header + */ + import React, { useState, useEffect } from 'react'; import { Link, useLocation } from 'react-router-dom'; import { useAuth } from '../hooks'; diff --git a/src/components/UserDropdown.jsx b/src/components/UserDropdown.jsx index 73ea0f1c..461ab33c 100644 --- a/src/components/UserDropdown.jsx +++ b/src/components/UserDropdown.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview User dropdown menu component. + * Avatar with dropdown for profile, preferences, and logout. + * @module components/UserDropdown + */ + import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; import { useAuth } from '../hooks'; diff --git a/src/components/ui/Input.jsx b/src/components/ui/Input.jsx index 86470608..e7c37f04 100644 --- a/src/components/ui/Input.jsx +++ b/src/components/ui/Input.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Form input components. + * Includes Input, Textarea, Select, and FormGroup components. + * @module components/ui/Input + */ + import React from 'react'; import PropTypes from 'prop-types'; import '../../styles/components.css'; @@ -16,7 +22,7 @@ const Input = React.forwardRef(({ const baseClass = 'form-input'; const sizeClass = size !== 'base' ? `form-input-${size}` : ''; const errorClass = error ? 'error' : ''; - + const classes = [ baseClass, sizeClass, @@ -57,7 +63,7 @@ export const Textarea = React.forwardRef(({ }, ref) => { const baseClass = 'form-textarea'; const errorClass = error ? 'error' : ''; - + const classes = [ baseClass, errorClass, @@ -97,7 +103,7 @@ export const Select = React.forwardRef(({ const baseClass = 'form-select'; const sizeClass = size !== 'base' ? `form-select-${size}` : ''; const errorClass = error ? 'error' : ''; - + const classes = [ baseClass, sizeClass, diff --git a/src/components/ui/PhotoGallery.jsx b/src/components/ui/PhotoGallery.jsx index 0c6378a4..1e3faf8f 100644 --- a/src/components/ui/PhotoGallery.jsx +++ b/src/components/ui/PhotoGallery.jsx @@ -1,3 +1,9 @@ +/** + * @fileoverview Photo gallery component with lightbox modal. + * Category-based gallery using Unsplash images. + * @module components/ui/PhotoGallery + */ + import React, { useState } from 'react'; import PropTypes from 'prop-types'; import Icon from './Icon'; diff --git a/vite.config.js b/vite.config.js index 7a6af65e..0778fd71 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,3 +1,9 @@ +/** + * @fileoverview Vite configuration for React frontend. + * Configures dev server port, React plugin, and build output. + * @module vite.config + */ + import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; From 2aa0fd2f0bba2a1ed85413e649b9d6df93559613 Mon Sep 17 00:00:00 2001 From: Giannis Fraidakis Date: Thu, 25 Dec 2025 23:31:43 +0200 Subject: [PATCH 25/25] docs: add comprehensive inline comments to improve comment density --- cypress/e2e/auth_login_logout.cy.js | 34 +++++++++++++++++++++- cypress/e2e/auth_validation.cy.js | 28 +++++++++++++++++- src/components/navigation/RouteForm.jsx | 8 +++++ src/components/navigation/RouteResults.jsx | 8 +++++ src/components/place/ReviewsSection.jsx | 3 ++ src/components/profile/SettingsCard.jsx | 5 ++++ src/components/ui/PhotoGallery.jsx | 14 ++++++++- src/pages/LoginPage.jsx | 17 +++++++++++ src/pages/SignupPage.jsx | 17 ++++++++++- 9 files changed, 130 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/auth_login_logout.cy.js b/cypress/e2e/auth_login_logout.cy.js index fb515d32..c768650b 100644 --- a/cypress/e2e/auth_login_logout.cy.js +++ b/cypress/e2e/auth_login_logout.cy.js @@ -1,18 +1,32 @@ /** - * E2E Tests for Login & Logout Flows + * @fileoverview E2E Tests for Login & Logout Flows + * Tests authentication workflow, session persistence, and protected routes. * Split from auth_happy_unhappy.cy.js for file size optimization + * @module cypress/e2e/auth_login_logout */ + import { visitLogin, expectUrlToEqual, expectUrlToContain, fillLoginForm, submitForm, logMessage } from '../support/helpers'; +/** + * Test suite for authentication flows. + * Covers login, logout, session persistence, and route protection. + */ describe('Login & Logout Flows', () => { + // Clear state before each test for isolation beforeEach(() => { cy.clearLocalStorage(); cy.clearCookies(); }); + /** + * Login Page Tests + * Verify login form renders and authentication succeeds with valid credentials. + */ describe('Login Page', () => { + // Test: Page loads correctly it('should load the login page', () => { visitLogin(); cy.contains(/login/i).should('be.visible'); }); + // Test: Login with custom test user fixture it('should successfully log in with valid credentials', () => { cy.loginAsTestUser('validUser'); expectUrlToEqual('/'); @@ -20,6 +34,7 @@ describe('Login & Logout Flows', () => { logMessage('✅ Successfully logged in with test user'); }); + // Test: Login with hardcoded demo credentials it('should log in with demo user credentials', () => { cy.login('user1@example.com', 'password123'); cy.url().should('not.include', '/login'); @@ -28,7 +43,12 @@ describe('Login & Logout Flows', () => { }); }); + /** + * Logout Tests + * Verify logout clears session and redirects appropriately. + */ describe('Logout', () => { + // Test: Programmatic logout it('should successfully log out', () => { cy.loginAsTestUser('validUser'); cy.url().should('not.include', '/login'); @@ -37,6 +57,7 @@ describe('Login & Logout Flows', () => { logMessage('✅ Successfully logged out'); }); + // Test: Logout via UI dropdown button it('should log out via UI logout button', () => { cy.loginAsTestUser('validUser'); cy.get('[data-cy="user-dropdown-trigger"]').should('be.visible').click(); @@ -47,7 +68,12 @@ describe('Login & Logout Flows', () => { }); }); + /** + * Session Persistence Tests + * Verify session survives page reloads and data is stored correctly. + */ describe('Session Persistence', () => { + // Test: Session survives reload it('should maintain session after page reload', () => { cy.loginAsTestUser('validUser'); expectUrlToEqual('/'); @@ -57,6 +83,7 @@ describe('Login & Logout Flows', () => { logMessage('✅ Session persists after page reload'); }); + // Test: User data in localStorage is correct it('should restore user data from localStorage', () => { cy.loginAsTestUser('validUser'); cy.window().its('localStorage.user').should('exist'); @@ -69,7 +96,12 @@ describe('Login & Logout Flows', () => { }); }); + /** + * Protected Route Tests + * Verify authenticated users can access protected routes. + */ describe('Protected Routes', () => { + // Test: Authenticated access to profile page it('should allow access to protected route when authenticated', () => { cy.loginAsTestUser('validUser'); cy.visit('/profile'); diff --git a/cypress/e2e/auth_validation.cy.js b/cypress/e2e/auth_validation.cy.js index 6a90c146..c1001c2e 100644 --- a/cypress/e2e/auth_validation.cy.js +++ b/cypress/e2e/auth_validation.cy.js @@ -1,13 +1,26 @@ /** - * E2E Tests for Authentication Validation Errors + * @fileoverview E2E Tests for Authentication Validation Errors + * Tests form validation, error messages, and protected route access. * Split from auth_happy_unhappy.cy.js for file size optimization + * @module cypress/e2e/auth_validation */ + import { visitLogin, visitSignup, expectUrlToContain, fillLoginForm, fillSignupForm, submitForm, getRandomEmail, logMessage } from '../support/helpers'; +/** + * Test suite for authentication validation. + * Covers signup validation, login errors, and route protection. + */ describe('Authentication Validation Errors', () => { + // Clear state before each test for isolation beforeEach(() => { cy.clearLocalStorage(); cy.clearCookies(); }); + /** + * Signup Validation Tests + * Tests email format, password strength, and required fields. + */ describe('Signup Validation Errors', () => { + // Test: Invalid email format validation it('should show email validation error for invalid email format', () => { cy.intercept('POST', '**/auth/signup').as('signupRequest'); visitSignup(); @@ -18,6 +31,7 @@ describe('Authentication Validation Errors', () => { cy.window().its('localStorage.token').should('not.exist'); }); + // Test: Weak password validation it('should show password strength error for weak password', () => { visitSignup(); fillSignupForm({ name: 'Test User', email: getRandomEmail(), password: 'weak', confirmPassword: 'weak' }); @@ -26,6 +40,7 @@ describe('Authentication Validation Errors', () => { expectUrlToContain('/signup'); }); + // Test: Password confirmation mismatch it('should show error for mismatched password confirmation', () => { visitSignup(); cy.get('[data-cy="input-name"]').clear().type('Test User'); @@ -37,6 +52,7 @@ describe('Authentication Validation Errors', () => { expectUrlToContain('/signup'); }); + // Test: Required fields validation it('should show required field errors for empty form submission', () => { visitSignup(); submitForm(); @@ -61,7 +77,12 @@ describe('Authentication Validation Errors', () => { }); }); + /** + * Login Authentication Error Tests + * Tests wrong credentials, required fields, and invalid email. + */ describe('Login Authentication Errors', () => { + // Test: Invalid credentials returns auth error it('should show auth error for incorrect credentials', () => { cy.intercept('POST', '**/auth/login', { statusCode: 401, body: { success: false, message: 'Invalid credentials' } }).as('login'); visitLogin(); @@ -88,7 +109,12 @@ describe('Authentication Validation Errors', () => { }); }); + /** + * Protected Route Access Control Tests + * Verifies unauthenticated users are redirected to login. + */ describe('Protected Route Access Control - Unauthenticated', () => { + // Routes requiring authentication const protectedRoutes = [ { path: '/recommendations', name: 'Recommendations' }, { path: '/profile', name: 'User Profile' }, diff --git a/src/components/navigation/RouteForm.jsx b/src/components/navigation/RouteForm.jsx index e968782b..91d39eb7 100644 --- a/src/components/navigation/RouteForm.jsx +++ b/src/components/navigation/RouteForm.jsx @@ -21,11 +21,13 @@ const RouteForm = ({ error, onSubmit, }) => { + /** Handle input field changes and update form state */ const handleInputChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); }; + /** Map transport mode to display icon */ const getTransportIcon = (mode) => { const icons = { 'WALKING': '🚶', 'DRIVING': '🚗', 'PUBLIC_TRANSPORT': '🚌' }; return icons[mode] || '🗺️'; @@ -38,7 +40,9 @@ const RouteForm = ({

{t('calculateRoute')}

+ {/* Route form with autocomplete inputs */}
+ {/* Start location section */}
📍 @@ -67,6 +71,7 @@ const RouteForm = ({
+ {/* Destination location section */}
🎯 @@ -89,6 +94,7 @@ const RouteForm = ({
+ {/* Transport mode selection */}
@@ -110,11 +116,13 @@ const RouteForm = ({
+ {/* Submit button with loading state */} + {/* Error display */} {error && (
diff --git a/src/components/navigation/RouteResults.jsx b/src/components/navigation/RouteResults.jsx index 233fcaba..a3761dc6 100644 --- a/src/components/navigation/RouteResults.jsx +++ b/src/components/navigation/RouteResults.jsx @@ -23,11 +23,13 @@ const RouteResults = ({ routeGeometry, routeError, }) => { + /** Map transport mode to display icon */ const getTransportIcon = (mode) => { const icons = { 'WALKING': '🚶', 'DRIVING': '🚗', 'PUBLIC_TRANSPORT': '🚌' }; return icons[mode] || '🗺️'; }; + /** Format duration in hours and minutes */ const formatDuration = (minutes) => { if (minutes < 60) return `${Math.round(minutes)} ${t('minutes')}`; const hours = Math.floor(minutes / 60); @@ -35,6 +37,7 @@ const RouteResults = ({ return `${hours}h ${mins}m`; }; + /** Render location name with loading state and coordinates */ const renderLocationName = (name, isLoading, lat, lng) => { if (isLoading) { return ( @@ -57,6 +60,7 @@ const RouteResults = ({ ); }; + // Empty state when no route calculated yet if (!routeData) { return (
@@ -74,6 +78,7 @@ const RouteResults = ({

{t('routeResults')}

+ {/* Route statistics cards */}
📏
@@ -104,6 +109,7 @@ const RouteResults = ({
+ {/* Start and end point markers */}
A @@ -115,6 +121,7 @@ const RouteResults = ({
+ {/* Route warnings (ferry required, route not possible) */} {routeError && (
{routeError.includes('⛴️') ? '⛴️' : '⚠️'}
@@ -129,6 +136,7 @@ const RouteResults = ({
)} + {/* Interactive map with route polyline */} { + /** Generate star display string from numeric rating */ const getRatingStars = (rating) => { return '★'.repeat(Math.floor(rating)) + '☆'.repeat(5 - Math.floor(rating)); }; @@ -36,6 +37,7 @@ const ReviewsSection = ({
+ {/* Review submission form - toggleable */} {showReviewForm && (
@@ -74,6 +76,7 @@ const ReviewsSection = ({ )} + {/* Reviews list with empty state */}
{reviews.length > 0 ? ( reviews.map((review, index) => ( diff --git a/src/components/profile/SettingsCard.jsx b/src/components/profile/SettingsCard.jsx index e9263ab0..1bee7a14 100644 --- a/src/components/profile/SettingsCard.jsx +++ b/src/components/profile/SettingsCard.jsx @@ -11,6 +11,7 @@ import { Button, Icon } from '../ui'; * Settings card for editing/displaying user settings */ const SettingsCard = ({ t, settings, setSettings, editMode, setEditMode, onSubmit }) => { + // Render settings card with edit/display modes return (
@@ -26,8 +27,10 @@ const SettingsCard = ({ t, settings, setSettings, editMode, setEditMode, onSubmi )}
+ {/* Edit mode: form with toggle switches */} {editMode ? (
+ {/* Email notifications toggle */}