refactor: streamline date and title handling in NewWeatherPage and NewWeeklyCheckInPage components for improved user experience
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -19,16 +19,15 @@ export default function NewWeatherPage() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
|
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState(() => getWeekYearLabel(new Date(new Date().toISOString().split('T')[0])));
|
||||||
|
const [isTitleManuallyEdited, setIsTitleManuallyEdited] = useState(false);
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setError(null);
|
setError(null);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const formData = new FormData(e.currentTarget);
|
const date = selectedDate ? new Date(selectedDate) : undefined;
|
||||||
const dateStr = formData.get('date') as string;
|
|
||||||
const date = dateStr ? new Date(dateStr) : undefined;
|
|
||||||
|
|
||||||
if (!title) {
|
if (!title) {
|
||||||
setError('Veuillez remplir le titre');
|
setError('Veuillez remplir le titre');
|
||||||
@@ -47,13 +46,19 @@ export default function NewWeatherPage() {
|
|||||||
router.push(`/weather/${result.data?.id}`);
|
router.push(`/weather/${result.data?.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default date to today
|
function handleDateChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const newDate = e.target.value;
|
||||||
|
setSelectedDate(newDate);
|
||||||
|
// Only update title if user hasn't manually modified it
|
||||||
|
if (!isTitleManuallyEdited) {
|
||||||
|
setTitle(getWeekYearLabel(new Date(newDate)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update title when date changes
|
function handleTitleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
useEffect(() => {
|
setTitle(e.target.value);
|
||||||
setTitle(getWeekYearLabel(new Date(selectedDate)));
|
setIsTitleManuallyEdited(true);
|
||||||
}, [selectedDate]);
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="mx-auto max-w-2xl px-4 py-8">
|
<main className="mx-auto max-w-2xl px-4 py-8">
|
||||||
@@ -81,7 +86,7 @@ export default function NewWeatherPage() {
|
|||||||
name="title"
|
name="title"
|
||||||
placeholder="Ex: Météo S05 - 2026"
|
placeholder="Ex: Météo S05 - 2026"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={handleTitleChange}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -93,8 +98,8 @@ export default function NewWeatherPage() {
|
|||||||
id="date"
|
id="date"
|
||||||
name="date"
|
name="date"
|
||||||
type="date"
|
type="date"
|
||||||
defaultValue={today}
|
value={selectedDate}
|
||||||
onChange={(e) => setSelectedDate(e.target.value)}
|
onChange={handleDateChange}
|
||||||
required
|
required
|
||||||
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -19,7 +19,8 @@ export default function NewWeeklyCheckInPage() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
|
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState(() => getWeekYearLabel(new Date(new Date().toISOString().split('T')[0])));
|
||||||
|
const [isTitleManuallyEdited, setIsTitleManuallyEdited] = useState(false);
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -28,8 +29,7 @@ export default function NewWeeklyCheckInPage() {
|
|||||||
|
|
||||||
const formData = new FormData(e.currentTarget);
|
const formData = new FormData(e.currentTarget);
|
||||||
const participant = formData.get('participant') as string;
|
const participant = formData.get('participant') as string;
|
||||||
const dateStr = formData.get('date') as string;
|
const date = selectedDate ? new Date(selectedDate) : undefined;
|
||||||
const date = dateStr ? new Date(dateStr) : undefined;
|
|
||||||
|
|
||||||
if (!title || !participant) {
|
if (!title || !participant) {
|
||||||
setError('Veuillez remplir tous les champs');
|
setError('Veuillez remplir tous les champs');
|
||||||
@@ -48,13 +48,19 @@ export default function NewWeeklyCheckInPage() {
|
|||||||
router.push(`/weekly-checkin/${result.data?.id}`);
|
router.push(`/weekly-checkin/${result.data?.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default date to today
|
function handleDateChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const newDate = e.target.value;
|
||||||
|
setSelectedDate(newDate);
|
||||||
|
// Only update title if user hasn't manually modified it
|
||||||
|
if (!isTitleManuallyEdited) {
|
||||||
|
setTitle(getWeekYearLabel(new Date(newDate)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update title when date changes
|
function handleTitleChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
useEffect(() => {
|
setTitle(e.target.value);
|
||||||
setTitle(getWeekYearLabel(new Date(selectedDate)));
|
setIsTitleManuallyEdited(true);
|
||||||
}, [selectedDate]);
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="mx-auto max-w-2xl px-4 py-8">
|
<main className="mx-auto max-w-2xl px-4 py-8">
|
||||||
@@ -83,7 +89,7 @@ export default function NewWeeklyCheckInPage() {
|
|||||||
name="title"
|
name="title"
|
||||||
placeholder="Ex: Check-in semaine du 15 janvier"
|
placeholder="Ex: Check-in semaine du 15 janvier"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={handleTitleChange}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -102,8 +108,8 @@ export default function NewWeeklyCheckInPage() {
|
|||||||
id="date"
|
id="date"
|
||||||
name="date"
|
name="date"
|
||||||
type="date"
|
type="date"
|
||||||
defaultValue={today}
|
value={selectedDate}
|
||||||
onChange={(e) => setSelectedDate(e.target.value)}
|
onChange={handleDateChange}
|
||||||
required
|
required
|
||||||
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
className="w-full rounded-lg border border-border bg-input px-3 py-2 text-foreground outline-none focus:border-primary focus:ring-2 focus:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -128,23 +128,23 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit }: Weathe
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Performance */}
|
{/* Performance */}
|
||||||
<td className="px-4 py-3">
|
<td className="w-24 px-2 py-3">
|
||||||
{canEditThis ? (
|
{canEditThis ? (
|
||||||
<div className="relative">
|
<div className="relative mx-auto w-fit">
|
||||||
<select
|
<select
|
||||||
value={performanceEmoji || ''}
|
value={performanceEmoji || ''}
|
||||||
onChange={(e) => handleEmojiChange('performance', e.target.value || null)}
|
onChange={(e) => handleEmojiChange('performance', e.target.value || null)}
|
||||||
className="w-full appearance-none rounded-lg border border-border bg-card px-3 py-2.5 pr-10 text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
className="w-16 appearance-none rounded-lg border border-border bg-card px-2 py-2.5 pr-8 text-center text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
{WEATHER_EMOJIS.map(({ emoji, label }) => (
|
{WEATHER_EMOJIS.map(({ emoji }) => (
|
||||||
<option key={emoji || 'none'} value={emoji}>
|
<option key={emoji || 'none'} value={emoji}>
|
||||||
{emoji} {label}
|
{emoji}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
<div className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2">
|
||||||
<svg
|
<svg
|
||||||
className="h-4 w-4 text-muted"
|
className="h-3 w-3 text-muted"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -159,23 +159,23 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit }: Weathe
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Moral */}
|
{/* Moral */}
|
||||||
<td className="px-4 py-3">
|
<td className="w-24 px-2 py-3">
|
||||||
{canEditThis ? (
|
{canEditThis ? (
|
||||||
<div className="relative">
|
<div className="relative mx-auto w-fit">
|
||||||
<select
|
<select
|
||||||
value={moralEmoji || ''}
|
value={moralEmoji || ''}
|
||||||
onChange={(e) => handleEmojiChange('moral', e.target.value || null)}
|
onChange={(e) => handleEmojiChange('moral', e.target.value || null)}
|
||||||
className="w-full appearance-none rounded-lg border border-border bg-card px-3 py-2.5 pr-10 text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
className="w-16 appearance-none rounded-lg border border-border bg-card px-2 py-2.5 pr-8 text-center text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
{WEATHER_EMOJIS.map(({ emoji, label }) => (
|
{WEATHER_EMOJIS.map(({ emoji }) => (
|
||||||
<option key={emoji || 'none'} value={emoji}>
|
<option key={emoji || 'none'} value={emoji}>
|
||||||
{emoji} {label}
|
{emoji}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
<div className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2">
|
||||||
<svg
|
<svg
|
||||||
className="h-4 w-4 text-muted"
|
className="h-3 w-3 text-muted"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -190,23 +190,23 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit }: Weathe
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Flux */}
|
{/* Flux */}
|
||||||
<td className="px-4 py-3">
|
<td className="w-24 px-2 py-3">
|
||||||
{canEditThis ? (
|
{canEditThis ? (
|
||||||
<div className="relative">
|
<div className="relative mx-auto w-fit">
|
||||||
<select
|
<select
|
||||||
value={fluxEmoji || ''}
|
value={fluxEmoji || ''}
|
||||||
onChange={(e) => handleEmojiChange('flux', e.target.value || null)}
|
onChange={(e) => handleEmojiChange('flux', e.target.value || null)}
|
||||||
className="w-full appearance-none rounded-lg border border-border bg-card px-3 py-2.5 pr-10 text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
className="w-16 appearance-none rounded-lg border border-border bg-card px-2 py-2.5 pr-8 text-center text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
{WEATHER_EMOJIS.map(({ emoji, label }) => (
|
{WEATHER_EMOJIS.map(({ emoji }) => (
|
||||||
<option key={emoji || 'none'} value={emoji}>
|
<option key={emoji || 'none'} value={emoji}>
|
||||||
{emoji} {label}
|
{emoji}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
<div className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2">
|
||||||
<svg
|
<svg
|
||||||
className="h-4 w-4 text-muted"
|
className="h-3 w-3 text-muted"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -221,23 +221,23 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit }: Weathe
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Création de valeur */}
|
{/* Création de valeur */}
|
||||||
<td className="px-4 py-3">
|
<td className="w-24 px-2 py-3">
|
||||||
{canEditThis ? (
|
{canEditThis ? (
|
||||||
<div className="relative">
|
<div className="relative mx-auto w-fit">
|
||||||
<select
|
<select
|
||||||
value={valueCreationEmoji || ''}
|
value={valueCreationEmoji || ''}
|
||||||
onChange={(e) => handleEmojiChange('valueCreation', e.target.value || null)}
|
onChange={(e) => handleEmojiChange('valueCreation', e.target.value || null)}
|
||||||
className="w-full appearance-none rounded-lg border border-border bg-card px-3 py-2.5 pr-10 text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
className="w-16 appearance-none rounded-lg border border-border bg-card px-2 py-2.5 pr-8 text-center text-lg text-foreground transition-colors hover:bg-card-hover focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20"
|
||||||
>
|
>
|
||||||
{WEATHER_EMOJIS.map(({ emoji, label }) => (
|
{WEATHER_EMOJIS.map(({ emoji }) => (
|
||||||
<option key={emoji || 'none'} value={emoji}>
|
<option key={emoji || 'none'} value={emoji}>
|
||||||
{emoji} {label}
|
{emoji}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<div className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2">
|
<div className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2">
|
||||||
<svg
|
<svg
|
||||||
className="h-4 w-4 text-muted"
|
className="h-3 w-3 text-muted"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -252,7 +252,7 @@ export function WeatherCard({ sessionId, currentUserId, entry, canEdit }: Weathe
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{/* Notes */}
|
{/* Notes */}
|
||||||
<td className="px-4 py-3 min-w-[300px]">
|
<td className="px-4 py-3 min-w-[400px]">
|
||||||
{canEditThis ? (
|
{canEditThis ? (
|
||||||
<Textarea
|
<Textarea
|
||||||
value={notes}
|
value={notes}
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export function WeatherShareModal({
|
|||||||
disabled={isPending || (shareType === 'user' && !email) || (shareType === 'team' && !teamId)}
|
disabled={isPending || (shareType === 'user' && !email) || (shareType === 'team' && !teamId)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
>
|
>
|
||||||
{isPending ? 'Partage...' : shareType === 'team' ? 'Partager à l'équipe' : 'Partager'}
|
{isPending ? 'Partage...' : shareType === 'team' ? "Partager à l'équipe" : 'Partager'}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user