diff --git a/.gitea/workflows/frontend-ci.yml b/.gitea/workflows/frontend-ci.yml index 3ea2640..c1676ae 100644 --- a/.gitea/workflows/frontend-ci.yml +++ b/.gitea/workflows/frontend-ci.yml @@ -17,10 +17,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v3 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm' @@ -39,7 +39,7 @@ jobs: run: npm run build - name: Upload build artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: frontend-build path: frontend/dist diff --git a/frontend/src/components/Footer.jsx b/frontend/src/components/Footer.jsx index c0d7a8f..fdbe63d 100644 --- a/frontend/src/components/Footer.jsx +++ b/frontend/src/components/Footer.jsx @@ -1,6 +1,6 @@ import React from 'react'; import './Footer.css'; -import { useLanguage } from '../contexts/LanguageContext'; +import { useLanguage } from '../hooks/useLanguage'; const Footer = () => { const { t } = useLanguage(); diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 74df815..2af746d 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { useLanguage } from '../contexts/LanguageContext'; +import { useLanguage } from '../hooks/useLanguage'; import LanguageSwitcher from './LanguageSwitcher'; import './Header.css'; diff --git a/frontend/src/components/LanguageSwitcher.jsx b/frontend/src/components/LanguageSwitcher.jsx index 0e5d04e..1a397ac 100644 --- a/frontend/src/components/LanguageSwitcher.jsx +++ b/frontend/src/components/LanguageSwitcher.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useLanguage } from '../contexts/LanguageContext'; +import { useLanguage } from '../hooks/useLanguage'; import './LanguageSwitcher.css'; const LanguageSwitcher = () => { diff --git a/frontend/src/contexts/LanguageContext.jsx b/frontend/src/contexts/LanguageContext.jsx index de0df80..e601770 100644 --- a/frontend/src/contexts/LanguageContext.jsx +++ b/frontend/src/contexts/LanguageContext.jsx @@ -1,86 +1,5 @@ -import React, { createContext, useContext, useState } from 'react'; - -const LanguageContext = createContext(); - -export const useLanguage = () => { - const context = useContext(LanguageContext); - if (!context) { - throw new Error('useLanguage must be used within a LanguageProvider'); - } - return context; -}; - -const translations = { - en: { - // Header - home: 'Home', - about: 'About', - contact: 'Contact', - - // CTA Section - readyToStart: 'Ready to get started?', - ctaSubtitle: 'Join thousands of developers building amazing applications with our modern stack.', - startBuilding: 'Start Building', - - // Language Switcher - language: 'Language', - english: 'English', - finnish: 'Finnish', - - // Search Page - searchPlaceholder: 'Enter your search query...', - searchButton: 'Search', - searchResults: 'Search Results', - searchResultsSubtitle: 'Here are the results for your search query.', - searchResultsPlaceholder: 'Search results will appear here. Try entering a search term above to get started.', - - // Search Filters - dogFriendly: 'Dog Friendly', - accessible: 'Accessible', - familyFriendly: 'Family Friendly', - peopleCount: 'People', - priceRange: 'Price', - any: 'Any', - - // Footer - footerDescription: 'Find fun things to do!', - }, - fi: { - // Header - home: 'Koti', - about: 'Tietoja', - contact: 'Yhteystiedot', - - - // CTA Section - readyToStart: 'Valmiina aloittamaan?', - ctaSubtitle: 'Liity tuhansien kehittäjien joukkoon, jotka rakentavat upeita sovelluksia modernilla teknologiapinoamme.', - startBuilding: 'Aloita rakentaminen', - - // Language Switcher - language: 'Kieli', - english: 'Englanti', - finnish: 'Suomi', - - // Search Page - searchPlaceholder: 'Syötä hakukyselysi...', - searchButton: 'Hae', - searchResults: 'Hakutulokset', - searchResultsSubtitle: 'Tässä ovat hakutulokset kyselysi perusteella.', - searchResultsPlaceholder: 'Hakutulokset näkyvät täällä. Kokeile syöttää hakusana yllä aloittaaksesi.', - - // Search Filters - dogFriendly: 'Koiraystävällinen', - accessible: 'Esteetön', - familyFriendly: 'Lapsiystävällinen', - peopleCount: 'Henkilöt', - priceRange: 'Hinta', - any: 'Mikä tahansa', - - // Footer - footerDescription: 'Löydä hauskaa tekemistä!', - } -}; +import React, { useState } from 'react'; +import { LanguageContext, translations } from './languageContextData'; export const LanguageProvider = ({ children }) => { const [language, setLanguage] = useState('en'); diff --git a/frontend/src/contexts/languageContextData.js b/frontend/src/contexts/languageContextData.js new file mode 100644 index 0000000..6ddf7b5 --- /dev/null +++ b/frontend/src/contexts/languageContextData.js @@ -0,0 +1,75 @@ +import { createContext } from 'react'; + +export const LanguageContext = createContext(); + +export const translations = { + en: { + // Header + home: 'Home', + about: 'About', + contact: 'Contact', + + // CTA Section + readyToStart: 'Ready to get started?', + ctaSubtitle: 'Join thousands of developers building amazing applications with our modern stack.', + startBuilding: 'Start Building', + + // Language Switcher + language: 'Language', + english: 'English', + finnish: 'Finnish', + + // Search Page + searchPlaceholder: 'Enter your search query...', + searchButton: 'Search', + searchResults: 'Search Results', + searchResultsSubtitle: 'Here are the results for your search query.', + searchResultsPlaceholder: 'Search results will appear here. Try entering a search term above to get started.', + + // Search Filters + dogFriendly: 'Dog Friendly', + accessible: 'Accessible', + familyFriendly: 'Family Friendly', + peopleCount: 'People', + priceRange: 'Price', + any: 'Any', + + // Footer + footerDescription: 'Find fun things to do!', + }, + fi: { + // Header + home: 'Koti', + about: 'Tietoja', + contact: 'Yhteystiedot', + + + // CTA Section + readyToStart: 'Valmiina aloittamaan?', + ctaSubtitle: 'Liity tuhansien kehittäjien joukkoon, jotka rakentavat upeita sovelluksia modernilla teknologiapinoamme.', + startBuilding: 'Aloita rakentaminen', + + // Language Switcher + language: 'Kieli', + english: 'Englanti', + finnish: 'Suomi', + + // Search Page + searchPlaceholder: 'Syötä hakukyselysi...', + searchButton: 'Hae', + searchResults: 'Hakutulokset', + searchResultsSubtitle: 'Tässä ovat hakutulokset kyselysi perusteella.', + searchResultsPlaceholder: 'Hakutulokset näkyvät täällä. Kokeile syöttää hakusana yllä aloittaaksesi.', + + // Search Filters + dogFriendly: 'Koiraystävällinen', + accessible: 'Esteetön', + familyFriendly: 'Lapsiystävällinen', + peopleCount: 'Henkilöt', + priceRange: 'Hinta', + any: 'Mikä tahansa', + + // Footer + footerDescription: 'Löydä hauskaa tekemistä!', + } +}; \ No newline at end of file diff --git a/frontend/src/hooks/useLanguage.js b/frontend/src/hooks/useLanguage.js new file mode 100644 index 0000000..82efa83 --- /dev/null +++ b/frontend/src/hooks/useLanguage.js @@ -0,0 +1,10 @@ +import { useContext } from 'react'; +import { LanguageContext } from '../contexts/languageContextData'; + +export const useLanguage = () => { + const context = useContext(LanguageContext); + if (!context) { + throw new Error('useLanguage must be used within a LanguageProvider'); + } + return context; +}; \ No newline at end of file diff --git a/frontend/src/pages/Home.css b/frontend/src/pages/Home.css index d43266a..b105651 100644 --- a/frontend/src/pages/Home.css +++ b/frontend/src/pages/Home.css @@ -205,6 +205,107 @@ gap: 2rem; } +/* Search Results Section */ +.search-results { + padding: 4rem 0; + background: white; +} + +.results-title { + text-align: center; + font-size: 2rem; + font-weight: 700; + margin: 0 0 3rem 0; + color: var(--color-text-primary); +} + +.results-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.result-card { + height: 100%; + display: flex; + flex-direction: column; +} + +.result-card .card-content { + flex: 1; + display: flex; + flex-direction: column; +} + +.result-card .card-body { + flex: 1; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.result-card .card-body p { + margin: 0; + line-height: 1.6; + color: var(--color-text-secondary); +} + +.amenities { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: auto; +} + +.amenity-tag { + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); + color: var(--color-dark); + padding: 0.25rem 0.75rem; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 600; + box-shadow: 0 2px 8px var(--color-shadow-primary); + transition: all 0.3s ease; +} + +.amenity-tag:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px var(--color-shadow-primary); +} + +/* No Results Section */ +.no-results { + padding: 4rem 0; + background: white; + text-align: center; +} + +.no-results-content h2 { + font-size: 2rem; + font-weight: 700; + margin: 0 0 1rem 0; + color: var(--color-text-primary); +} + +.no-results-content p { + font-size: 1.1rem; + color: var(--color-text-secondary); + margin: 0; +} + +/* Loading State */ +.search-button.searching { + background: linear-gradient(135deg, var(--color-secondary-dark) 0%, var(--color-dark) 100%); + cursor: not-allowed; + opacity: 0.8; +} + +.search-button.searching:hover { + transform: none; + box-shadow: 0 4px 12px var(--color-shadow-secondary); +} + /* Responsive Design */ @media (max-width: 768px) { .hero { @@ -261,6 +362,27 @@ .features-grid { grid-template-columns: 1fr; } + + .search-results { + padding: 3rem 0; + } + + .results-title { + font-size: 1.75rem; + } + + .results-grid { + grid-template-columns: 1fr; + gap: 1.5rem; + } + + .no-results { + padding: 3rem 0; + } + + .no-results-content h2 { + font-size: 1.75rem; + } } @media (max-width: 480px) { @@ -284,6 +406,14 @@ min-width: 180px; padding: 0.75rem; } + + .results-title { + font-size: 1.5rem; + } + + .no-results-content h2 { + font-size: 1.5rem; + } } /* Dark Mode Support */ @@ -334,4 +464,24 @@ .features { background: var(--color-bg-dark); } + + .search-results { + background: var(--color-bg-dark); + } + + .no-results { + background: var(--color-bg-dark); + } + + .results-title { + color: var(--color-text-primary-light); + } + + .no-results-content h2 { + color: var(--color-text-primary-light); + } + + .no-results-content p { + color: var(--color-text-secondary); + } } \ No newline at end of file diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index 61a6ed2..21bf963 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Header, Footer, Button, Card } from '../components'; -import { useLanguage } from '../contexts/LanguageContext'; +import { useLanguage } from '../hooks/useLanguage'; import './Home.css'; const Home = () => { @@ -13,14 +13,62 @@ const Home = () => { peopleCount: '', priceRange: '' }); + const [searchResults, setSearchResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); + + // Mockup data for search results + const mockResults = [ + { + id: 1, + title: "Cozy Mountain Cabin", + subtitle: "€€ • 4 guests • Dog friendly", + image: "https://images.unsplash.com/photo-1449824913935-59a10b8d2000?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + description: "Beautiful mountain cabin with stunning views, perfect for a peaceful getaway. Features a hot tub and fireplace.", + amenities: ["Dog friendly", "Fireplace", "Hot tub", "Mountain view"] + }, + { + id: 2, + title: "Modern City Apartment", + subtitle: "€€€ • 2 guests • Accessible", + image: "https://images.unsplash.com/photo-1502672260266-1c1ef2d93688?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + description: "Stylish apartment in the heart of the city with modern amenities and easy access to public transportation.", + amenities: ["Accessible", "City view", "Gym access", "Parking"] + }, + { + id: 3, + title: "Family Beach House", + subtitle: "€€€€ • 6 guests • Family friendly", + image: "https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + description: "Spacious beachfront property perfect for family vacations. Includes a pool, playground, and direct beach access.", + amenities: ["Family friendly", "Beach access", "Pool", "Playground"] + }, + { + id: 4, + title: "Rustic Farmhouse", + subtitle: "€€ • 8 guests • Dog friendly", + image: "https://images.unsplash.com/photo-1571896349842-33c89424de2d?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + description: "Charming farmhouse surrounded by nature. Perfect for large groups looking for a rural escape.", + amenities: ["Dog friendly", "Large groups", "Nature", "Fireplace"] + }, + { + id: 5, + title: "Luxury Villa", + subtitle: "€€€€ • 4 guests • Accessible", + image: "https://images.unsplash.com/photo-1613490493576-7fde63acd811?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + description: "Exclusive villa with premium amenities including a private pool, chef's kitchen, and stunning ocean views.", + amenities: ["Accessible", "Private pool", "Ocean view", "Chef's kitchen"] + }, + { + id: 6, + title: "Cozy Studio", + subtitle: "€ • 2 guests • Family friendly", + image: "https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + description: "Affordable studio apartment perfect for couples or small families. Located in a quiet neighborhood.", + amenities: ["Family friendly", "Affordable", "Quiet", "Central location"] + } + ]; - const handleGetStarted = () => { - alert('Get Started button clicked!'); - }; - const handleLearnMore = () => { - alert('Learn More button clicked!'); - }; const handleFilterChange = (filterName, value) => { setFilters(prev => ({ @@ -30,9 +78,30 @@ const Home = () => { }; const handleSearch = () => { - // Handle search logic here - console.log('Search query:', searchQuery); - console.log('Filters:', filters); + if (!searchQuery.trim()) { + alert('Please enter a search query'); + return; + } + + setIsSearching(true); + + // Simulate API call delay + setTimeout(() => { + // Filter results based on search query and filters + let filteredResults = mockResults.filter(result => { + const matchesQuery = result.title.toLowerCase().includes(searchQuery.toLowerCase()) || + result.description.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesDogFriendly = !filters.dogFriendly || result.amenities.includes('Dog friendly'); + const matchesAccessible = !filters.accessible || result.amenities.includes('Accessible'); + const matchesFamilyFriendly = !filters.familyFriendly || result.amenities.includes('Family friendly'); + + return matchesQuery && matchesDogFriendly && matchesAccessible && matchesFamilyFriendly; + }); + + setSearchResults(filteredResults); + setIsSearching(false); + }, 1000); }; return ( @@ -57,8 +126,12 @@ const Home = () => { onChange={(e) => setSearchQuery(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSearch()} /> - @@ -140,6 +213,50 @@ const Home = () => { + {/* Search Results Section */} + {searchResults.length > 0 && ( +
+
+

+ Found {searchResults.length} result{searchResults.length !== 1 ? 's' : ''} for "{searchQuery}" +

+
+ {searchResults.map(result => ( + +

{result.description}

+
+ {result.amenities.map((amenity, index) => ( + + {amenity} + + ))} +
+
+ ))} +
+
+
+ )} + + {/* No Results Message */} + {searchResults.length === 0 && !isSearching && searchQuery && ( +
+
+
+

No results found for "{searchQuery}"

+

Try adjusting your search terms or filters to find more options.

+
+
+
+ )} +