v1 release

- various formatting changes
- info card displays vipps information
- sanity integration for contact information, can append events later
- time is displayed in human-readable format to avoid confusion (e.g., ISO vs US vs EU)
This commit is contained in:
centauridesigns committed 2024-10-15 15:13:53 +02:00
1 parent 96ef605ab4
commit 26993e3520
13 files changed
+231 -57

No files matched your search

+3
View File
@@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
# config
config.tsx
+49 -20
View File
@@ -21,14 +21,26 @@ interface Place {
timeDiffMs?: number;
}
interface EventCardProps {
recurringTimeAhead: string;
specialTimeAhead: string;
placeTimeAhead: string;
}
const recurringEvents: RecurringEvent[] = eventItems.recurring_events;
const specialEvents: SpecialEvent[] = eventItems.special_events;
const places: Place[] = eventItems.places.map((place: any) => ({
name: place.name,
closingHours: new Map(place.closing_times.map((time: any) => [time.day, time.time])),
closingHours: new Map(
place.closing_times.map((time: any) => [time.day, time.time])
),
}));
const EventCard: React.FC = () => {
const EventCard: React.FC<EventCardProps> = ({
recurringTimeAhead,
specialTimeAhead,
placeTimeAhead,
}) => {
const [upcomingEvents, setUpcomingEvents] = useState<
(RecurringEvent | SpecialEvent | Place)[]
>([]);
@@ -36,21 +48,24 @@ const EventCard: React.FC = () => {
useEffect(() => {
const updateEvents = () => {
const now = new Date();
// Handle recurring events
// handle recurring events
const eventsWithCountdown = recurringEvents.map((event) => {
const eventTime = getNextRecurringEvent(event.day, event.time || "00:00");
const eventTime = getNextRecurringEvent(
event.day,
event.time || "00:00"
);
const timeDiffMs = eventTime.getTime() - now.getTime();
return { ...event, timeDiffMs };
});
// Handle places' closing times
// handle places' closing times
const placesWithCountdown = places.map((place) => {
const closingTime = getNextClosingTime(place.closingHours);
const timeDiffMs = closingTime.getTime() - now.getTime();
return { ...place, timeDiffMs };
});
// Handle special events
// handle special events
const specialEventsWithCountdown = specialEvents.map((event) => {
const eventTime = new Date(`${event.date}T${event.time}`);
const timeDiffMs = eventTime.getTime() - now.getTime();
@@ -59,17 +74,23 @@ const EventCard: React.FC = () => {
const filteredEvents = [
...eventsWithCountdown.filter(
(event) => event.timeDiffMs! > 0 && event.timeDiffMs! <= 48 * 60 * 60 * 1000
),
...placesWithCountdown.filter(
(place) => place.timeDiffMs! > 0 && place.timeDiffMs! <= 4 * 60 * 60 * 1000
(event) =>
event.timeDiffMs! > 0 &&
event.timeDiffMs! <= parseInt(recurringTimeAhead) * 60 * 60 * 1000 // 4 days
),
...specialEventsWithCountdown.filter(
(event) => event.timeDiffMs! > 0 && event.timeDiffMs! <= 48 * 60 * 60 * 1000
(event) =>
event.timeDiffMs! > 0 &&
event.timeDiffMs! <= parseInt(specialTimeAhead) * 60 * 60 * 1000 // 4 days
),
...placesWithCountdown.filter(
(place) =>
place.timeDiffMs! > 0 &&
place.timeDiffMs! <= parseInt(placeTimeAhead) * 60 * 60 * 1000 // 4 hours
),
];
// Sort events by time difference
// sort events by time difference
filteredEvents.sort((a, b) => a.timeDiffMs! - b.timeDiffMs!);
setUpcomingEvents(filteredEvents);
@@ -85,13 +106,18 @@ const EventCard: React.FC = () => {
<div className="flex flex-col bg-foreground rounded-lg p-3">
<div className="flex flex-col gap-3">
{upcomingEvents.map((event) => (
<div className="flex flex-1 flex-row bg-primary rounded-lg p-3 justify-between space" key={event.name}>
<div
className="flex flex-1 flex-row bg-primary rounded-lg p-3 justify-between space"
key={event.name}
>
<div className="flex flex-row gap-3">
<p className="event-type">{'closingHours' in event ? "Closing" : "Event"}</p>
<p>{event.name}</p>
<p className="parabold">
{"closingHours" in event ? "Closing" : "Event"}
</p>
<p className="para">{event.name}</p>
</div>
<div className="flex flex-col gap-3">
<p>{getTimeDifference(event.timeDiffMs ?? 0)}</p>
<p className="para">{getTimeDifference(event.timeDiffMs ?? 0)}</p>
</div>
</div>
))}
@@ -156,7 +182,7 @@ const getNextClosingTime = (closingHours: Map<string, string>): Date => {
const closingTime = closingHours.get(currentDay);
if (!closingTime) {
return now; // If no closing time for today, return now (or handle differently)
return now;
}
const [closingHour, closingMinute] = closingTime.split(":").map(Number);
@@ -164,12 +190,15 @@ const getNextClosingTime = (closingHours: Map<string, string>): Date => {
closingDate.setHours(closingHour, closingMinute, 0, 0);
if (now > closingDate) {
// If already past today's closing time, get the next day's closing time
const nextDay = new Date(now.getTime() + 24 * 60 * 60 * 1000); // add 1 day
const nextDayName = nextDay.toLocaleDateString("en-US", { weekday: "long" });
const nextDayName = nextDay.toLocaleDateString("en-US", {
weekday: "long",
});
const nextClosingTime = closingHours.get(nextDayName);
if (nextClosingTime) {
const [nextClosingHour, nextClosingMinute] = nextClosingTime.split(":").map(Number);
const [nextClosingHour, nextClosingMinute] = nextClosingTime
.split(":")
.map(Number);
closingDate.setDate(closingDate.getDate() + 1); // Move to the next day
closingDate.setHours(nextClosingHour, nextClosingMinute, 0, 0);
}
+37
View File
@@ -1 +1,38 @@
import React, { useEffect, useState } from "react";
import vipps from "../../public/images/vipps.png";
import Image from 'next/image';
import SanityPeople from "../people";
import SanityEvents from "../events";
const InfoCard: React.FC = () => {
interface Person {
name: string;
telephone: string;
}
interface Event {
name: string;
date: string;
time: string;
timeDiffMs?: number;
}
const data: Person[] = SanityPeople();
const person = data.find((person: Person) => person.telephone) || { name: "", telephone: "" };
return (
<div className="flex flex-col bg-primary rounded-lg p-3">
<div className="flex flex-row gap-3 place-content-between items-center">
<div className="w-1/4">
<Image src={vipps} alt="Vipps"/>
</div>
<div className="flex flex-1 flex-col items-end">
<p className="parabold">{person.name}</p>
<p className="para">{person.telephone}</p>
</div>
</div>
</div>
);
};
export default InfoCard;
+4 -3
View File
@@ -1,6 +1,7 @@
import React from "react";
import menuItems from "../../public/menuItems.json";
import MenuSection from "./MenuSection";
import InfoCard from "./InfoCard";
function MenuCard() {
return (
@@ -16,9 +17,9 @@ function MenuCard() {
<MenuSection title="Soda and Energy Drinks" items={menuItems.Soda}></MenuSection>
{/*bottom box*/}
<div className="flex-1 bg-primary rounded-lg p-3">
<h1 className="text-headers">Info</h1>
</div>
<InfoCard>
</InfoCard>
</div>
</div>
+1 -1
View File
@@ -28,7 +28,7 @@ const MenuSection: React.FC<MenuSectionProps> = ({ title, items }) => {
<div className="flex flex-col gap-0.5">
{items.map((item) => (
<div key={item.name} className="flex flex-row justify-end">
<p className="text-headers text-right">kr {item.price}</p>
<p className="text-p text-right">kr {item.price}</p>
</div>
))}
</div>
+5 -5
View File
@@ -11,7 +11,7 @@ const TemperatureCard: React.FC<TemperatureCardProps> = ({
fiveHourForecast,
}) => {
return (
<div className="flex flex-1 flex-col bg-foreground rounded-lg p-3 gap-3 h-full">
<div className="flex flex-col bg-foreground rounded-lg p-3 gap-3">
{/* two vertical containers */}
<div className="flex flex-row gap-3">
{/* left container */}
@@ -23,10 +23,10 @@ const TemperatureCard: React.FC<TemperatureCardProps> = ({
</div>
<div className="flex flex-col bg-primary rounded-lg p-3">
<div className="flex flex-row place-content-between">
<div className="text-headers text-p forecasts">
<div className="text-headers text-p para">
{precipitation + " mm"}
</div>
<div className="text-headers text-p forecasts">
<div className="text-headers text-p para">
{wind.speed + "(" + wind.gust + ") m/s"}
</div>
</div>
@@ -41,8 +41,8 @@ const TemperatureCard: React.FC<TemperatureCardProps> = ({
className="flex flex-1 flex-col bg-primary rounded-lg gap-3 p-3 place-content-center"
>
<div className="flex justify-between content-center">
<div className="text-headers text-p forecasts">{item.time}</div>
<div className="text-headers text-p forecasts">{item.temperature}°</div>
<div className="text-headers text-p para">{item.time}</div>
<div className="text-headers text-p para">{item.temperature}°</div>
</div>
</div>
))}
+32 -10
View File
@@ -24,26 +24,48 @@ function TimeCard() {
const formatDate = (date: Date) => {
const day = date.getDate().toString().padStart(2, "0");
const ending = findDateEnding(date);
const month = (date.getMonth() + 1).toString().padStart(2, "0");
const monthText = date.toLocaleString("default", { month: "long" });
const year = date.getFullYear();
return `${day}.${month}.${year}`;
}
return `${day}${ending} ${monthText} ${year}`;
};
const day = (date: Date) => {
const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
const days = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
return days[date.getDay()];
}
};
const findDateEnding = (date: Date) => {
const day = date.getDate();
if (day === 1 || day === 21 || day === 31) {
return "st";
} else if (day === 2 || day === 22) {
return "nd";
} else if (day === 3 || day === 23) {
return "rd";
} else {
return "th";
}
};
return (
<div className="flex flex-col bg-foreground rounded-lg p-3">
<div className="flex flex-row gap-3">
<div className="flex flex-1 flex-row bg-primary rounded-lg p-3 justify-evenly items-center">
<p>{formatDate(date)}</p>
<div className="text-headers text-p time">
{formatTime(time)}
</div>
<p>{day(date)}</p>
<div className="flex flex-1 flex-row bg-primary rounded-lg p-5 pl-3 pr-3 justify-between items-center">
<p className="text-p para">{formatDate(date)}</p>
<div className="absolute left-1/2 transform -translate-x-1/2 text-center text-headers text-p time">{formatTime(time)}</div>
<p className="text-p para">{day(date)}</p>
</div>
</div>
</div>
+36
View File
@@ -0,0 +1,36 @@
import React, { useEffect, useState } from "react";
const SanityEvents = () => {
const [events, setEvents] = useState([]);
const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID;
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET;
useEffect(() => {
const fetchData = async () => {
const query = encodeURIComponent('*[_type == "event"]');
const url = `https://${projectId}.api.sanity.io/v1/data/query/${dataset}?query=${query}`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
setEvents(data.result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchData();
}, [projectId, dataset]);
return event;
}
export default SanityEvents;
+12 -7
View File
@@ -7,6 +7,7 @@
--foreground: #261F31;
--primary: #392e49;
--headers: #D64097;
--overlay: #4A3B61;
}
@media (prefers-color-scheme: dark) {
@@ -15,6 +16,7 @@
--foreground: #261F31;
--primary: #392e49;
--headers: #D64097;
--overlay: #4A3B61;
}
}
@@ -42,21 +44,24 @@ p {
color: white !important;
}
.forecasts {
.para {
font-family: var(--font-nunito);
font-weight: 400;
font-size: 1rem;
color: white !important;
}
.parabold {
font-family: var(--font-nunito);
font-weight: 800;
font-size: 1rem;
color: white !important;
}
.time {
font-family: var(--font-nunito);
font-weight: 800;
color: white !important;
}
.event-type {
font-family: var(--font-nunito);
font-weight: 800;
font-size: 2rem;
color: white !important;
}
+15 -11
View File
@@ -73,14 +73,16 @@ export default function Home() {
const currentTime = new Date();
const timeseries = data.properties.timeseries;
const nearest = data.properties.timeseries.reduce((prev: { time: string }, curr: { time: string }) => {
const prevTime = new Date(prev.time);
const currTime = new Date(curr.time);
return Math.abs(currTime.getTime() - currentTime.getTime()) <
Math.abs(prevTime.getTime() - currentTime.getTime())
? curr
: prev;
});
const nearest = data.properties.timeseries.reduce(
(prev: { time: string }, curr: { time: string }) => {
const prevTime = new Date(prev.time);
const currTime = new Date(curr.time);
return Math.abs(currTime.getTime() - currentTime.getTime()) <
Math.abs(prevTime.getTime() - currentTime.getTime())
? curr
: prev;
}
);
setnearestHour(nearest.time);
@@ -146,10 +148,12 @@ export default function Home() {
wind={currentWind}
fiveHourForecast={nextThreeHours}
/>
<EventCard></EventCard>
<EventCard
recurringTimeAhead="96"
specialTimeAhead="96"
placeTimeAhead="4"
></EventCard>
<TimeCard></TimeCard>
<div className="flex-1 bg-green-500"></div>
</div>
);
}
+36
View File
@@ -0,0 +1,36 @@
import React, { useEffect, useState } from "react";
const SanityPeople = () => {
const [people, setPeople] = useState([]);
const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID;
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET;
useEffect(() => {
const fetchData = async () => {
const query = encodeURIComponent('*[_type == "author"]');
const url = `https://${projectId}.api.sanity.io/v1/data/query/${dataset}?query=${query}`;
try {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
setPeople(data.result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchData();
}, [projectId, dataset]);
return people;
}
export default SanityPeople;
File renamed without changes.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

+1
View File
@@ -14,6 +14,7 @@ const config: Config = {
foreground: "var(--foreground)",
primary: "var(--primary)",
headers: "var(--headers)",
overlay: "var(--overlay)",
},
},
},