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
Binary file modified public/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/app/(dashboard)/archive/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
import ArchiveView from "@/components/archive/ArchiveView";

export const metadata = {
title: "Archive | SubVantage",
title: "Archive",
};

export default async function ArchivePage() {
Expand Down
7 changes: 2 additions & 5 deletions src/app/(dashboard)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import { auth } from "@/auth";
import { prisma } from "@/lib/prisma";
import { DashboardHeader } from "@/components/dashboard/DashboardHeader";
import { StatsGrid } from "@/components/dashboard/DashboardWidgets";
import { SpendingChart } from "@/components/dashboard/SpendingChart";
import { UpcomingBills } from "@/components/dashboard/UpcomingBills";
import { SubscriptionCarousel } from "@/components/dashboard/SubscriptionCarousel";
import { InsightsCard } from "@/components/dashboard/Insights";
// 👇 FIX: Use the same helper as Subscriptions Page
import { getExchangeRates } from "@/lib/currency-helper";
import { processSubscriptionData } from "@/lib/calculations";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { SpendingChart } from "@/components/dashboard/SpendingChart"; // 👈 FIX: Restored normal static import
import {
Tooltip,
TooltipContent,
Expand All @@ -20,15 +19,14 @@ import { Info } from "lucide-react";
import { OnboardingFlow } from "@/components/dashboard/OnboardingFlow";

export const metadata = {
title: "Dashboard | SubVantage",
title: "Dashboard",
description: "Your financial pulse at a glance.",
};

export default async function DashboardPage() {
const session = await auth();
if (!session?.user?.id) return null;

// ✅ PARALLEL FETCH: Fetch User and Subscriptions at the same time
const [user, rawSubs] = await Promise.all([
prisma.user.findUnique({
where: { id: session.user.id },
Expand All @@ -42,7 +40,6 @@ export default async function DashboardPage() {

const baseCurrency = user?.preferredCurrency || "USD";

// 👇 FIX: Unified Rate Fetching
const rates = await getExchangeRates(baseCurrency);

const subs = rawSubs.map((sub) => ({
Expand Down
2 changes: 1 addition & 1 deletion src/app/(dashboard)/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
import SettingsView from "@/components/settings/SettingsView";

export const metadata = {
title: "Settings | SubVantage",
title: "Settings",
};

export default async function SettingsPage() {
Expand Down
7 changes: 3 additions & 4 deletions src/app/(dashboard)/subscriptions/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { id } = await params;

if (!session?.user) {
return { title: "Details | SubVantage" };
return { title: "Details" };
}

// Fetch just enough data to get the title
Expand All @@ -28,12 +28,11 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
});

if (!sub) {
return { title: "Subscription Not Found | SubVantage" };
return { title: "Subscription Not Found" };
}

// Example: "GitHub | SubVantage"
return {
title: `${sub.vendor.name} | SubVantage`,
title: `${sub.vendor.name}`,
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/(dashboard)/subscriptions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SubscriptionsView } from "@/components/subscriptions/SubscriptionsView"
import { getExchangeRates } from "@/lib/currency-helper";

export const metadata = {
title: "Subscriptions | SubVantage",
title: "Subscriptions",
};

export default async function SubscriptionsPage() {
Expand Down
15 changes: 6 additions & 9 deletions src/app/api/cron/check-trials/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,16 @@ export async function GET(req: Request) {
const now = dayjs();
const twoDaysFromNow = now.add(2, "day").toDate();

// 👇 UPDATE 1: Check BOTH startDate and nextRenewalDate
const expiringTrials = await prisma.subscription.findMany({
where: {
isTrial: true,
OR: [
// Case A: Standard Trial (Renewal is approaching)
{
nextRenewalDate: {
gte: now.toDate(),
lte: twoDaysFromNow
}
},
// Case B: Future Start Trial (Start Date IS the trial end)
{
startDate: {
gte: now.toDate(),
Expand All @@ -47,13 +44,14 @@ export async function GET(req: Request) {
const emailPromises = expiringTrials.map(async (sub) => {
if (!sub.user.email) return null;

// 👇 UPDATE 2: Calculate correct target date
// If the subscription hasn't technically started yet, the "End Date" is the Start Date.
const startDate = dayjs(sub.startDate);
const isFutureStart = startDate.isAfter(now);

const targetDate = isFutureStart ? startDate : dayjs(sub.nextRenewalDate);
const daysLeft = targetDate.diff(now, "day");

// Compare exact calendar days
const today = dayjs().startOf("day");
const target = targetDate.startOf("day");
const daysLeft = target.diff(today, "day");

const { data, error } = await resend.emails.send({
from: "SubVantage <subvantage@iansebastian.dev>",
Expand All @@ -63,7 +61,7 @@ export async function GET(req: Request) {
userName: sub.user.name || "User",
vendorName: sub.vendor.name,
daysLeft: Math.max(0, daysLeft),
renewalCost: formatCurrency(Number(sub.cost), sub.currency), // Helper for nice currency
renewalCost: formatCurrency(Number(sub.cost), sub.currency),
}),
});

Expand All @@ -83,7 +81,6 @@ export async function GET(req: Request) {
}
}

// Simple helper to format currency inside the cron job
function formatCurrency(amount: number, currency: string) {
return new Intl.NumberFormat("en-US", {
style: "currency",
Expand Down
9 changes: 5 additions & 4 deletions src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ export default function LoginPage() {
<div className="absolute bottom-0 right-0 w-full h-full bg-[radial-gradient(circle_at_100%_100%,_#6366f1_0%,_transparent_40%)] opacity-20" />

<div className="relative z-10 flex items-center gap-2">
{/* 👇 FIX: Replaced SVG with logo.png */}
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-gradient-to-tr from-primary to-blue-600 shadow-lg shadow-primary/20 overflow-hidden">
{/* Transparent and larger logo container */}
<div className="flex h-12 w-12 items-center justify-center bg-transparent overflow-hidden">
<Image
src="/logo.png"
alt="SubVantage"
width={40}
height={40}
width={48}
height={48}
className="object-cover"
priority
/>
</div>
<span className="text-xl font-bold tracking-tight">SubVantage</span>
Expand Down
Loading
Loading