Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend UI improvements #14

Merged
merged 2 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
49 changes: 14 additions & 35 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
@@ -1,40 +1,19 @@
:root {
--primary-color: #2c3e50;
--secondary-color: #61dafb;
--background-color: #f9f9f9;
--card-background: #ffffff;
--text-color: #34495e;
--shadow-color: rgba(0, 0, 0, 0.1);
}

#root {
margin: 0 auto;
text-align: center;
max-width: 1200px;
padding: 1rem;
background-color: var(--background-color);
border-radius: 10px;
box-shadow: 0 4px 6px var(--shadow-color);
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
76 changes: 39 additions & 37 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,71 @@
import { useState, useEffect } from 'react'
// import reactLogo from './assets/react.svg'
// import viteLogo from '/vite.svg'
import './App.css'
import { useState, useEffect } from 'react';
import './App.css';
import DaySelector from './DaySelector';
import Header from './Header'
import TabMeteo from './TabMeteo'
import Header from './Header';
import WeatherCard from './WeatherCard';

function App() {

const [data, setData] = useState({});
const [cities, setCities] = useState([]);

const [selectedCity, setSelectedCity] = useState('Paris');
const [selectedDate, setSelectedDate] = useState("2024-10-08");

useEffect(() => {

console.log(selectedCity, selectedDate);

fetch('http://localhost:5984/ecometeo/_find', {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
selector: { city: selectedCity, "meteo.date" : selectedDate },
limit: 1
})
})
.then(x => x.json())
.then(data => {
setData(data.docs[0]);
fetch('http://localhost:5984/ecometeo/_find', {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
selector: { city: selectedCity, "meteo.date": selectedDate },
limit: 1
})
})
.then((x) => x.json())
.then((data) => {
setData(data.docs[0]);
});
}, [selectedCity, selectedDate]);

useEffect(() => {
fetch('http://localhost:5984/ecometeo/_find', {
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
selector: { "meteo.date" : selectedDate },
selector: { "meteo.date": selectedDate },
fields: ["city"],
limit: 1000000, // On veut tous les résultats (Ajout futur d'une table contenant une liste des villes)
limit: 1000000
})
})
.then(x => x.json())
.then(data => {
const uniqueCities = [...new Set(data.docs.map(doc => doc.city))];
setCities(uniqueCities);
})
.then((x) => x.json())
.then((data) => {
const uniqueCities = [...new Set(data.docs.map((doc) => doc.city))];
setCities(uniqueCities);
});
}, []);


const handleCity = (city) => {
setSelectedCity(city);
setSelectedCity(city);
};

const handleDate = (date) => {
setSelectedDate(date);
setSelectedDate(date);
};

return (
<>
<Header cities={cities && cities} ville={selectedCity} cityChange={handleCity} selectedDate={selectedDate}/>
<TabMeteo data={data && data} date={selectedDate} city={selectedCity}/>
<DaySelector date={selectedDate} dateChange={handleDate}/>
</>
)
<div className="app-container">
<Header
cities={cities}
ville={selectedCity}
cityChange={handleCity}
selectedDate={selectedDate}
/>
<main className="main-content">
<WeatherCard data={data} date={selectedDate} />
<DaySelector date={selectedDate} dateChange={handleDate} />
</main>
</div>
);
}

export default App
export default App;
48 changes: 48 additions & 0 deletions frontend/src/Header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.header-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 2rem;
background-color: #f4f4f4; /* Couleur de fond subtile */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Ombre douce */
border-radius: 10px;
}

.select-container {
flex: 1;
max-width: 300px; /* Limite la largeur du menu déroulant */
}

.select {
font-size: 1rem;
}

.logo-container {
flex: 1;
text-align: center;
}

.logo-container h2 {
font-family: 'Roboto', sans-serif;
font-size: 1.8rem;
color: #2c3e50; /* Texte élégant */
margin: 0;
}

.info-container {
flex: 1;
text-align: right;
}

.city-name {
font-size: 1.2rem;
font-weight: bold;
margin: 0;
color: #34495e;
}

.date {
font-size: 1rem;
margin: 0;
color: #7f8c8d;
}
47 changes: 24 additions & 23 deletions frontend/src/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
import Select from 'react-select';
import 'dayjs/locale/fr'
import './Header.css'

function Header({ cities, ville, cityChange, selectedDate }) {
let options;
const options = cities?.map((city) => ({ value: city, label: city })) || [];

if (cities && cities[0]) {
options = cities.map((city) => ({ value: city, label: city }))
}

const defaultValue = options && options.find(city => city.value === city);
const defaultValue = options.find((option) => option.value === ville);
const handleChange = (selectedOption) => {
cityChange(selectedOption.value);
};
cityChange(selectedOption.value);
};

const formattedDate = new Date(selectedDate).toLocaleDateString('fr-FR', { day: 'numeric', month: 'long' });


return (
<main className="container" style={{ display: "flex", alignItems: "center", justifyContent: "space-around"}}>
<Select
defaultvalue={defaultValue}
onChange={handleChange}
options={options && options}
placeholder="Choisissez une ville"
/>
<h2>Éco Météo</h2>
<div>
<p>{ville}</p>
<p>{formattedDate}</p>
<header className="header-container">
<div className="select-container">
<Select
defaultValue={defaultValue}
onChange={handleChange}
options={options}
placeholder="Choisissez une ville"
className="select"
/>
</div>
<div className="logo-container">
<h2>Éco Météo</h2>
</div>
<div className="info-container">
<p className="city-name">{ville || 'Ville non sélectionnée'}</p>
<p className="date">{formattedDate}</p>
</div>
</main>
)
</header>
);
}

export default Header
export default Header;
55 changes: 55 additions & 0 deletions frontend/src/WeatherCard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#WeatherCard {
background-color: var(--card-background, #ffffff);
border-radius: 10px;
padding: 2em;
box-shadow: 0 4px 6px var(--shadow-color, rgba(0, 0, 0, 0.1));
margin: 1.5rem 0;
width: 100%;
max-width: 100%;
text-align: left;
transition: transform 300ms, box-shadow 300ms;
overflow: hidden;
}

.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1.5rem;
font-size: 1.1rem;
color: var(--text-color, #333);
}

.grid-item {
background-color: #f7f7f7;
padding: 1.5em;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: background-color 300ms ease;
}

.grid-item .label {
font-weight: bold;
color: var(--primary-color, #0077cc);
margin-bottom: 0.5em;
display: block;
}

.grid-item span {
font-size: 1.2rem;
color: #333;
}

@media (max-width: 768px) {
#WeatherCard {
padding: 1.5em;
}

.grid {
grid-template-columns: 1fr;
}

.grid-item {
padding: 1.2em;
}
}

47 changes: 47 additions & 0 deletions frontend/src/WeatherCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/fr';
import './WeatherCard.css';

dayjs.extend(relativeTime);
dayjs.locale('fr');

const WeatherCard = ({ data }) => {
const temperatureString = data?.meteo?.temperature;
const temperature = temperatureString ? parseInt(temperatureString.split("°")[0], 10) : null;

const cardStyle = {
backgroundColor: temperature !== null
? temperature > 15
? "#ff8c00"
: temperature < 10
? "#1e90ff"
: "#87ceeb"
: "#ffffff",
};

return (
<div className="card" style={cardStyle} id="WeatherCard">
<div className="grid">
<div className="grid-item">
<span className="label">Température:</span>
<span>{temperature !== null ? `${temperature}°C` : "Non disponible"}</span>
</div>
<div className="grid-item">
<span className="label">Précipitation:</span>
<span>{data?.meteo?.precipitation ?? "Non disponible"}</span>
</div>
<div className="grid-item">
<span className="label">Humidité:</span>
<span>{data?.meteo?.humidity ?? "Non disponible"}</span>
</div>
<div className="grid-item">
<span className="label">Vent:</span>
<span>{data?.meteo?.wind ?? "Non disponible"}</span>
</div>
</div>
</div>
);
};

export default WeatherCard;
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading