From 13f2dedf139f962f0e298c7211dfd8cb504e1687 Mon Sep 17 00:00:00 2001 From: neodk2004 Date: Thu, 25 Dec 2025 11:15:47 +0100 Subject: [PATCH] feat(pets): add birthdate-based age system with birthday support - introduce central pet age utilities (calculate, format, birthday check) - replace static pet.age with dynamic birthDate logic - display growing age correctly in pet detail view - prepare foundation for birthday highlights and notifications - fix duplicate imports and ensure stable build --- frontend/app/globals.css | 4 +- frontend/app/lib/petAge.ts | 34 ++++ frontend/app/pets/[id]/page.tsx | 10 +- frontend/app/pets/new/page.tsx | 265 ++++++++++++++++---------------- 4 files changed, 173 insertions(+), 140 deletions(-) create mode 100644 frontend/app/lib/petAge.ts diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 67f57d2..2448dbc 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -180,11 +180,11 @@ body { padding: var(--space-lg) var(--space-md) var(--space-md); } -.feed-header p, +/*.feed-header p, .explore-header p { color: var(--color-text-secondary); margin: 0.25rem 0 0; -} +}*/ .post-list { display: flex; flex-direction: column; diff --git a/frontend/app/lib/petAge.ts b/frontend/app/lib/petAge.ts new file mode 100644 index 0000000..32a35b8 --- /dev/null +++ b/frontend/app/lib/petAge.ts @@ -0,0 +1,34 @@ +export function calculatePetAge(birthDate: string) { + const today = new Date(); + const birth = new Date(birthDate); + + let age = today.getFullYear() - birth.getFullYear(); + const hasHadBirthdayThisYear = + today.getMonth() > birth.getMonth() || + (today.getMonth() === birth.getMonth() && + today.getDate() >= birth.getDate()); + + if (!hasHadBirthdayThisYear) { + age -= 1; + } + + return age < 0 ? 0 : age; +} + +export function isBirthdayToday(birthDate: string) { + const today = new Date(); + const birth = new Date(birthDate); + + return ( + today.getDate() === birth.getDate() && + today.getMonth() === birth.getMonth() + ); +} + +export function formatPetAge(birthDate: string) { + const age = calculatePetAge(birthDate); + + if (age === 0) return 'unter 1 Jahr'; + if (age === 1) return '1 Jahr'; + return `${age} Jahre`; +} diff --git a/frontend/app/pets/[id]/page.tsx b/frontend/app/pets/[id]/page.tsx index 1b9bc50..fdb7512 100644 --- a/frontend/app/pets/[id]/page.tsx +++ b/frontend/app/pets/[id]/page.tsx @@ -2,6 +2,9 @@ import { useParams } from 'next/navigation'; import PostCard from '@/app/components/PostCard/PostCard'; +import {calculatePetAge, formatPetAge, isBirthdayToday,} from '@/app/lib/petAge'; + + type Pet = { id: number; @@ -94,9 +97,10 @@ export default function PetDetailPage() {

{pet.name}

- {pet.type} · {pet.breed} - {pet.age && ` · ${pet.age}`} - + {pet.type} · {pet.breed} + {pet.birthDate && ` · ${formatPetAge(pet.birthDate)}`} + + Betreut von {pet.owner} diff --git a/frontend/app/pets/new/page.tsx b/frontend/app/pets/new/page.tsx index 880ef5f..e528896 100644 --- a/frontend/app/pets/new/page.tsx +++ b/frontend/app/pets/new/page.tsx @@ -3,43 +3,42 @@ import { useState } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; - import { dogBreeds, catBreeds } from '@/app/data/breeds'; type PetType = 'Hund' | 'Katze' | ''; export default function NewPetPage() { + const router = useRouter(); + + // Pflichtfelder const [name, setName] = useState(''); const [type, setType] = useState(''); const [breed, setBreed] = useState(''); - const [breedSearch, setBreedSearch] = useState(''); + + // Erweiterte Felder + const [birthDate, setBirthDate] = useState(''); + const [ageFallback, setAgeFallback] = useState(''); + const [gender, setGender] = useState<'Männlich' | 'Weiblich' | 'Unbekannt'>( + 'Unbekannt' + ); const [bio, setBio] = useState(''); - const [error, setError] = useState(''); - const [success, setSuccess] = useState(''); -const router = useRouter(); - -function handleCancel() { - router.back(); // geht zur vorherigen Seite zurück -} - - const breeds = - type === 'Hund' - ? dogBreeds - : type === 'Katze' - ? catBreeds - : []; - - const filteredBreeds = breeds.filter((b) => - b.toLowerCase().includes(breedSearch.toLowerCase()) + const [visibility, setVisibility] = useState<'public' | 'followers'>( + 'public' ); - function handleSubmit(event: React.FormEvent) { - event.preventDefault(); + // UX States + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + + const breeds = type === 'Hund' ? dogBreeds : catBreeds; + + function handleSubmit(e: React.FormEvent) { + e.preventDefault(); setError(''); setSuccess(''); if (!name.trim()) { - setError('Bitte gib einen Namen für dein Haustier ein.'); + setError('Bitte gib einen Namen für dein Tier an.'); return; } @@ -49,120 +48,80 @@ function handleCancel() { } if (!breed) { - setError('Bitte wähle eine Rasse aus.'); + setError('Bitte wähle eine Rasse oder „Mix“ aus.'); return; } - const newPetPayload = { + const petPayload = { name, type, breed, - bio: bio || null, - createdAt: new Date().toISOString(), + birthDate: birthDate || null, + ageFallback: birthDate ? null : ageFallback || null, + gender, + bio, + visibility, }; - console.log('Neues Haustier (Mock):', newPetPayload); + console.log('NEW PET:', petPayload); - setSuccess('Haustier wurde technisch angelegt ✅'); - setName(''); - setType(''); - setBreed(''); - setBreedSearch(''); - setBio(''); + setSuccess('Haustier wurde erfolgreich angelegt.'); + setTimeout(() => { + router.push('/pets'); + }, 800); + } + + function handleCancel() { + router.back(); } return ( -
-
-

Haustier hinzufügen

-

- Lege ein neues Haustier für dein Profil an. -

+
+
+

Haustier hinzufügen

+

Lege ein neues Tier für dein Profil an.

-
+ {/* Name */} -
- Name +
+ setName(e.target.value)} placeholder="z. B. Luna" - style={{ - padding: '0.7rem', - borderRadius: '10px', - border: '1px solid #ccc', - }} />
- {/* Art */} -
- Tier-Art + {/* Tier-Art */} +
+
{/* Rasse */} {type && ( -
- Rasse - - setBreedSearch(e.target.value)} - style={{ - padding: '0.6rem', - borderRadius: '10px', - border: '1px solid #ccc', - }} - /> - - setBreed(e.target.value)}> + + {breeds.map((b) => ( @@ -171,58 +130,94 @@ function handleCancel() {
)} + {/* Geburtsdatum */} +
+ + { + setBirthDate(e.target.value); + if (e.target.value) setAgeFallback(''); + }} + /> + + Wenn bekannt – das Alter wird automatisch berechnet. + +
+ + {/* Fallback Alter */} + {!birthDate && ( +
+ + setAgeFallback(e.target.value)} + placeholder="z. B. 3" + /> + Optional – eine grobe Angabe reicht. +
+ )} + + {/* Geschlecht */} +
+ + +
+ {/* Bio */} -
- Kurzbeschreibung (optional) +
+