Skip to content
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
8 changes: 0 additions & 8 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
reactStrictMode: true,
images: {
loader: "akamai",
path: "",
unoptimized: true,
},
basePath: "",
assetPrefix: "",
};

module.exports = nextConfig;
30 changes: 18 additions & 12 deletions src/app/auth/callback/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,58 @@

import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { supabase } from "@/lib/supabase";
import { supabase } from "@/database/supabase";
import Image from "next/image";
import logo from "@/public/home/cookCraftLogo.webp";

const AuthCallbackPage = () => {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);

useEffect(() => {
const handleCallback = async () => {
const handleAuthCallback = async () => {
try {
const hashParams = new URLSearchParams(
window.location.hash.substring(1),
);
const urlHash = window.location.hash.substring(1);
const hashParams = new URLSearchParams(urlHash);

const accessToken = hashParams.get("access_token");
const refreshToken = hashParams.get("refresh_token");

if (accessToken && refreshToken) {
const { error } = await supabase.auth.setSession({
const sessionResult = await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken,
});

if (error) throw error;
if (sessionResult.error) {
throw sessionResult.error;
}

router.push("/profile");
} else {
throw new Error("No tokens found in URL");
}
} catch (err) {
setError(err instanceof Error ? err.message : "Authentication failed");
if (err instanceof Error) {
setErrorMessage(err.message);
} else {
setErrorMessage("Authentication failed");
}
}
};

handleCallback();
handleAuthCallback();
}, [router]);

if (error) {
if (errorMessage) {
return (
<div className="bg-cookcraft-white flex h-screen w-screen flex-col items-center justify-center">
<Image src={logo} alt="CookCraft Logo" className="mb-6 w-48" />
<div className="border-cookcraft-red bg-cookcraft-white flex w-full max-w-md flex-col gap-4 rounded-2xl border-3 p-8">
<h2 className="text-cookcraft-red text-2xl font-bold">
Authentication Error
</h2>
<p className="text-cookcraft-olive">{error}</p>
<p className="text-cookcraft-olive">{errorMessage}</p>
<button
onClick={() => router.push("/login")}
className="bg-cookcraft-red hover:bg-cookcraft-yellow mt-2 rounded-2xl p-4 text-lg font-bold text-white transition-colors"
Expand Down
116 changes: 109 additions & 7 deletions src/app/history/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,114 @@
import HistoryCards from "@/components/history/HistoryCards";
import Header from "@/components/Header";
const History = () => {
"use client";

import { useEffect, useState } from "react";
import { getMealHistory, deleteMeal } from "@/database/api/meals";
import { MealWithRecipe } from "@/types/database";

const HistoryPage = () => {
const [mealsList, setMealsList] = useState<MealWithRecipe[]>([]);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
loadMealsFromDatabase();
}, []);

const loadMealsFromDatabase = async () => {
try {
const mealsData = await getMealHistory();
setMealsList(mealsData);
setIsLoading(false);
} catch (error) {
console.error(error);
setIsLoading(false);
}
};

const handleDeleteMeal = async (mealId: string) => {
const confirmDelete = confirm("Delete this meal entry?");

if (confirmDelete) {
try {
await deleteMeal(mealId);
loadMealsFromDatabase();
} catch (error) {
console.error(error);
}
}
};

if (isLoading) {
return (
<div className="bg-cookcraft-white text-cookcraft-olive flex h-screen w-screen flex-col items-center justify-center">
<div className="border-cookcraft-olive h-12 w-12 animate-spin rounded-full border-4 border-t-transparent"></div>
<p className="mt-4 text-xl font-medium">Loading your meal history...</p>
</div>
);
}

return (
<div className="bg-cookcraft-white h-screen w-screen">
<Header title="History" />
<HistoryCards />
<div className="bg-cookcraft-white flex min-h-screen w-screen flex-col items-center py-20">
<div className="w-full max-w-4xl px-8">
<h1 className="text-cookcraft-olive mb-8 text-4xl font-bold">
Meal History
</h1>

{mealsList.length === 0 ? (
<div className="border-cookcraft-olive bg-cookcraft-white rounded-2xl border-3 p-12 text-center">
<p className="text-cookcraft-olive text-xl">
No meals logged yet. Log meals from your recipes!
</p>
</div>
) : (
<div className="flex flex-col gap-4">
{mealsList.map((mealItem) => {
const mealDate = new Date(mealItem.eaten_at);
const formattedDate = mealDate.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});

return (
<div
key={mealItem.id}
className="border-cookcraft-olive bg-cookcraft-white rounded-2xl border-3 p-6"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<h2 className="text-cookcraft-olive text-2xl font-bold">
{mealItem.meal_name}
</h2>
{mealItem.notes && (
<p className="text-cookcraft-olive mt-2 text-sm">
{mealItem.notes}
</p>
)}
<div className="mt-3 flex gap-4 text-sm">
<span className="text-cookcraft-olive">
Servings: {mealItem.servings}
</span>
<span className="text-cookcraft-olive">
{formattedDate}
</span>
</div>
</div>
<button
onClick={() => handleDeleteMeal(mealItem.id)}
className="text-cookcraft-red hover:text-cookcraft-yellow rounded-2xl border-3 px-4 py-2 font-bold transition-colors"
>
Delete
</button>
</div>
</div>
);
})}
</div>
)}
</div>
</div>
);
};

export default History;
export default HistoryPage;
Loading
Loading