Skip to content
Open
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
2 changes: 1 addition & 1 deletion frontend/src/Components/DashBoard/LeetCode.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import BackButton from "../ui/backbutton";

ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);

export default function LeetCode({ platforms = {} }) {
export default function LeetCode() {
const [stats, setStats] = useState(null);
const { leetUser } = useParams();

Expand Down
1 change: 0 additions & 1 deletion frontend/src/Components/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export default function Dashboard() {
const {
socialLinks = [],
streak = 0,
githubUsername = null,
timeSpent = "0 minutes",
activity = [],
notes = []
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Components/Hero.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from "react";
import React, { useState, useRef } from "react";
import { motion } from "framer-motion";

import { ArrowRightIcon } from "lucide-react";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Components/auth/ForgotPassword.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const ForgotPassword = () => {
} else {
setStatus({ type: "error", message: data.message || "Something went wrong" });
}
} catch (error) {
} catch {
setStatus({ type: "error", message: "Server error. Try again later." });
} finally {
setLoading(false);
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/Components/auth/Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const Login = () => {
let data;
try {
data = await response.json();
} catch (jsonError) {
} catch {
throw new Error("Invalid server response");
}

Expand All @@ -69,7 +69,7 @@ const Login = () => {
}
};

const handleVerificationSuccess = (user) => {
const handleVerificationSuccess = () => {
navigate("/dashboard");
};

Expand Down
7 changes: 1 addition & 6 deletions frontend/src/Components/auth/Register.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,11 @@ const Register = () => {
}
};

const handleVerificationSuccess = (user) => {
const handleVerificationSuccess = () => {
// Redirect to dashboard
window.location.href = "/dashboard";
};

const handleGoogleRegister = () => {
// Use backend OAuth route at /auth (not protected /api)
window.location.href = `${import.meta.env.VITE_API_URL}/auth/google`;
};

if (showVerification) {
return (
<EmailVerification
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Components/contact.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const Contact = () => {
message: "Something went wrong. Please try again.",
});
}
} catch (error) {
} catch {
setStatus("error");
setError("root", { message: "Something went wrong. Please try again." });
}
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/Components/ui/Card.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// src/Components/ui/Card.jsx
import React from "react";

export function Card({ children, className = "" }) {
return (
<div className={`rounded-xl border bg-white shadow p-4 ${className}`}>
Expand All @@ -9,12 +11,16 @@ export function Card({ children, className = "" }) {

export function CardHeader({ children, className = "" }) {
return (
<div className={`font-bold text-lg mb-2 ${className}`}>{children}</div>
<div className={`font-bold text-lg mb-2 ${className}`}>
{children}
</div>
);
}

export function CardTitle({ children, className = "" }) {
return <h3 className={`text-xl font-semibold ${className}`}>{children}</h3>;
return (
<h3 className={`text-xl font-semibold ${className}`}>{children}</h3>
);
}

export function CardContent({ children, className = "" }) {
Expand All @@ -24,3 +30,4 @@ export function CardContent({ children, className = "" }) {
export function CardFooter({ children, className = "" }) {
return <div className={`mt-2 border-t pt-2 ${className}`}>{children}</div>;
}

2 changes: 1 addition & 1 deletion frontend/src/Components/ui/DarkModeToggle.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function DarkModeToggle() {
aria-label="Toggle dark mode"
className={`flex items-center justify-center w-12 h-12 rounded-full transition-all duration-300 cursor-pointer hover:scale-110 ${
isDark
? "bg-[ --background]"
? "bg-[--background]"
: "bg-[--foreground]"
}`}
>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/Components/ui/Loader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react";
import { cn } from "@/lib/utils"; // if you have shadcn's `cn`, else remove
// import { cn } from "@/lib/utils"; // if you have shadcn's `cn`, else remove

interface LoaderProps {
className?: string;
Expand Down
33 changes: 29 additions & 4 deletions frontend/src/Components/ui/ScrollRevealWrapper.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
// src/components/ScrollRevealWrapper.jsx
import React, { useRef } from "react";
import { motion } from "framer-motion";
import { useInView } from "react-intersection-observer";

export default function ScrollRevealWrapper({ children, delay = 0, className = "" }) {
const { ref, inView } = useInView({ triggerOnce: true, threshold: 0.1 });
interface ScrollRevealWrapperProps {
children: React.ReactNode;
delay?: number;
className?: string;
}

export default function ScrollRevealWrapper({
children,
delay = 0,
className = "",
}: ScrollRevealWrapperProps) {
const ref = useRef<HTMLDivElement>(null);

// Track if element is in view
const { inView } = useInView({
triggerOnce: true,
threshold: 0.1,
});

// Animation variants
const variants = {
hidden: { opacity: 0, y: 40 },
visible: { opacity: 1, y: 0 },
};

return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 40 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
variants={variants}
initial="hidden"
animate={inView ? "visible" : "hidden"}
transition={{ duration: 0.6, delay }}
className={className}
>
{children}
</motion.div>
);
}

33 changes: 1 addition & 32 deletions frontend/src/Components/ui/button.jsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority";

import { cn } from "@/lib/utils"

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
import { buttonVariants } from "@/lib/buttonVariants"

function Button({
className,
Expand Down
18 changes: 1 addition & 17 deletions frontend/src/Components/ui/input.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,6 @@
import * as React from "react"
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"

const inputVariants = cva(
"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-all",
{
variants: {
size: {
default: "h-10",
sm: "h-9",
lg: "h-11",
},
},
defaultVariants: {
size: "default",
},
}
)
import { inputVariants } from "@/lib/inputVariants"

const Input = React.forwardRef(({ className, type, size, ...props }, ref) => {
return (
Expand Down
49 changes: 24 additions & 25 deletions frontend/src/context/TimerContext.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { createContext, useContext, useState, useEffect } from "react";
import { createContext, useContext, useState, useEffect, useCallback } from "react";
import { SESSIONS_BEFORE_LONG_BREAK } from "../lib/timerConstants";

const TimerContext = createContext();

export function useTimer() {
return useContext(TimerContext);
}

const SESSIONS_BEFORE_LONG_BREAK = 4;

export function TimerProvider({ children }) {
const [workTime, setWorkTime] = useState(25 * 60);
const [shortBreak, setShortBreak] = useState(5 * 60);
Expand All @@ -27,6 +26,27 @@ export function TimerProvider({ children }) {
return saved ? Number(saved) : workTime;
});

const handleSessionEnd = useCallback(() => {
const nextSession = isWork ? sessions + 1 : sessions;
setSessions(nextSession);
const nextTime = isWork
? nextSession % SESSIONS_BEFORE_LONG_BREAK === 0
? longBreak
: shortBreak
: workTime;

setIsWork(prev => !prev);
setTimeLeft(nextTime);
setEndTimestamp(Date.now() + nextTime * 1000);
setIsRunning(true);

if ("Notification" in window && Notification.permission === "granted") {
new Notification(
isWork ? "Work session complete! Time for a break 🎉" : "Break over! Back to work 💻"
);
}
}, [sessions, isWork, longBreak, shortBreak, workTime]);

// Timer tick using requestAnimationFrame
useEffect(() => {
let raf;
Expand All @@ -50,7 +70,7 @@ export function TimerProvider({ children }) {
}

return () => cancelAnimationFrame(raf);
}, [isRunning, endTimestamp]);
}, [isRunning, endTimestamp, handleSessionEnd]);

// Persist timer info
useEffect(() => localStorage.setItem("pomodoroTimeLeft", timeLeft), [timeLeft]);
Expand All @@ -59,27 +79,6 @@ export function TimerProvider({ children }) {
if (endTimestamp) localStorage.setItem("pomodoroEndTimestamp", endTimestamp);
}, [endTimestamp]);

const handleSessionEnd = () => {
const nextSession = isWork ? sessions + 1 : sessions;
setSessions(nextSession);
const nextTime = isWork
? nextSession % SESSIONS_BEFORE_LONG_BREAK === 0
? longBreak
: shortBreak
: workTime;

setIsWork(prev => !prev);
setTimeLeft(nextTime);
setEndTimestamp(Date.now() + nextTime * 1000);
setIsRunning(true);

if ("Notification" in window && Notification.permission === "granted") {
new Notification(
isWork ? "Work session complete! Time for a break 🎉" : "Break over! Back to work 💻"
);
}
};

const startTimer = () => {
if (!endTimestamp) setEndTimestamp(Date.now() + timeLeft * 1000);
setIsRunning(true);
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/lib/buttonVariants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { cva } from "class-variance-authority";

export const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
17 changes: 17 additions & 0 deletions frontend/src/lib/inputVariants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { cva } from "class-variance-authority";

export const inputVariants = cva(
"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-all",
{
variants: {
size: {
default: "h-10",
sm: "h-9",
lg: "h-11",
},
},
defaultVariants: {
size: "default",
},
}
);
1 change: 1 addition & 0 deletions frontend/src/lib/timerConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SESSIONS_BEFORE_LONG_BREAK = 4;
3 changes: 3 additions & 0 deletions frontend/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import path from "path";
import { fileURLToPath } from "url";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
Expand Down