diff --git a/frontend/src/axiosClient.js b/frontend/src/axiosClient.js index cecd798..8cf56ef 100644 --- a/frontend/src/axiosClient.js +++ b/frontend/src/axiosClient.js @@ -1,7 +1,7 @@ import axios from 'axios'; const axiosClient = axios.create({ - baseURL: 'https://sponge-climbing-adder.ngrok-free.app/api', + baseURL: 'https://legible-freely-wren.ngrok-free.app/api', }); axiosClient.interceptors.request.use((config) => { diff --git a/frontend/src/components/Footer.js b/frontend/src/components/Footer.js index 3d28214..c5f9640 100644 --- a/frontend/src/components/Footer.js +++ b/frontend/src/components/Footer.js @@ -7,7 +7,7 @@ export default function Footer() {

©kibolAPP - 2024 Legnica Kebab City Tour. Wszelkie prawa zastrzeżone. + 2025 Legnica Kebab City Tour. Wszelkie prawa zastrzeżone.

); diff --git a/frontend/src/components/KebabListClone.js b/frontend/src/components/KebabListClone.js new file mode 100644 index 0000000..55149a6 --- /dev/null +++ b/frontend/src/components/KebabListClone.js @@ -0,0 +1,158 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; + +const daysTranslations = { + monday: 'Poniedziałek', + tuesday: 'Wtorek', + wednesday: 'Środa', + thursday: 'Czwartek', + friday: 'Piątek', + saturday: 'Sobota', + sunday: 'Niedziela', +}; + +const dayOrder = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']; + +const translateStatus = (status) => { + const statusMap = { + exists: 'Istnieje', + closed: 'Zamknięty', + planned: 'Planowany', + }; + return statusMap[status] || 'Nieznany'; +}; + +export default function KebabsListClone({ kebabs, activeKebabIndex }) { + const [openIndex, setOpenIndex] = useState(null); + const kebabRefs = useRef([]); + + useEffect(() => { + if (activeKebabIndex !== null) { + setOpenIndex(activeKebabIndex); + if (kebabRefs.current[activeKebabIndex]) { + kebabRefs.current[activeKebabIndex].scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + } + }, [activeKebabIndex]); + + const toggleDetails = (index) => { + setOpenIndex(openIndex === index ? null : index); + }; + + return ( +
+ {kebabs.map((kebab, index) => ( +
(kebabRefs.current[index] = el)} + className={`p-4 rounded-lg shadow-md bg-white ${ + activeKebabIndex === index ? 'border-2 border-blue-500' : '' + }`} + > +
+
+
+ {kebab.name} +
+
+

{kebab.name}

+

{kebab.address}

+
+
+ + toggleDetails(index)} + /> +
+ +
+
+

Godziny otwarcia:

+ {dayOrder + .filter((day) => kebab.opening_hours?.[day]) + .map((day) => ( +

+ {daysTranslations[day]}: {kebab.opening_hours[day]} +

+ ))} + + {kebab.meats && kebab.meats.length > 0 && ( +
+

Mięsa:

+

{kebab.meats.join(', ')}

+
+ )} + + {kebab.sauces && kebab.sauces.length > 0 && ( +
+

Sosy:

+

{kebab.sauces.join(', ')}

+
+ )} + + {kebab.status && ( +
+

Status:

+

{translateStatus(kebab.status)}

+
+ )} + + {kebab.pages && Object.keys(kebab.pages).length > 0 && ( +
+

Strony:

+
+ {Object.entries(kebab.pages).map(([key, value]) => ( + + ))} +
+
+ )} + + {kebab.comments && kebab.comments.length > 0 && ( +
+

Komentarze:

+
+ {kebab.comments.map((commentObj, index) => ( +
+ + {commentObj.name || `Użytkownik #${commentObj.id_user}`}: + + {commentObj.comment} +
+ ))} +
+
+ )} +
+
+
+ ))} +
+ ); +} diff --git a/frontend/src/components/KebabsList.js b/frontend/src/components/KebabsList.js index c4eac44..4f2cb94 100644 --- a/frontend/src/components/KebabsList.js +++ b/frontend/src/components/KebabsList.js @@ -135,7 +135,7 @@ const translateStatus = (status) => { toggleDetails(index)} @@ -191,17 +191,53 @@ const translateStatus = (status) => { {kebab.ordering_options && kebab.ordering_options.length > 0 && ( -
-

Opcje zamówień:

+
+

Opcje zamówień:

{kebab.ordering_options.map((option, index) => ( -

+

{option}

))}
)} + + {kebab.pages && Object.keys(kebab.pages).length > 0 && ( +
+

Strony:

+
+ {Object.entries(kebab.pages).map(([key, value]) => ( +
+ + {key} + +
+ ))} +
+
+ )} + + {kebab.comments && kebab.comments.length > 0 && ( +
+

Komentarze:

+
+ {kebab.comments.map((commentObj, index) => ( +
+ + {commentObj.name || `Użytkownik #${commentObj.id_user}`}: + + {commentObj.comment} +
+ ))} +
+
+ )}
diff --git a/frontend/src/components/SearchPanel.js b/frontend/src/components/SearchPanel.js index a8f77f4..115fc11 100644 --- a/frontend/src/components/SearchPanel.js +++ b/frontend/src/components/SearchPanel.js @@ -10,6 +10,8 @@ export default function SearchPanel({ kebabs, onSearch }) { const [selectedSauces, setSelectedSauces] = useState([]); const [selectedMeats, setSelectedMeats] = useState([]); const [selectedOrderingOptions, setSelectedOrderingOptions] = useState([]); + const [selectedPages, setSelectedPages] = useState([]); + const allSauces = Array.from( new Set(kebabs.flatMap((kebab) => kebab.sauces || [])) @@ -23,6 +25,10 @@ export default function SearchPanel({ kebabs, onSearch }) { new Set(kebabs.flatMap((kebab) => kebab.ordering_options || [])) ); + const allPages = Array.from( + new Set(kebabs.flatMap((kebab) => Object.keys(kebab.pages || {}))) + ); + const getCurrentTimeDetails = () => { const now = new Date(); const currentHour = now.getHours(); @@ -87,13 +93,22 @@ export default function SearchPanel({ kebabs, onSearch }) { applyFilters(searchQuery, selectedSauces, selectedMeats, showOpenNow, sortOrder, updatedOptions); }; + const handlePageToggle = (page) => { + const updatedPages = selectedPages.includes(page) + ? selectedPages.filter((p) => p !== page) + : [...selectedPages, page]; + + setSelectedPages(updatedPages); + applyFilters(searchQuery, selectedSauces, selectedMeats, showOpenNow, sortOrder, selectedOrderingOptions, updatedPages); + }; + const toggleOpenNow = () => { const updatedShowOpenNow = !showOpenNow; setShowOpenNow(updatedShowOpenNow); applyFilters(searchQuery, selectedSauces, selectedMeats, updatedShowOpenNow, sortOrder); }; - const applyFilters = (query, sauces, meats, openNow, order, orderingOptions = []) => { + const applyFilters = (query, sauces, meats, openNow, order, orderingOptions = [], pages = []) => { let filteredKebabs = kebabs; if (query) { @@ -120,11 +135,18 @@ export default function SearchPanel({ kebabs, onSearch }) { ); } + if (pages.length > 0) { + filteredKebabs = filteredKebabs.filter((kebab) => + pages.every((page) => Object.keys(kebab.pages || {}).includes(page)) + ); + } + if (openNow) { filteredKebabs = filteredKebabs.filter(isOpenNow); } - filteredKebabs = filteredKebabs.sort((a, b) => { + filteredKebabs = [...filteredKebabs].sort((a, b) => { + if (!a.name || !b.name) return 0; if (order === 'asc') { return a.name.localeCompare(b.name); } @@ -134,6 +156,18 @@ export default function SearchPanel({ kebabs, onSearch }) { onSearch(filteredKebabs); }; + const kebabStatusCounts = () => { + const statuses = { exists: 0, closed: 0, planned: 0 }; + kebabs.forEach((kebab) => { + if (statuses[kebab.status] !== undefined) { + statuses[kebab.status]++; + } + }); + return statuses; + }; + + const { exists, closed, planned } = kebabStatusCounts(); + return (
+
+

Otwarte: {exists}

+

Zamknięte: {closed}

+

Planowane: {planned}

+
+
Sortowanie po nazwie:
+ + {/* Strony */} +
+

Strony:

+
+ {allPages.map((page) => ( + + ))} +
+
diff --git a/frontend/src/pages/Home.js b/frontend/src/pages/Home.js index e81c555..6bf99fe 100644 --- a/frontend/src/pages/Home.js +++ b/frontend/src/pages/Home.js @@ -85,7 +85,7 @@ export default function Home() {
diff --git a/frontend/src/pages/MapClone.js b/frontend/src/pages/MapClone.js new file mode 100644 index 0000000..790b4d4 --- /dev/null +++ b/frontend/src/pages/MapClone.js @@ -0,0 +1,113 @@ +import React, { useState, useEffect } from 'react'; +import '../index.css'; +import Header from '../components/Header.js'; +import Footer from '../components/Footer.js'; +import KebabsListClone from '../components/KebabListClone.js'; +import SearchPanel from '../components/SearchPanel.js'; +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import L from 'leaflet'; +import 'leaflet/dist/leaflet.css'; +import kebab_icon from '../img/kebab_icon.png'; +import axiosClient from '../axiosClient.js'; + +export default function MapClone() { + const legnicaBounds = [ + [51.158, 16.114], + [51.242, 16.260], + ]; + + const [kebabs, setKebabs] = useState([]); + const [filteredKebabs, setFilteredKebabs] = useState([]); + const [activeKebabIndex, setActiveKebabIndex] = useState(null); + + useEffect(() => { + axiosClient + .get('/kebabs') + .then((response) => { + setKebabs(response.data); + setFilteredKebabs(response.data); + }) + .catch((error) => { + console.error('Błąd pobierania kebabów:', error); + }); + }, []); + + const kebabIcon = new L.Icon({ + iconUrl: kebab_icon, + iconSize: [30, 30], + iconAnchor: [15, 30], + popupAnchor: [0, -30], + }); + + const handleSearch = (filtered) => { + setFilteredKebabs(filtered); + }; + + const scrollToKebab = (index) => { + setActiveKebabIndex(index); + + setTimeout(() => { + const kebabElement = document.getElementById(`kebab-${index}`); + if (kebabElement) { + kebabElement.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + } + }, 0); + }; + + return ( +
+
+
+ {/* Mapa */} +
+ + + + {filteredKebabs.map((kebab, index) => ( + + + {kebab.name}
+ {kebab.address}
+ +
+
+ ))} +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ ); +} diff --git a/frontend/src/router.js b/frontend/src/router.js index d5bfa20..90a34b2 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -2,6 +2,7 @@ import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Home from './pages/Home.js'; import Map from './pages/Map.js'; +import MapClone from './pages/MapClone.js'; import AuthPage from './pages/AuthPage.js'; import AdminPanel from './pages/AdminPanel.js'; import ProtectedRoute from './components/ProtectedRoute.js'; @@ -11,6 +12,7 @@ const AppRouter = () => ( } /> } /> + } /> } />