mirror of
https://github.com/EdiFarcas/Car-Fuel-Tracking-App.git
synced 2026-06-22 07:00:55 +03:00
Background and Logo improvment
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 234 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 266 KiB |
@@ -0,0 +1,22 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { prisma } from '@/lib/prisma';
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
// Calculate average fuel price (RON/L), average efficiency (L/100km), and total CO2 saved (kg)
|
||||||
|
// For demo, fallback to fake values if not enough data
|
||||||
|
const fillUps = await prisma.fillUp.findMany({ select: { cost: true, liters: true, mileage: true, fuelType: true } });
|
||||||
|
let avgFuelPrice = 7.2;
|
||||||
|
let avgEfficiency = 6.8;
|
||||||
|
let totalCO2Saved = 12000;
|
||||||
|
if (fillUps.length > 0) {
|
||||||
|
avgFuelPrice = fillUps.reduce((sum, f) => sum + f.cost / f.liters, 0) / fillUps.length;
|
||||||
|
avgEfficiency = fillUps.reduce((sum, f) => sum + (f.liters / (f.mileage || 1)) * 100, 0) / fillUps.length;
|
||||||
|
// Assume 2.3kg CO2 per L gasoline, 0 for electric, 1.6 for hybrid (if fuelType is ELECTRIC or car has both)
|
||||||
|
totalCO2Saved = fillUps.filter(f => f.fuelType === 'ELECTRIC').length * 2.3 * 40; // fake: 40L per fillup
|
||||||
|
}
|
||||||
|
return NextResponse.json({
|
||||||
|
avgFuelPrice,
|
||||||
|
avgEfficiency,
|
||||||
|
totalCO2Saved,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ export default function LoginPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="w-full flex justify-center bg-[var(--background)] px-4 md:px-0 pt-10">
|
<main className="w-full flex justify-center px-4 md:px-0 pt-10">
|
||||||
<div className="w-full max-w-2xl flex flex-col items-center p-8 bg-[var(--muted)] rounded-2xl shadow space-y-6">
|
<div className="w-full max-w-2xl flex flex-col items-center p-8 bg-[var(--muted)] rounded-2xl shadow space-y-6">
|
||||||
<h1 className="text-3xl font-bold text-[var(--primary)]">Login</h1>
|
<h1 className="text-3xl font-bold text-[var(--primary)]">Login</h1>
|
||||||
<form onSubmit={handleSubmit} className="w-full flex flex-col gap-6 items-center">
|
<form onSubmit={handleSubmit} className="w-full flex flex-col gap-6 items-center">
|
||||||
@@ -38,7 +38,7 @@ export default function LoginPage() {
|
|||||||
value={email}
|
value={email}
|
||||||
required
|
required
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
className="w-full max-w-2xl text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-[var(--background)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50"
|
className="w-full max-w-2xl text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-transparent focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50"
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full max-w-2xl">
|
<div className="relative w-full max-w-2xl">
|
||||||
<input
|
<input
|
||||||
@@ -47,7 +47,7 @@ export default function LoginPage() {
|
|||||||
value={password}
|
value={password}
|
||||||
required
|
required
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
className="w-full text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-[var(--background)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50 pr-16"
|
className="w-full text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-transparent focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50 pr-16"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export default function RegisterPage() {
|
|||||||
|
|
||||||
const handleRegister = async (e: React.FormEvent) => {
|
const handleRegister = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
// Email validation
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(email)) {
|
||||||
|
setError("Please include a valid email.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const res = await fetch('/api/auth/register', {
|
const res = await fetch('/api/auth/register', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ email, password }),
|
body: JSON.stringify({ email, password }),
|
||||||
@@ -26,7 +32,7 @@ export default function RegisterPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="w-full flex justify-center bg-[var(--background)] px-4 md:px-0 pt-10">
|
<main className="w-full flex justify-center px-4 md:px-0 pt-10">
|
||||||
<div className="w-full max-w-2xl flex flex-col items-center p-8 bg-[var(--muted)] rounded-2xl shadow space-y-6">
|
<div className="w-full max-w-2xl flex flex-col items-center p-8 bg-[var(--muted)] rounded-2xl shadow space-y-6">
|
||||||
<h1 className="text-3xl font-bold text-[var(--secondary)]">Register</h1>
|
<h1 className="text-3xl font-bold text-[var(--secondary)]">Register</h1>
|
||||||
<form onSubmit={handleRegister} className="w-full flex flex-col gap-6 items-center">
|
<form onSubmit={handleRegister} className="w-full flex flex-col gap-6 items-center">
|
||||||
@@ -35,9 +41,17 @@ export default function RegisterPage() {
|
|||||||
placeholder="Email"
|
placeholder="Email"
|
||||||
value={email}
|
value={email}
|
||||||
required
|
required
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => {
|
||||||
className="w-full max-w-2xl text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-[var(--background)] focus:outline-none focus:ring-2 focus:ring-[var(--secondary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50"
|
setEmail(e.target.value);
|
||||||
|
if (error) setError("");
|
||||||
|
}}
|
||||||
|
className="w-full max-w-2xl text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-transparent focus:outline-none focus:ring-2 focus:ring-[var(--secondary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50"
|
||||||
|
autoComplete="off"
|
||||||
|
pattern="[^\s@]+@[^\s@]+\.[^\s@]+"
|
||||||
/>
|
/>
|
||||||
|
{error && (
|
||||||
|
<p className="text-red-500 text-center w-full -mt-4 mb-2">{error}</p>
|
||||||
|
)}
|
||||||
<div className="relative w-full max-w-2xl">
|
<div className="relative w-full max-w-2xl">
|
||||||
<input
|
<input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
@@ -45,7 +59,7 @@ export default function RegisterPage() {
|
|||||||
value={password}
|
value={password}
|
||||||
required
|
required
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
className="w-full text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-[var(--background)] focus:outline-none focus:ring-2 focus:ring-[var(--secondary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50 pr-16"
|
className="w-full text-lg border border-[var(--border)] px-6 py-4 rounded-lg bg-transparent focus:outline-none focus:ring-2 focus:ring-[var(--secondary)] text-[var(--foreground)] placeholder:text-[var(--foreground)]/50 pr-16"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -60,7 +74,6 @@ export default function RegisterPage() {
|
|||||||
<button type="submit" className="w-full max-w-2xl bg-[var(--secondary)] text-white py-4 text-lg rounded-lg font-semibold shadow hover:bg-[var(--secondary)]/80 hover:scale-105 hover:shadow-lg transition-all duration-200">
|
<button type="submit" className="w-full max-w-2xl bg-[var(--secondary)] text-white py-4 text-lg rounded-lg font-semibold shadow hover:bg-[var(--secondary)]/80 hover:scale-105 hover:shadow-lg transition-all duration-200">
|
||||||
Register
|
Register
|
||||||
</button>
|
</button>
|
||||||
{error && <p className="text-red-500 text-center w-full">{error}</p>}
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
|
--background-image: url('/Light_Car_Interior.png');
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
--primary: #2563eb; /* blue-600 */
|
--primary: #2563eb; /* blue-600 */
|
||||||
--secondary: #22c55e; /* green-500 */
|
--secondary: #22c55e; /* green-500 */
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: #0a0a0a;
|
--background: #0a0a0a;
|
||||||
|
--background-image: url('/Dark_Car_Interior.png');
|
||||||
--foreground: #ededed;
|
--foreground: #ededed;
|
||||||
--muted: #171717;
|
--muted: #171717;
|
||||||
--border: #232323;
|
--border: #232323;
|
||||||
@@ -35,6 +37,10 @@
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
|
background-image: var(--background-image);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
transition: background 0.2s, color 0.2s;
|
transition: background 0.2s, color 0.2s;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default function ImprovmentIdeasPage() {
|
|||||||
return (
|
return (
|
||||||
<main className="max-w-xl mx-auto p-8 space-y-8 bg-[var(--muted)] rounded-xl shadow">
|
<main className="max-w-xl mx-auto p-8 space-y-8 bg-[var(--muted)] rounded-xl shadow">
|
||||||
<h1 className="text-3xl font-bold text-[var(--primary)] mb-4">Improvement Ideas</h1>
|
<h1 className="text-3xl font-bold text-[var(--primary)] mb-4">Improvement Ideas</h1>
|
||||||
<p className="text-lg text-[var(--foreground)]/80 mb-4">Have a suggestion or idea to make Car Fuel Tracker better? Share it with us!</p>
|
<p className="text-lg text-[var(--foreground)]/80 mb-4">Have a suggestion or idea to make FuelTrack better? Share it with us!</p>
|
||||||
{submitted ? (
|
{submitted ? (
|
||||||
<div className="bg-green-100 text-green-800 p-4 rounded-lg font-semibold text-center">Thank you for your suggestion! We value your feedback and ideas.</div>
|
<div className="bg-green-100 text-green-800 p-4 rounded-lg font-semibold text-center">Thank you for your suggestion! We value your feedback and ideas.</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
+2
-2
@@ -15,7 +15,7 @@ const geistMono = Geist_Mono({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Car Fuel Tracker",
|
title: "FuelTrack",
|
||||||
description: "Track your car's fuel and mileage easily.",
|
description: "Track your car's fuel and mileage easily.",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ export default async function RootLayout({
|
|||||||
<script dangerouslySetInnerHTML={{ __html: setThemeScript }} />
|
<script dangerouslySetInnerHTML={{ __html: setThemeScript }} />
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-[var(--background)] text-[var(--foreground)]`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
>
|
>
|
||||||
<Providers>
|
<Providers>
|
||||||
<header className="w-full px-6 py-4 border-b border-[var(--border)] bg-[var(--muted)]/80 backdrop-blur-md shadow-lg rounded-b-xl z-30 sticky top-0">
|
<header className="w-full px-6 py-4 border-b border-[var(--border)] bg-[var(--muted)]/80 backdrop-blur-md shadow-lg rounded-b-xl z-30 sticky top-0">
|
||||||
|
|||||||
+143
-118
@@ -1,13 +1,117 @@
|
|||||||
|
"use client";
|
||||||
|
import React, { Suspense, useState, useEffect } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { getSession } from "@/lib/auth";
|
import LiveStatsPreview from "@/components/LiveStatsPreview";
|
||||||
|
import { useSession } from "next-auth/react";
|
||||||
|
|
||||||
export default async function Home() {
|
function UseCaseBlocks() {
|
||||||
const session = await getSession();
|
const blocks = [
|
||||||
|
{
|
||||||
|
icon: "🚗",
|
||||||
|
title: "For Commuters",
|
||||||
|
desc: "Track daily costs and distance. See your monthly spend and optimize your route.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "👨👩👧👦",
|
||||||
|
title: "For Families",
|
||||||
|
desc: "Manage multiple cars in one account. Switch between vehicles with ease.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "♻️",
|
||||||
|
title: "For Eco-conscious Drivers",
|
||||||
|
desc: "Get hybrid/EV performance and sustainability insights. See your CO₂ savings.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<section className="w-full max-w-4xl mx-auto py-12 px-4">
|
||||||
|
<h2 className="text-3xl font-bold text-center text-[var(--primary)] mb-8">Who Is This For?</h2>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-8">
|
||||||
|
{blocks.map((b) => (
|
||||||
|
<div key={b.title} className="group bg-white/40 dark:bg-[var(--muted)]/60 rounded-xl shadow p-6 flex flex-col items-center gap-3 border border-[var(--border)] transition-all hover:scale-105 hover:bg-white/60 dark:hover:bg-[var(--muted)]/80 animate-fade-in backdrop-blur">
|
||||||
|
<span className="text-4xl group-hover:scale-125 transition-transform">{b.icon}</span>
|
||||||
|
<h3 className="font-semibold text-lg text-[var(--primary)] group-hover:text-[var(--secondary)]">{b.title}</h3>
|
||||||
|
<p className="text-center text-[var(--foreground)]/80 group-hover:text-[var(--foreground)]">{b.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FeatureGrid() {
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
icon: "⛽",
|
||||||
|
title: "Log Fill-Ups",
|
||||||
|
desc: "Quickly add fuel entries and keep your records organized.",
|
||||||
|
more: "Supports gasoline, diesel, LPG, and electric charging. Hybrid-aware UI.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "📊",
|
||||||
|
title: "Analyze Stats",
|
||||||
|
desc: "Visualize your fuel consumption, costs, and mileage trends.",
|
||||||
|
more: "Export your data, filter by car or fuel type, and see your efficiency improve.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: "🚗",
|
||||||
|
title: "Manage Cars",
|
||||||
|
desc: "Track multiple vehicles and switch between them easily.",
|
||||||
|
more: "Perfect for families, fleets, or anyone with more than one car.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const [open, setOpen] = useState<number | null>(null);
|
||||||
|
return (
|
||||||
|
<section className="w-full max-w-4xl mx-auto grid grid-cols-1 sm:grid-cols-3 gap-8 py-12 px-4">
|
||||||
|
{features.map((f, i) => (
|
||||||
|
<div
|
||||||
|
key={f.title}
|
||||||
|
className={`flex flex-col items-center text-center gap-2 bg-white/40 dark:bg-[var(--muted)]/60 rounded-xl shadow p-6 border border-[var(--border)] transition-all duration-300 cursor-pointer ${open === i ? "scale-105 bg-white/60 dark:bg-[var(--muted)]/80" : ""} backdrop-blur`}
|
||||||
|
onClick={() => setOpen(open === i ? null : i)}
|
||||||
|
onMouseEnter={() => setOpen(i)}
|
||||||
|
onMouseLeave={() => setOpen(null)}
|
||||||
|
>
|
||||||
|
<span className="text-4xl text-[var(--primary)]">{f.icon}</span>
|
||||||
|
<h2 className="font-semibold text-lg text-[var(--foreground)]">{f.title}</h2>
|
||||||
|
<p className="text-[var(--foreground)] opacity-80">{f.desc}</p>
|
||||||
|
<div
|
||||||
|
className={`overflow-hidden transition-all duration-300 ${open === i ? "max-h-40 opacity-100 mt-2" : "max-h-0 opacity-0"}`}
|
||||||
|
>
|
||||||
|
<p className="text-[var(--foreground)]/90 text-sm bg-[var(--background)] rounded p-2 border mt-2 border-[var(--border)] shadow-inner animate-fade-in">
|
||||||
|
{f.more}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CTAVariant() {
|
||||||
|
// Randomly show one of two CTAs per session
|
||||||
|
const [variant] = useState(() => Math.random() < 0.5 ? 0 : 1);
|
||||||
|
const ctas = [
|
||||||
|
{ label: "Start Tracking for Free", href: "/auth/register" },
|
||||||
|
{ label: "Log Your First Fill-Up Now", href: "/auth/register" },
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center my-8">
|
||||||
|
<Link
|
||||||
|
href={ctas[variant].href}
|
||||||
|
className="rounded-lg bg-[var(--primary)] text-white px-8 py-4 text-lg font-bold shadow hover:bg-blue-700 transition animate-bounce-in"
|
||||||
|
>
|
||||||
|
{ctas[variant].label}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const { data: session } = useSession();
|
||||||
const isLoggedIn = !!session;
|
const isLoggedIn = !!session;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-screen bg-[var(--background)] text-[var(--foreground)]">
|
<div className="flex flex-col min-h-screen">
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<section className="flex-1 flex flex-col items-center justify-center gap-10 p-8 sm:p-20">
|
<section className="flex-1 flex flex-col items-center justify-center gap-10 p-8 sm:p-20">
|
||||||
<div className="flex flex-col items-center gap-4">
|
<div className="flex flex-col items-center gap-4">
|
||||||
@@ -27,132 +131,53 @@ export default async function Home() {
|
|||||||
? "Jump right into your dashboard to log fill-ups, view stats, and manage your cars."
|
? "Jump right into your dashboard to log fill-ups, view stats, and manage your cars."
|
||||||
: "Effortlessly log fill-ups, monitor fuel costs, and analyze your driving habits—all in one beautiful dashboard. Save money, track efficiency, and manage all your vehicles including hybrids and EVs."}
|
: "Effortlessly log fill-ups, monitor fuel costs, and analyze your driving habits—all in one beautiful dashboard. Save money, track efficiency, and manage all your vehicles including hybrids and EVs."}
|
||||||
</p>
|
</p>
|
||||||
{!isLoggedIn && (
|
{/* CTA or Dashboard Button */}
|
||||||
<div className="flex gap-4 mt-4">
|
{isLoggedIn ? (
|
||||||
<Link href="/auth/register" className="rounded-lg bg-[var(--primary)] text-white px-8 py-4 text-lg font-bold shadow hover:bg-blue-700 transition">Get Started</Link>
|
<div className="flex justify-center my-8">
|
||||||
<Link href="/auth/login" className="rounded-lg border border-[var(--primary)] bg-[var(--muted)] text-[var(--primary)] px-8 py-4 text-lg font-bold hover:bg-[var(--primary)] hover:text-white transition">Login</Link>
|
<Link href="/dashboard" className="rounded-lg bg-[var(--primary)] text-white px-8 py-4 text-lg font-bold shadow hover:bg-blue-700 transition animate-bounce-in">
|
||||||
|
Go to Dashboard
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col sm:flex-row justify-center gap-4 my-8 w-full">
|
||||||
|
<Link
|
||||||
|
href="/auth/login"
|
||||||
|
className="w-full sm:w-auto rounded-lg bg-white border border-[var(--primary)] text-[var(--primary)] px-8 py-4 text-lg font-bold shadow hover:bg-[var(--primary)] hover:text-white transition text-center"
|
||||||
|
>
|
||||||
|
Log In
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/auth/register"
|
||||||
|
className="w-full sm:w-auto rounded-lg bg-[var(--primary)] text-white px-8 py-4 text-lg font-bold shadow hover:bg-blue-700 transition animate-bounce-in text-center"
|
||||||
|
>
|
||||||
|
Sign Up And Log Your First Fill-Up Now
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isLoggedIn && (
|
|
||||||
<Link href="/dashboard" className="rounded-lg bg-[var(--primary)] text-white px-8 py-4 text-lg font-bold shadow hover:bg-blue-700 transition">Go to Dashboard</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* How It Works Section */}
|
{/* Live Stats Preview */}
|
||||||
<section className="w-full max-w-4xl mx-auto py-12 px-4">
|
<Suspense fallback={<div className="text-center py-8">Loading live stats...</div>}>
|
||||||
<h2 className="text-3xl font-bold text-center text-[var(--primary)] mb-8">How It Works</h2>
|
<LiveStatsPreview />
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-8">
|
</Suspense>
|
||||||
<div className="flex flex-col items-center gap-2">
|
|
||||||
<span className="text-4xl">➕</span>
|
|
||||||
<h3 className="font-semibold text-lg">Add Your Car</h3>
|
|
||||||
<p className="text-center text-[var(--foreground)]/80">Register your vehicle, including hybrids and EVs.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center gap-2">
|
|
||||||
<span className="text-4xl">⛽</span>
|
|
||||||
<h3 className="font-semibold text-lg">Log Fill-Ups & Mileage</h3>
|
|
||||||
<p className="text-center text-[var(--foreground)]/80">Record fuel, charging, and odometer readings in seconds.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center gap-2">
|
|
||||||
<span className="text-4xl">📊</span>
|
|
||||||
<h3 className="font-semibold text-lg">View Stats & Insights</h3>
|
|
||||||
<p className="text-center text-[var(--foreground)]/80">Analyze consumption, costs, and trends to save money.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Benefits Section */}
|
{/* Use Case Blocks */}
|
||||||
<section className="w-full max-w-4xl mx-auto py-12 px-4">
|
<UseCaseBlocks />
|
||||||
<h2 className="text-3xl font-bold text-center text-[var(--primary)] mb-8">Why Use Car Fuel Tracker?</h2>
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-8">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<span className="font-bold text-[var(--primary)]">💸 Save Money</span>
|
|
||||||
<p className="text-[var(--foreground)]/80">Spot trends, optimize driving habits, and reduce fuel costs.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<span className="font-bold text-[var(--primary)]">🚗 Multi-Car & Hybrid Support</span>
|
|
||||||
<p className="text-[var(--foreground)]/80">Track any number of vehicles, including hybrids and EVs.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<span className="font-bold text-[var(--primary)]">📈 Export & Analyze</span>
|
|
||||||
<p className="text-[var(--foreground)]/80">Download your data for deeper analysis or tax purposes.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<span className="font-bold text-[var(--primary)]">🔒 Private & Secure</span>
|
|
||||||
<p className="text-[var(--foreground)]/80">Your data is protected and only accessible to you.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Reviews Section */}
|
{/* Dynamic Feature Grid */}
|
||||||
<section className="w-full max-w-3xl mx-auto py-12 px-4">
|
<FeatureGrid />
|
||||||
<h2 className="text-3xl font-bold text-center text-[var(--primary)] mb-8">What Users Say</h2>
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-8">
|
|
||||||
<div className="bg-[var(--muted)] rounded-xl shadow p-6 flex flex-col items-center gap-3 border border-[var(--border)]">
|
|
||||||
<span className="text-3xl text-[var(--secondary)]">“</span>
|
|
||||||
<p className="text-center text-lg text-[var(--foreground)] font-medium">This app made it so easy to track my car’s fuel expenses and spot trends. Highly recommended for anyone who wants to save money and understand their driving habits!</p>
|
|
||||||
<span className="text-sm text-gray-500">— Happy Driver</span>
|
|
||||||
</div>
|
|
||||||
<div className="bg-[var(--muted)] rounded-xl shadow p-6 flex flex-col items-center gap-3 border border-[var(--border)]">
|
|
||||||
<span className="text-3xl text-[var(--secondary)]">“</span>
|
|
||||||
<p className="text-center text-lg text-[var(--foreground)] font-medium">I love the hybrid support! Now I can track both my EV charging and gasoline fill-ups in one place.</p>
|
|
||||||
<span className="text-sm text-gray-500">— Hybrid Owner</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Features Section (condensed) */}
|
{/* ...existing reviews, etc... */}
|
||||||
<section className="w-full max-w-4xl mx-auto grid grid-cols-1 sm:grid-cols-3 gap-8 py-12 px-4">
|
|
||||||
<div className="flex flex-col items-center text-center gap-2 bg-[var(--muted)] rounded-xl shadow p-6 border border-[var(--border)]">
|
|
||||||
<span className="text-4xl text-[var(--primary)]">⛽</span>
|
|
||||||
<h2 className="font-semibold text-lg text-[var(--foreground)]">Log Fill-Ups</h2>
|
|
||||||
<p className="text-[var(--foreground)] opacity-80">Quickly add fuel entries and keep your records organized.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center text-center gap-2 bg-[var(--muted)] rounded-xl shadow p-6 border border-[var(--border)]">
|
|
||||||
<span className="text-4xl text-[var(--secondary)]">📊</span>
|
|
||||||
<h2 className="font-semibold text-lg text-[var(--foreground)]">Analyze Stats</h2>
|
|
||||||
<p className="text-[var(--foreground)] opacity-80">Visualize your fuel consumption, costs, and mileage trends.</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col items-center text-center gap-2 bg-[var(--muted)] rounded-xl shadow p-6 border border-[var(--border)]">
|
|
||||||
<span className="text-4xl text-[var(--accent)]">🚗</span>
|
|
||||||
<h2 className="font-semibold text-lg text-[var(--foreground)]">Manage Cars</h2>
|
|
||||||
<p className="text-[var(--foreground)] opacity-80">Track multiple vehicles and switch between them easily.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="w-full py-6 flex flex-col items-center justify-center border-t border-[var(--border)] bg-[var(--muted)] text-sm text-gray-500 gap-2 mt-auto">
|
<footer className="w-full py-6 flex flex-col items-center justify-center border-t border-[var(--border)] bg-[var(--muted)] text-sm text-gray-500 gap-2 mt-auto">
|
||||||
<nav className="flex gap-6 mb-1">
|
<nav className="flex gap-6 mb-1">
|
||||||
<Link
|
<Link href="/about" className="hover:text-[var(--primary)] transition">About</Link>
|
||||||
href="/about"
|
<Link href="/contact" className="hover:text-[var(--primary)] transition">Contact</Link>
|
||||||
className="hover:text-[var(--primary)] transition"
|
<Link href="/bugs" className="hover:text-[var(--primary)] transition">Bug Report</Link>
|
||||||
>
|
<Link href="/improvment_ideas" className="hover:text-[var(--primary)] transition">Improvment Ideas</Link>
|
||||||
About
|
<Link href="/privacy" className="hover:text-[var(--primary)] transition">Privacy Policy</Link>
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/contact"
|
|
||||||
className="hover:text-[var(--primary)] transition"
|
|
||||||
>
|
|
||||||
Contact
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/bugs"
|
|
||||||
className="hover:text-[var(--primary)] transition"
|
|
||||||
>
|
|
||||||
Bug Report
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/improvment_ideas"
|
|
||||||
className="hover:text-[var(--primary)] transition"
|
|
||||||
>
|
|
||||||
Improvment Ideas
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
href="/privacy"
|
|
||||||
className="hover:text-[var(--primary)] transition"
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</Link>
|
|
||||||
</nav>
|
</nav>
|
||||||
<span>© {new Date().getFullYear()} Car Fuel Tracker.</span>
|
<span>© {new Date().getFullYear()} Car Fuel Tracker.</span>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import ThemeToggle from "./ThemeToggle";
|
import ThemeToggle from "./ThemeToggle";
|
||||||
|
import Image from "./Image";
|
||||||
|
|
||||||
export default function ClientNavbar() {
|
export default function ClientNavbar() {
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
@@ -11,6 +12,19 @@ export default function ClientNavbar() {
|
|||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const userInitial = session?.user?.name?.[0]?.toUpperCase() || session?.user?.email?.[0]?.toUpperCase() || "U";
|
const userInitial = session?.user?.name?.[0]?.toUpperCase() || session?.user?.email?.[0]?.toUpperCase() || "U";
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const [theme, setTheme] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Listen for theme changes
|
||||||
|
const updateTheme = () => {
|
||||||
|
if (document.documentElement.classList.contains("dark")) setTheme("dark");
|
||||||
|
else setTheme("light");
|
||||||
|
};
|
||||||
|
updateTheme();
|
||||||
|
const observer = new MutationObserver(updateTheme);
|
||||||
|
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
|
function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
|
||||||
const isActive = pathname === href;
|
const isActive = pathname === href;
|
||||||
@@ -29,8 +43,28 @@ export default function ClientNavbar() {
|
|||||||
return (
|
return (
|
||||||
<nav className="flex w-full items-center justify-between">
|
<nav className="flex w-full items-center justify-between">
|
||||||
<Link href="/" className="flex items-center gap-2 text-2xl font-bold tracking-tight text-[var(--primary)] hover:scale-105 transition">
|
<Link href="/" className="flex items-center gap-2 text-2xl font-bold tracking-tight text-[var(--primary)] hover:scale-105 transition">
|
||||||
<span className="text-3xl">🚗</span>
|
<span className="relative w-10 h-10 block">
|
||||||
<span>Car Fuel Tracker</span>
|
{theme === "dark" ? (
|
||||||
|
<Image
|
||||||
|
src="/Light_Logo.png"
|
||||||
|
alt="FuelTrack logo light"
|
||||||
|
className="w-10 h-10 object-contain transition-colors"
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Image
|
||||||
|
src="/Dark_Logo.png"
|
||||||
|
alt="FuelTrack logo dark"
|
||||||
|
className="w-10 h-10 object-contain transition-colors"
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span>FuelTrack</span>
|
||||||
</Link>
|
</Link>
|
||||||
{/* Desktop nav */}
|
{/* Desktop nav */}
|
||||||
<div className="hidden md:flex gap-4 items-center">
|
<div className="hidden md:flex gap-4 items-center">
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
export default Image;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"use client";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface LiveStats {
|
||||||
|
avgFuelPrice: number;
|
||||||
|
avgEfficiency: number;
|
||||||
|
totalCO2Saved: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LiveStatsPreview() {
|
||||||
|
const [stats, setStats] = useState<LiveStats | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch("/api/live-stats")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
setStats(data);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="w-full max-w-4xl mx-auto py-8 px-4">
|
||||||
|
<h2 className="text-2xl font-bold text-center text-[var(--primary)] mb-6">Live Community Stats</h2>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6">
|
||||||
|
<div className="bg-white/40 dark:bg-[var(--muted)]/60 rounded-xl shadow p-6 flex flex-col items-center gap-2 border border-[var(--border)] animate-fade-in backdrop-blur">
|
||||||
|
<span className="text-3xl">⛽</span>
|
||||||
|
<span className="font-semibold text-lg">Avg. Fuel Price</span>
|
||||||
|
<span className="text-2xl font-bold text-[var(--primary)]">{loading ? "..." : `${stats?.avgFuelPrice.toFixed(2)} RON/L`}</span>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/40 dark:bg-[var(--muted)]/60 rounded-xl shadow p-6 flex flex-col items-center gap-2 border border-[var(--border)] animate-fade-in backdrop-blur">
|
||||||
|
<span className="text-3xl">📉</span>
|
||||||
|
<span className="font-semibold text-lg">Avg. Efficiency</span>
|
||||||
|
<span className="text-2xl font-bold text-[var(--primary)]">{loading ? "..." : `${stats?.avgEfficiency.toFixed(1)} L/100km`}</span>
|
||||||
|
</div>
|
||||||
|
<div className="bg-white/40 dark:bg-[var(--muted)]/60 rounded-xl shadow p-6 flex flex-col items-center gap-2 border border-[var(--border)] animate-fade-in backdrop-blur">
|
||||||
|
<span className="text-3xl">🌱</span>
|
||||||
|
<span className="font-semibold text-lg text-center w-full">CO₂ Saved by Hybrids/EVs</span>
|
||||||
|
<span className="text-2xl font-bold text-green-700">{loading ? "..." : `${stats?.totalCO2Saved.toLocaleString()} kg`}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user