diff --git a/package-lock.json b/package-lock.json index 31b8f82..a5198d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,8 +18,10 @@ "@mui/material": "^7.0.0", "@vitejs/plugin-react-swc": "^4.2.3", "analytics": "^0.8.19", + "leaflet": "^1.9.4", "react": "^19.3.1", "react-dom": "^19.3.1", + "react-leaflet": "^5.0.0", "react-router-dom": "^7.18.4", "react-scroll-parallax": "^3.5.0", "use-analytics": "^1.1.0", @@ -31,6 +33,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/leaflet": "^1.9.22", "@types/node": "^25.9.3", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", @@ -1761,6 +1764,17 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", @@ -2415,6 +2429,13 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2422,6 +2443,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/leaflet": { + "version": "1.9.22", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.22.tgz", + "integrity": "sha512-h3lhECYEKDasG7LFHu+GiHqAvsgLuQvlJvVZzJDGONo3sEL+wUOqSFLnwkZlK0qVxnxbuGFW8iBlJNYs5wgndA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "25.9.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", @@ -4184,6 +4215,12 @@ "@keyv/serialize": "^1.1.1" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4652,6 +4689,20 @@ "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", "license": "MIT" }, + "node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/react-router": { "version": "7.18.4", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.4.tgz", diff --git a/package.json b/package.json index 44ea412..12bf9f7 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,10 @@ "@mui/material": "^7.0.0", "@vitejs/plugin-react-swc": "^4.2.3", "analytics": "^0.8.19", + "leaflet": "^1.9.4", "react": "^19.3.1", "react-dom": "^19.3.1", + "react-leaflet": "^5.0.0", "react-router-dom": "^7.18.4", "react-scroll-parallax": "^3.5.0", "use-analytics": "^1.1.0", @@ -42,6 +44,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/leaflet": "^1.9.22", "@types/node": "^25.9.3", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", diff --git a/src/App.tsx b/src/App.tsx index ccac76b..9c274d8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,7 @@ import Loading from 'components/molecules/LoadingScreen/index.js' const MainPage = React.lazy(() => import('components/pages/MainPage/index.js')) const PeoplePage = React.lazy(() => import('components/pages/PeoplePage/index.js')) const ActivitiesPage = React.lazy(() => import('components/pages/ActivitiesPage/index.js')) +const EventsPage = React.lazy(() => import('components/pages/EventsPage/index.js')) const ContactPage = React.lazy(() => import('components/pages/ContactPage/index.js')) const PrivacyPage = React.lazy(() => import('components/pages/PrivacyPage/index.js')) const BusinessCardPage = React.lazy(() => import('components/pages/BusinessCardPage/index.js')) @@ -42,6 +43,14 @@ const router = createBrowserRouter([ ), }, + { + path: 'events', + element: ( + }> + + + ), + }, { path: 'contacts', element: ( diff --git a/src/components/molecules/Toolbar/index.tsx b/src/components/molecules/Toolbar/index.tsx index b3f5db2..bfb1c27 100644 --- a/src/components/molecules/Toolbar/index.tsx +++ b/src/components/molecules/Toolbar/index.tsx @@ -101,6 +101,11 @@ const CustomToolbar: React.FC = ({ drawerWidth = 240, window + + + + + @@ -163,9 +168,9 @@ const CustomToolbar: React.FC = ({ drawerWidth = 240, window Compagnia d'Arme del Santo Luca - Home Attività Personaggi + Eventi Contattaci diff --git a/src/components/pages/EventsPage/EventDialog.tsx b/src/components/pages/EventsPage/EventDialog.tsx new file mode 100644 index 0000000..acd399e --- /dev/null +++ b/src/components/pages/EventsPage/EventDialog.tsx @@ -0,0 +1,97 @@ +import React from 'react' +import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Divider, Link, Stack, Typography } from '@mui/material' +import CalendarMonthIcon from '@mui/icons-material/CalendarMonth' +import LocationOnIcon from '@mui/icons-material/LocationOn' +import OpenInNewIcon from '@mui/icons-material/OpenInNew' + +import type { Event } from './types.js' + +interface EventDialogProps { + event: Event | null + onClose: () => void +} + +const formatDateRange = (date: Date, endDate?: Date): string => { + const fmt = (d: Date) => d.toLocaleDateString('it-IT', { day: 'numeric', month: 'long', year: 'numeric' }) + if (!endDate) return fmt(date) + if (date.getMonth() === endDate.getMonth() && date.getFullYear() === endDate.getFullYear()) { + return `${date.getDate()}–${endDate.getDate()} ${date.toLocaleDateString('it-IT', { month: 'long', year: 'numeric' })}` + } + return `${fmt(date)} – ${fmt(endDate)}` +} + +const EventDialog: React.FC = ({ event, onClose }) => { + if (!event) return null + + const osmUrl = `https://www.openstreetmap.org/?mlat=${event.location.lat}&mlon=${event.location.lng}#map=15/${event.location.lat}/${event.location.lng}` + + return ( + + {event.image && } + + + {event.details?.title ?? event.title} + + + + + + + + {event.location.name}, {event.location.city} + + + + + {formatDateRange(event.date, event.endDate)} + + + {event.details?.description && ( + <> + + + {event.details.description} + + > + )} + {event.details?.howToReach && ( + <> + + + {event.details.howToReach} + + > + )} + + + + {event.link && ( + } + clickable + /> + )} + } + clickable + /> + + + Chiudi + + + + ) +} + +export default EventDialog diff --git a/src/components/pages/EventsPage/ListView.tsx b/src/components/pages/EventsPage/ListView.tsx new file mode 100644 index 0000000..fef8780 --- /dev/null +++ b/src/components/pages/EventsPage/ListView.tsx @@ -0,0 +1,117 @@ +import React from 'react' +import { Box, Card, CardActionArea, CardContent, CardMedia, Chip, Stack, Typography } from '@mui/material' +import CalendarMonthIcon from '@mui/icons-material/CalendarMonth' +import LocationOnIcon from '@mui/icons-material/LocationOn' +import { InfoOutline } from '@mui/icons-material' + +import type { Event } from './types.js' + +interface ListViewProps { + events: Event[] + onSelect: (event: Event) => void +} + +const formatDateRange = (date: Date, endDate?: Date): string => { + const fmt = (d: Date) => d.toLocaleDateString('it-IT', { day: 'numeric', month: 'long', year: 'numeric' }) + if (!endDate) return fmt(date) + if (date.getMonth() === endDate.getMonth() && date.getFullYear() === endDate.getFullYear()) { + return `${date.getDate()}-${endDate.getDate()} ${date.toLocaleDateString('it-IT', { month: 'long', year: 'numeric' })}` + } + return `${fmt(date)} - ${fmt(endDate)}` +} + +const isUpcoming = (date: Date) => date >= new Date() + +const EventCard: React.FC<{ event: Event; onSelect: (event: Event) => void }> = ({ event, onSelect }) => { + return ( + theme.breakpoints.values.lg * 0.3, + }}> + onSelect(event)} + sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', alignItems: 'stretch', justifyContent: 'flex-end' }}> + {event.image && } + + + + {event.title} + + {!isUpcoming(event.date) && } + + + + + + + {formatDateRange(event.date, event.endDate)} + + + + + + {event.location.city} + + + + + + + + + ) +} + +const ListView: React.FC = ({ events, onSelect }) => { + const eventGroups = React.useMemo(() => { + const groups: { upcoming: Event[]; past: Event[] } = { upcoming: [], past: [] } + + for (const event of events) { + if (isUpcoming(event.date)) { + groups.upcoming.push(event) + } else { + groups.past.push(event) + } + } + + groups.upcoming.sort((a, b) => a.date.getTime() - b.date.getTime()) + groups.past.sort((a, b) => b.date.getTime() - a.date.getTime()) + + return groups + }, [events]) + return ( + + {eventGroups.upcoming.length > 0 && ( + <> + + Prossimi eventi + + + {eventGroups.upcoming.map(event => ( + + ))} + + > + )} + {eventGroups.past.length > 0 && ( + <> + + Eventi passati + + + {eventGroups.past.map(event => ( + + ))} + + > + )} + + ) +} + +export default ListView diff --git a/src/components/pages/EventsPage/MapView.tsx b/src/components/pages/EventsPage/MapView.tsx new file mode 100644 index 0000000..c162433 --- /dev/null +++ b/src/components/pages/EventsPage/MapView.tsx @@ -0,0 +1,109 @@ +import React from 'react' +import { Box, Button, Chip, Stack, Typography, useTheme } from '@mui/material' +import CalendarMonthIcon from '@mui/icons-material/CalendarMonth' +import LocationOnIcon from '@mui/icons-material/LocationOn' +import InfoIcon from '@mui/icons-material/InfoOutline' +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet' +import { divIcon } from 'leaflet' +import 'leaflet/dist/leaflet.css' + +import type { Event } from './types.js' + +/** Creates a numbered teardrop pin SVG as a Leaflet divIcon */ +const makeNumberedIcon = (n: number, color: string, isPast: boolean) => { + const fill = isPast ? '#9e9e9e' : color + const svg = ` + + + ${n} + ` + return divIcon({ + html: svg, + className: '', + iconSize: [32, 42], + iconAnchor: [16, 42], + popupAnchor: [0, -44], + }) +} + +interface MapViewProps { + events: Event[] + onSelect: (event: Event) => void +} + +const formatDateRange = (date: Date, endDate?: Date): string => { + const fmt = (d: Date) => d.toLocaleDateString('it-IT', { day: 'numeric', month: 'long', year: 'numeric' }) + if (!endDate) return fmt(date) + if (date.getMonth() === endDate.getMonth() && date.getFullYear() === endDate.getFullYear()) { + return `${date.getDate()}–${endDate.getDate()} ${date.toLocaleDateString('it-IT', { month: 'long', year: 'numeric' })}` + } + return `${fmt(date)} – ${fmt(endDate)}` +} + +const isUpcoming = (date: Date) => date >= new Date() + +const CENTER: [number, number] = [43.5, 12.5] // roughly central Italy + +const MapView: React.FC = ({ events, onSelect }) => { + const theme = useTheme() + const primaryColor = theme.palette.primary.main + const today = new Date() + + const sorted = [...events].sort((a, b) => b.date.getTime() - a.date.getTime()) + + return ( + + + + {sorted.map((event, idx) => ( + + + + + + {event.title} + + {!isUpcoming(event.date) && } + + + + + {event.location.name} + {event.location.city} + + + + + {formatDateRange(event.date, event.endDate)} + + } size="small" variant="contained" onClick={() => onSelect(event)}> + Dettagli + + + + + ))} + + + ) +} + +export default MapView diff --git a/src/components/pages/EventsPage/events.ts b/src/components/pages/EventsPage/events.ts new file mode 100644 index 0000000..cfbd896 --- /dev/null +++ b/src/components/pages/EventsPage/events.ts @@ -0,0 +1,127 @@ +import type { Event } from './types.js' + +export const events: Event[] = [ + { + id: '1', + title: '4 Passi nel Medioevo', + date: new Date('2026-05-03'), + endDate: new Date('2026-05-04'), + location: { + name: 'Castello dei Ronchi', + city: 'Crevalcore (BO)', + lat: 44.7559844, + lng: 11.1382366, + link: 'https://maps.app.goo.gl/eqNB13PpJ4G7Sq2DA', + }, + link: 'https://www.4passinelmedioevo.com/', + image: 'https://www.4passinelmedioevo.com/wp-content/uploads/2026/04/cropped-565125388_1208800397957599_3322632688455654651_n-scaled-1.jpg', + details: { + description: + "La Compagnia d'Arme del Santo Luca partecipa al tradizionale Palio di Ferrara con uno spettacolo di combattimento storico e una mostra della vita civile medievale.", + }, + }, + { + id: '2', + title: 'Armi e Bagabli', + date: new Date('2026-03-21'), + endDate: new Date('2026-03-22'), + location: { + name: 'Piacenza Expo', + city: 'Piacenza (PC)', + lat: 45.0423607, + lng: 9.75375, + link: 'https://maps.app.goo.gl/FQAtyyK5zCZyaGzx9', + }, + link: 'https://armiebagagli.org/', + image: + 'https://scontent-fco2-1.xx.fbcdn.net/v/t39.30808-6/687039739_1615155693950230_1523012821653618722_n.jpg?_nc_cat=110&ccb=1-7&_nc_sid=2a1932&_nc_ohc=301psXNiznUQ7kNvwFIU2A_&_nc_oc=AdqgQ3sUtKe-X02NMO-pOaTgFocqAEc4rep8ZTUFvGN7e1sij1OdLeErW01aLp6NJKE&_nc_zt=23&_nc_ht=scontent-fco2-1.xx&_nc_gid=vfSFXIAfYJiRdDTqz1le-g&_nc_ss=7b2a8&oh=00_Af5QgkkEnukOrBqB9EiDmBbgNtXI4_Qi3WZ8eNzsg1MwZA&oe=69FFD320', + details: { + description: + 'La più grande manifestazione italiana per operatori e appassionati della Rievocazione Storica, presso il quartiere fieristico di Piacenza, appuntamento imperdibile per chiunque operi nel settore storico/rievocativo.', + }, + }, + { + id: 'trecentesca', + title: 'Trecentesca', + date: new Date('2026-05-16'), + endDate: new Date('2026-05-17'), + location: { + name: 'Abbazia di Morimondo', + city: 'Morimondo (MI)', + lat: 45.351915, + lng: 8.954823, + link: 'https://maps.app.goo.gl/oV97GdTZejNjpneD8', + }, + link: 'https://www.abbaziamorimondo.it/trecentesca-2026', + image: + 'https://scontent-fco2-1.xx.fbcdn.net/v/t39.30808-6/645894440_26176732645298731_6691381800434721321_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=25d718&_nc_ohc=EIa29pGGa5QQ7kNvwHIRuQt&_nc_oc=AdpEUIYfsBiSh8HpwsT5KkFdSYvlZYkUOCOUl9DYdEQ9PTbGR0uaDmVjI5HKy6cugb4&_nc_zt=23&_nc_ht=scontent-fco2-1.xx&_nc_gid=EcW53pTVT3DWReZnsh_SSw&_nc_ss=7b2a8&oh=00_Af5duE5CS05V2-KRwthjoxoib2SIeP18zu5obSxr1R6J7g&oe=69FFBB66', + }, + { + id: 'Palio dello Sparviero - Voci dal Medioevo di Trambaque', + title: 'Palio dello Sparviero', + date: new Date('2026-06-20'), + location: { + name: 'Castello di San Martino della Vaneza', + city: 'Cervarese Santa Croce (PD)', + lat: 45.4145888, + lng: 11.7110493, + link: 'https://maps.app.goo.gl/j1QwPm2CYL24vMQz6', + }, + link: 'https://www.paliodellosparviero.it/', + image: 'https://www.paliodellosparviero.it/wp-content/uploads/2021/06/e3c1afa4-630c-480f-acdd-0a5685c5c0ae.jpg', + details: { + title: 'Palio dello Sparviero - Voci dal Medioevo di Trambaque', + }, + }, + { + id: '3', + title: 'Giostra del Borgo', + date: new Date('2026-06-13'), + location: { + name: 'Ippodromo', + city: 'Ferrara', + lat: 44.82578, + lng: 11.612924, + link: 'https://maps.app.goo.gl/mqgVWmm8crAz7ofUA', + }, + image: + 'https://scontent-fco2-1.xx.fbcdn.net/v/t39.30808-6/495026144_998527592485792_333972602704298657_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=2a1932&_nc_ohc=V-Rj6Zro94UQ7kNvwGL_LTQ&_nc_oc=AdqbGYdKAgeN0MyVzbsnRbz31W9qqEIyJMzesfZl9r0f33UcVC-00gGtdM62Y6eLZso&_nc_zt=23&_nc_ht=scontent-fco2-1.xx&_nc_gid=XaR2VZ5CeSlQhvYnG2Ofbg&_nc_ss=7b2a8&oh=00_Af6CgCkib7vEwAVeuiPwlTTjkQrBeeLqzpE2ZkxzdWmxDA&oe=69FFB224', + link: 'https://www.quintana.it', + }, + { + id: 'Mantova Medievale', + title: 'Mantova Medievale', + date: new Date('2026-11-07'), + endDate: new Date('2026-11-08'), + location: { + name: 'Lungolago Gonzaga', + city: 'Mantova', + lat: 45.160947, + lng: 10.799786, + link: 'https://maps.app.goo.gl/QF5UjjLb5HRYgLxg8', + }, + link: 'http://www.mantovamedievale.it/', + image: + 'https://scontent-fco2-1.xx.fbcdn.net/v/t39.30808-6/475772202_928196622760603_187755049466880515_n.jpg?_nc_cat=108&ccb=1-7&_nc_sid=2a1932&_nc_ohc=GFNIVf-3cKoQ7kNvwHWpMoA&_nc_oc=AdqCNjkyT6mYI9D8sIsmAgfQdXtrVMNOthjth3dfcGCvVgxVSJSG_SVWroEcBPbyI7k&_nc_zt=23&_nc_ht=scontent-fco2-1.xx&_nc_gid=nD82EIE8zEzhzr8oQIF78A&_nc_ss=7b2a8&oh=00_Af6Jg9HJBlMy--pZ0eQp_QZ18k1b-hSmMDp-zUC6ID5Yrw&oe=69FFE2CC', + }, + { + id: '5', + title: 'Usi & Costumi', + date: new Date('2026-11-07'), + endDate: new Date('2026-11-08'), + location: { + name: 'Ferrara Fiere', + city: 'Ferrara', + lat: 44.8141398, + lng: 11.584555, + link: 'https://maps.app.goo.gl/q6MgoYrjm1uGxrrD8', + }, + link: 'https://www.usiecostumi.org/', + image: + 'https://scontent-fco2-1.xx.fbcdn.net/v/t39.30808-6/688923196_1294084136264066_3431102776522875652_n.jpg?_nc_cat=111&ccb=1-7&_nc_sid=2a1932&_nc_ohc=8DTyQeSJaEUQ7kNvwHpXmkS&_nc_oc=AdoKtRJlPuSnY0LgIEkrFSznA6aOekLn9cj09sScE7fPJEQz4YU7GSHIxHTZ0oOcJSw&_nc_zt=23&_nc_ht=scontent-fco2-1.xx&_nc_gid=6_24uMOlLU5IMhQptMgtsg&_nc_ss=7b2a8&oh=00_Af4WsF0pOUJT70MBLbOaRro0wJ1kct7-4dh_dto93ObmWw&oe=69FFD8CE', + details: { + description: 'La prima grande fiera internazionale, dedicata alla tradizione come strumento al servizio del turismo sostenibile.', + }, + }, + // +] diff --git a/src/components/pages/EventsPage/index.tsx b/src/components/pages/EventsPage/index.tsx new file mode 100644 index 0000000..8cb0885 --- /dev/null +++ b/src/components/pages/EventsPage/index.tsx @@ -0,0 +1,52 @@ +import React from 'react' +import { Box, Tab, Tabs, Typography } from '@mui/material' +import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted' +import MapIcon from '@mui/icons-material/Map' + +import { Page } from 'components/templates/index.js' +import type { Event } from './types.js' +import { events } from './events.js' +import EventDialog from './EventDialog.js' +import ListView from './ListView.js' + +const MapView = React.lazy(() => import('./MapView.js')) + +type ViewTab = 'list' | 'map' + +const EventsPage = () => { + const [tab, setTab] = React.useState('list') + // const [selectedTab, setSelectedTab] = React.useState('list') + const [selectedEvent, setSelectedEvent] = React.useState(null) + + return ( + theme.breakpoints.values.lg, + px: 'auto', + mx: 'auto', + }}> + + Vieni a trovarci! Partecipiamo a rievocazioni storiche, fiere e manifestazioni in tutta Italia, portando la nostra passione per il Medioevo in + ogni angolo del paese. Consulta il calendario per non perdere le nostre prossime avventure! + + + setTab(v)} centered aria-label="vista eventi"> + } iconPosition="start" label="Lista" value="list" /> + } iconPosition="start" label="Mappa" value="map" /> + + + + {tab === 'list' && } + {tab === 'map' && ( + + + + )} + + setSelectedEvent(null)} /> + + ) +} + +export default EventsPage diff --git a/src/components/pages/EventsPage/types.ts b/src/components/pages/EventsPage/types.ts new file mode 100644 index 0000000..a5633d6 --- /dev/null +++ b/src/components/pages/EventsPage/types.ts @@ -0,0 +1,24 @@ +export interface EventLocation { + name: string + city: string + lat: number + lng: number + link?: string +} + +export interface EventDetails { + title?: string + description?: string + howToReach?: string +} + +export interface Event { + id: string + title: string + date: Date + endDate?: Date + location: EventLocation + details?: EventDetails + image?: string + link?: string +}