From 1a4d7e851c8cd2a81f9f23172dd0a6c25e50785f Mon Sep 17 00:00:00 2001 From: Swoyam Date: Sat, 11 Oct 2025 16:19:57 +0530 Subject: [PATCH 1/2] Fixing Links and Updated CSS --- src/client/src/App.css | 116 -------- src/client/src/App.js | 4 + src/client/src/components/layout/Header.js | 5 +- src/client/src/components/layout/Main.js | 4 - .../src/components/problems/ProblemCard.js | 5 +- .../src/components/problems/ProblemPage.js | 1 + .../src/styles/components/FilterOptions.css | 166 +++++++++-- .../src/styles/components/ProblemCard.css | 245 +++++++++++++--- .../src/styles/components/ProblemPage.css | 271 ++++++++++++++++++ src/client/src/styles/layout/Footer.css | 55 +++- src/client/src/styles/layout/Header.css | 217 +++++++++++++- 11 files changed, 890 insertions(+), 199 deletions(-) create mode 100644 src/client/src/styles/components/ProblemPage.css diff --git a/src/client/src/App.css b/src/client/src/App.css index 064292a..74b5e05 100644 --- a/src/client/src/App.css +++ b/src/client/src/App.css @@ -36,119 +36,3 @@ transform: rotate(360deg); } } - -/* LeetCode-like styling for ProblemPage */ -.lcw-problem-page { - max-width: 800px; - margin: 32px auto; - background: #fff; - border-radius: 12px; - box-shadow: 0 2px 16px rgba(0,0,0,0.08); - padding: 32px; - font-family: 'Segoe UI', 'Roboto', 'Arial', sans-serif; -} - -.lcw-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 18px; -} - -.lcw-link { - color: #ffa116; - text-decoration: none; - font-weight: 500; - border-bottom: 1px dashed #ffa116; - transition: border-bottom 0.2s; -} -.lcw-link:hover { - border-bottom: 2px solid #ffa116; -} - -.lcw-meta { - display: flex; - gap: 18px; - margin-bottom: 24px; - font-size: 16px; -} - -.lcw-meta-item { - color: #555; -} - -.lcw-difficulty { - padding: 4px 12px; - border-radius: 8px; - font-weight: bold; - color: #fff; -} -.lcw-difficulty-easy { - background: #43a047; -} -.lcw-difficulty-medium { - background: #ffa116; -} -.lcw-difficulty-hard { - background: #d32f2f; -} -.lcw-difficulty-unknown { - background: #888; -} - -.lcw-section-title { - margin-top: 32px; - margin-bottom: 12px; - font-size: 20px; - color: #222; - font-weight: 600; -} - -.lcw-table { - width: 100%; - border-collapse: collapse; - margin-bottom: 24px; - background: #fafbfc; - border-radius: 8px; - overflow: hidden; -} - -.lcw-table th, .lcw-table td { - padding: 12px 10px; - border-bottom: 1px solid #eee; - text-align: left; -} - -.lcw-table th { - background: #f6f8fa; - font-weight: 600; - color: #333; -} - -.lcw-tag { - display: inline-block; - background: #e3e6ea; - color: #444; - padding: 4px 10px; - border-radius: 6px; - font-size: 14px; - font-weight: 500; -} - -.lcw-company { - display: inline-block; - background: #f5f5f5; - color: #222; - margin: 2px 6px 2px 0; - padding: 4px 10px; - border-radius: 6px; - font-size: 14px; - border: 1px solid #eee; -} - -.lcw-loading, .lcw-error { - text-align: center; - margin-top: 40px; - font-size: 18px; - color: #d32f2f; -} \ No newline at end of file diff --git a/src/client/src/App.js b/src/client/src/App.js index 4e81d85..10f3213 100644 --- a/src/client/src/App.js +++ b/src/client/src/App.js @@ -2,15 +2,19 @@ import "./App.css"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import Main from "./components/layout/Main"; import ProblemPage from "./components/problems/ProblemPage"; +import Header from "./components/layout/Header"; +import Footer from "./components/layout/Footer"; function App() { return (
+
} /> } /> +
); diff --git a/src/client/src/components/layout/Header.js b/src/client/src/components/layout/Header.js index fcdfe21..7fc2f69 100644 --- a/src/client/src/components/layout/Header.js +++ b/src/client/src/components/layout/Header.js @@ -1,11 +1,14 @@ import React from "react"; import "../../styles/layout/Header.css"; +import { Link } from "react-router-dom"; const Header = () => { return (
-

LeetCode Problem Tracker

+ +

LeetCode Problem Tracker

+
); diff --git a/src/client/src/components/layout/Main.js b/src/client/src/components/layout/Main.js index 429a4e3..a493e2d 100644 --- a/src/client/src/components/layout/Main.js +++ b/src/client/src/components/layout/Main.js @@ -1,9 +1,7 @@ import React, { useState, useEffect, useMemo } from "react"; import { fetchProblems } from "../../services/api"; -import Header from "./Header"; import Nav from "./Nav"; import Body from "./Body"; -import Footer from "./Footer"; import "../../styles/layout/Main.css"; const Main = () => { @@ -53,7 +51,6 @@ const Main = () => { return (
-
); }; diff --git a/src/client/src/components/problems/ProblemCard.js b/src/client/src/components/problems/ProblemCard.js index ff81d28..d715b53 100644 --- a/src/client/src/components/problems/ProblemCard.js +++ b/src/client/src/components/problems/ProblemCard.js @@ -4,6 +4,7 @@ import { getDifficultyClass, } from "../../constants/difficulty"; import "../../styles/components/ProblemCard.css"; +import { Link } from "react-router-dom"; const TIME_PERIOD_LABELS = { "thirty-days": "Last 30 Days", @@ -43,9 +44,7 @@ const ProblemCard = ({ problem }) => {

- - {problem.title} - + {problem.title}

{difficultyText} diff --git a/src/client/src/components/problems/ProblemPage.js b/src/client/src/components/problems/ProblemPage.js index fd0fa56..7d4ccbd 100644 --- a/src/client/src/components/problems/ProblemPage.js +++ b/src/client/src/components/problems/ProblemPage.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import { getProblemById } from "../../services/api"; +import "../../styles/components/ProblemPage.css"; const tagOrder = ["thirty-days", "three-months", "six-months", "one-year", "all"]; diff --git a/src/client/src/styles/components/FilterOptions.css b/src/client/src/styles/components/FilterOptions.css index 0a17f25..a5ff42d 100644 --- a/src/client/src/styles/components/FilterOptions.css +++ b/src/client/src/styles/components/FilterOptions.css @@ -1,72 +1,180 @@ .filter-options { display: flex; - gap: 1.5rem; + gap: 2rem; align-items: flex-end; flex-wrap: wrap; - padding: 1rem 0; + padding: 1.5rem 0; + position: relative; + z-index: 10; } .filter-group { display: flex; flex-direction: column; - gap: 0.5rem; - min-width: 200px; + gap: 0.75rem; + min-width: 220px; + position: relative; + z-index: 1000; /* Ensure high z-index for dropdown */ } .filter-group label { - font-size: 0.9rem; - color: #666; - font-weight: 500; + font-size: 0.85rem; + color: #4a5568; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; } .filter-group select { - padding: 0.5rem; - border: 1px solid #ddd; - border-radius: 4px; + padding: 0.75rem 1rem; + border: 2px solid #e2e8f0; + border-radius: 10px; background-color: white; - font-size: 0.9rem; - color: #333; + font-size: 0.95rem; + color: #2d3748; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%234a5568'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 1rem center; + background-size: 1rem; + padding-right: 2.5rem; + font-weight: 500; + position: relative; + z-index: 1001; /* Higher z-index for the select element */ +} + +/* Ensure dropdown options are visible */ +.filter-group select option { + background: white; + color: #2d3748; + padding: 12px 16px; + font-size: 0.95rem; + border-bottom: 1px solid #f1f5f9; + position: relative; + z-index: 10000; /* Very high z-index for options */ +} + +.filter-group select option:hover { + background-color: #f0f7ff; + color: #0066cc; +} + +.filter-group select option:checked { + background-color: #0066cc; + color: white; +} + +.filter-group select:focus { + outline: none; + border-color: #0066cc; + box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1); + background-color: white; + z-index: 1002; /* Even higher when focused */ +} + +/* Style the dropdown container */ +.filter-group select { + z-index: 1001; +} + +/* Ensure the dropdown appears above other elements */ +.filter-group { + position: relative; +} + +/* Fix for dropdown visibility */ +select { + position: relative; } -.filter-group select:disabled { - background-color: #f5f5f5; - cursor: not-allowed; +/* Additional styles to ensure dropdown visibility */ +.filter-options * { + box-sizing: border-box; } +/* Company dropdown specific fixes */ +#company { + min-height: 48px; + max-height: 200px; /* Limit height if needed */ +} + +/* Ensure the nav has proper z-index */ +.nav { + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + border-bottom: 1px solid #e2e8f0; + padding: 0; + box-shadow: 0 2px 20px rgba(0, 0, 0, 0.06); + position: relative; + z-index: 100; +} + +.nav-content { + max-width: 1200px; + margin: 0 auto; + padding: 0 1.5rem; + position: relative; + z-index: 101; +} + +/* Clear filters button */ .clear-filters { - padding: 0.5rem 1rem; - background-color: #dc3545; + padding: 0.75rem 1.5rem; + background: linear-gradient(135deg, #dc3545, #c82333); color: white; border: none; - border-radius: 4px; + border-radius: 10px; cursor: pointer; font-size: 0.9rem; + font-weight: 600; height: fit-content; -} - -.clear-filters:hover { - background-color: #c82333; -} - -.clear-filters:disabled { - background-color: #f5f5f5; - color: #999; - cursor: not-allowed; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + text-transform: uppercase; + letter-spacing: 0.5px; + box-shadow: 0 2px 8px rgba(220, 53, 69, 0.3); + position: relative; + z-index: 100; } /* Responsive adjustments */ @media (max-width: 768px) { .filter-options { flex-direction: column; - gap: 1rem; + gap: 1.25rem; + padding: 1.25rem 0; + z-index: 1000; } .filter-group { width: 100%; min-width: unset; + z-index: 1001; + } + + .filter-group select { + width: 100%; + z-index: 1002; } .clear-filters { width: 100%; + margin-top: 0.5rem; + z-index: 100; } +} + +/* Emergency fix - if dropdown still doesn't show */ +.filter-group select { + transform: translateZ(0); /* Force hardware acceleration */ +} + +/* Alternative: Increase the size of the dropdown */ +.filter-group select { + min-height: 48px; +} + +/* Make sure the dropdown has enough space */ +.filter-group { + margin-bottom: 0; } \ No newline at end of file diff --git a/src/client/src/styles/components/ProblemCard.css b/src/client/src/styles/components/ProblemCard.css index c3f30b5..97027f8 100644 --- a/src/client/src/styles/components/ProblemCard.css +++ b/src/client/src/styles/components/ProblemCard.css @@ -1,96 +1,269 @@ .problem-card { - background: white; - border-radius: 8px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - padding: 1.5rem; - transition: transform 0.2s ease, box-shadow 0.2s ease; + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + border-radius: 12px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); + padding: 1.75rem; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + border: 1px solid rgba(255, 255, 255, 0.8); + position: relative; + overflow: hidden; +} + +.problem-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, #0066cc, #0099ff); + transform: scaleX(0); + transition: transform 0.3s ease; } .problem-card:hover { - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12); +} + +.problem-card:hover::before { + transform: scaleX(1); } .problem-header { display: flex; justify-content: space-between; align-items: flex-start; - margin-bottom: 1rem; + margin-bottom: 1.25rem; + gap: 1rem; } .problem-header h3 { margin: 0; - font-size: 1.1rem; + font-size: 1.25rem; flex: 1; - padding-right: 1rem; + font-weight: 600; + line-height: 1.4; } .problem-header a { - color: #1a1a1a; + color: #1a202c; text-decoration: none; + background: linear-gradient(90deg, #1a202c, #2d3748); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + transition: all 0.3s ease; + position: relative; +} + +.problem-header a::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 0; + height: 2px; + background: linear-gradient(90deg, #0066cc, #0099ff); + transition: width 0.3s ease; } .problem-header a:hover { - color: #0066cc; + background: linear-gradient(90deg, #0066cc, #0099ff); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.problem-header a:hover::after { + width: 100%; } .difficulty { - padding: 0.25rem 0.75rem; - border-radius: 12px; - font-size: 0.85rem; - font-weight: 500; + padding: 0.4rem 1rem; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + white-space: nowrap; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(255, 255, 255, 0.3); } .difficulty.easy { - background-color: #e7f6e7; - color: #2cbb5d; + background: linear-gradient(135deg, #43a047, #4caf50); + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } .difficulty.medium { - background-color: #fff7e6; - color: #ffa116; + background: linear-gradient(135deg, #ffa116, #ffb74d); + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } .difficulty.hard { - background-color: #ffe6e6; - color: #ff375f; + background: linear-gradient(135deg, #d32f2f, #f44336); + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); } .problem-stats { display: flex; - gap: 1rem; - margin-bottom: 1rem; + gap: 1.5rem; + margin-bottom: 1.25rem; font-size: 0.9rem; - color: #666; +} + +.problem-stats span { + display: flex; + align-items: center; + color: #4a5568; + font-weight: 500; + position: relative; +} + +.problem-stats span::before { + content: ''; + display: inline-block; + width: 4px; + height: 4px; + border-radius: 50%; + background: #cbd5e0; + margin-right: 0.5rem; +} + +.problem-stats span:first-child::before { + display: none; +} + +.acceptance { + color: #2d3748; + font-weight: 600; +} + +.frequency { + color: #4a5568; } .problem-companies { - border-top: 1px solid #eee; - padding-top: 1rem; + border-top: 1px solid #e2e8f0; + padding-top: 1.25rem; + margin-top: 1rem; } .problem-companies small { display: block; - margin-bottom: 0.5rem; - color: #666; + margin-bottom: 0.75rem; + color: #718096; + font-weight: 600; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.5px; } .company-tags { display: flex; flex-wrap: wrap; - gap: 0.5rem; + gap: 0.6rem; } .company-tag { - background: #f5f5f5; - padding: 0.25rem 0.75rem; - border-radius: 12px; - font-size: 0.85rem; + background: linear-gradient(135deg, #ffffff, #f7fafc); + padding: 0.5rem 1rem; + border-radius: 10px; + font-size: 0.8rem; display: flex; align-items: center; - gap: 0.25rem; + gap: 0.4rem; + border: 1px solid #e2e8f0; + transition: all 0.2s ease; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); + font-weight: 500; + color: #2d3748; +} + +.company-tag:hover { + transform: translateY(-1px); + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1); + border-color: #ffa116; + background: linear-gradient(135deg, #fffaf0, #fffbeb); +} + +.company-name { + font-weight: 600; + color: #2d3748; } .recent-indicator { - font-size: 0.8rem; + font-size: 0.7rem; + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.7; + transform: scale(1.1); + } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .problem-card { + padding: 1.25rem; + margin: 0.5rem; + } + + .problem-header { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + } + + .problem-header h3 { + font-size: 1.1rem; + padding-right: 0; + } + + .difficulty { + align-self: flex-start; + } + + .problem-stats { + flex-direction: column; + gap: 0.5rem; + } + + .problem-stats span::before { + display: none; + } + + .company-tags { + gap: 0.4rem; + } + + .company-tag { + padding: 0.4rem 0.8rem; + font-size: 0.75rem; + } +} + +/* Animation for card entrance */ +@keyframes cardEntrance { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.problem-card { + animation: cardEntrance 0.6s ease-out; } \ No newline at end of file diff --git a/src/client/src/styles/components/ProblemPage.css b/src/client/src/styles/components/ProblemPage.css new file mode 100644 index 0000000..3ddf68b --- /dev/null +++ b/src/client/src/styles/components/ProblemPage.css @@ -0,0 +1,271 @@ +.lcw-problem-page { + max-width: 900px; + margin: 32px auto; + background: #fff; + border-radius: 16px; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); + padding: 40px; + font-family: 'Segoe UI', 'Roboto', 'Arial', sans-serif; + color: #2d3748; + line-height: 1.6; +} + +/* Header Section */ +.lcw-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 24px; + border-bottom: 2px solid #f1f5f9; + padding-bottom: 20px; +} + +.lcw-header h2 { + margin: 0; + font-size: 28px; + font-weight: 700; + color: #1a202c; + line-height: 1.3; + flex: 1; +} + +.lcw-link { + color: #ffa116; + text-decoration: none; + font-weight: 600; + padding: 8px 16px; + border: 2px solid #ffa116; + border-radius: 8px; + transition: all 0.3s ease; + font-size: 14px; + white-space: nowrap; + margin-left: 20px; +} + +.lcw-link:hover { + background: #ffa116; + color: white; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 161, 22, 0.3); +} + +/* Meta Information */ +.lcw-meta { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin-bottom: 32px; + padding: 20px; + background: #f8fafc; + border-radius: 12px; + border-left: 4px solid #ffa116; +} + +.lcw-meta-item { + color: #4a5568; + font-size: 15px; + font-weight: 500; +} + +.lcw-difficulty { + padding: 6px 16px; + border-radius: 12px; + font-weight: 600; + color: #fff; + font-size: 14px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.lcw-difficulty-easy { + background: linear-gradient(135deg, #43a047, #4caf50); + box-shadow: 0 2px 8px rgba(67, 160, 71, 0.3); +} + +.lcw-difficulty-medium { + background: linear-gradient(135deg, #ffa116, #ffb74d); + box-shadow: 0 2px 8px rgba(255, 161, 22, 0.3); +} + +.lcw-difficulty-hard { + background: linear-gradient(135deg, #d32f2f, #f44336); + box-shadow: 0 2px 8px rgba(211, 47, 47, 0.3); +} + +.lcw-difficulty-unknown { + background: linear-gradient(135deg, #718096, #a0aec0); +} + +/* Section Title */ +.lcw-section-title { + margin: 40px 0 20px 0; + font-size: 22px; + color: #1a202c; + font-weight: 700; + position: relative; + padding-bottom: 12px; +} + +.lcw-section-title::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 60px; + height: 3px; + background: linear-gradient(90deg, #ffa116, #ffd54f); + border-radius: 2px; +} + +/* Table Styling */ +.lcw-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin-bottom: 32px; + background: #fff; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); +} + +.lcw-table th { + background: linear-gradient(135deg, #1a202c, #2d3748); + color: white; + font-weight: 600; + padding: 16px 20px; + text-align: left; + font-size: 15px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.lcw-table td { + padding: 20px; + border-bottom: 1px solid #e2e8f0; + background: #fff; + transition: background-color 0.2s ease; +} + +.lcw-table tr:last-child td { + border-bottom: none; +} + +.lcw-table tr:hover td { + background: #f7fafc; +} + +/* Tag Styling */ +.lcw-tag { + display: inline-block; + background: linear-gradient(135deg, #e2e8f0, #edf2f7); + color: #2d3748; + padding: 8px 16px; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + text-transform: capitalize; + border: 1px solid #cbd5e0; + min-width: 100px; + text-align: center; +} + +/* Company Tags */ +.lcw-company { + display: inline-block; + background: linear-gradient(135deg, #ffffff, #f7fafc); + color: #2d3748; + margin: 4px 8px 4px 0; + padding: 6px 14px; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + border: 1px solid #e2e8f0; + transition: all 0.2s ease; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.lcw-company:hover { + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border-color: #ffa116; + background: linear-gradient(135deg, #fffaf0, #fffbeb); +} + +/* Loading and Error States */ +.lcw-loading, .lcw-error { + text-align: center; + margin-top: 60px; + font-size: 18px; + padding: 40px; + border-radius: 12px; +} + +.lcw-loading { + color: #4a5568; + background: #f7fafc; + border: 2px dashed #cbd5e0; +} + +.lcw-error { + color: #e53e3e; + background: #fed7d7; + border: 2px solid #feb2b2; + font-weight: 600; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .lcw-problem-page { + margin: 16px; + padding: 24px; + border-radius: 12px; + } + + .lcw-header { + flex-direction: column; + align-items: flex-start; + } + + .lcw-link { + margin: 16px 0 0 0; + align-self: flex-start; + } + + .lcw-meta { + flex-direction: column; + gap: 12px; + align-items: flex-start; + } + + .lcw-table { + font-size: 14px; + } + + .lcw-table th, + .lcw-table td { + padding: 12px 8px; + } + + .lcw-company { + font-size: 12px; + padding: 4px 10px; + margin: 2px 4px 2px 0; + } +} + +/* Animation for page load */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.lcw-problem-page { + animation: fadeIn 0.6s ease-out; +} \ No newline at end of file diff --git a/src/client/src/styles/layout/Footer.css b/src/client/src/styles/layout/Footer.css index aa1159f..9e9425a 100644 --- a/src/client/src/styles/layout/Footer.css +++ b/src/client/src/styles/layout/Footer.css @@ -1,19 +1,64 @@ .footer { - background-color: #1a1a1a; + background: #1a1a1a; color: white; - padding: 1rem 0; + padding: 1.5rem 0; margin-top: auto; + border-top: 1px solid #2d2d2d; } .footer-content { max-width: 1200px; margin: 0 auto; - padding: 0 1rem; + padding: 0 2rem; text-align: center; } .footer p { margin: 0; - font-size: 0.9rem; - opacity: 0.8; + font-size: 0.95rem; + color: #a0a0a0; + font-weight: 400; + letter-spacing: 0.5px; + transition: color 0.2s ease; +} + +.footer p:hover { + color: #d0d0d0; +} + +/* Optional: Add a subtle top border accent */ +.footer { + position: relative; +} + +.footer::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 100px; + height: 2px; + background: linear-gradient(90deg, transparent, #ffa116, transparent); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .footer { + padding: 1.25rem 0; + } + + .footer-content { + padding: 0 1rem; + } + + .footer p { + font-size: 0.9rem; + } +} + +@media (max-width: 480px) { + .footer p { + font-size: 0.85rem; + } } \ No newline at end of file diff --git a/src/client/src/styles/layout/Header.css b/src/client/src/styles/layout/Header.css index 6648f4d..7796726 100644 --- a/src/client/src/styles/layout/Header.css +++ b/src/client/src/styles/layout/Header.css @@ -1,22 +1,229 @@ .header { width: 100%; - background-color: #1a1a1a; + background: linear-gradient(135deg, #1a1a1a 0%, #2d1b69 50%, #1a1a1a 100%); color: white; - padding: 1rem 0; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + padding: 1.25rem 0; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + position: relative; + overflow: hidden; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.header::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + radial-gradient(circle at 20% 80%, rgba(255, 161, 22, 0.1) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(0, 102, 204, 0.1) 0%, transparent 50%), + radial-gradient(circle at 40% 40%, rgba(67, 160, 71, 0.05) 0%, transparent 50%); + pointer-events: none; +} + +.header::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 1px; + background: linear-gradient(90deg, + transparent 0%, + rgba(255, 161, 22, 0.6) 25%, + rgba(0, 102, 204, 0.6) 50%, + rgba(255, 161, 22, 0.6) 75%, + transparent 100%); } .header-content { max-width: 1200px; margin: 0 auto; - padding: 0 1rem; + padding: 0 2rem; display: flex; align-items: center; justify-content: center; + position: relative; + z-index: 2; +} + +.header-link { + text-decoration: none; + color: inherit; + cursor: pointer; + transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); + position: relative; + padding: 0.5rem 1rem; + border-radius: 12px; +} + +.header-link::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 161, 22, 0.1), rgba(0, 102, 204, 0.1)); + border-radius: 12px; + opacity: 0; + transition: opacity 0.3s ease; +} + +.header-link:hover { + transform: translateY(-2px); + text-shadow: 0 4px 12px rgba(255, 255, 255, 0.3); +} + +.header-link:hover::before { + opacity: 1; +} + +.header-link:active { + transform: translateY(0); } .header h1 { margin: 0; + font-size: 2rem; + font-weight: 700; + background: linear-gradient(135deg, #ffffff 0%, #ffa116 50%, #0099ff 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-align: center; + line-height: 1.2; + letter-spacing: -0.5px; + position: relative; + padding: 0.5rem 0; +} + +.header h1::after { + content: '🔍'; + position: absolute; + top: 50%; + right: -2rem; + transform: translateY(-50%); font-size: 1.5rem; - font-weight: 600; + opacity: 0.8; + animation: float 3s ease-in-out infinite; +} + +/* Code-inspired decoration */ +.header-content::before { + content: '{ }'; + position: absolute; + left: 2rem; + top: 50%; + transform: translateY(-50%); + font-size: 1.2rem; + color: rgba(255, 161, 22, 0.6); + font-family: 'Courier New', monospace; + font-weight: bold; + opacity: 0.7; +} + +.header-content::after { + content: ''; + position: absolute; + right: 2rem; + top: 50%; + transform: translateY(-50%); + font-size: 1.2rem; + color: rgba(0, 102, 204, 0.6); + font-family: 'Courier New', monospace; + font-weight: bold; + opacity: 0.7; +} + +/* Floating animation */ +@keyframes float { + 0%, 100% { + transform: translateY(-50%) rotate(0deg); + } + 50% { + transform: translateY(-50%) rotate(10deg); + } +} + +/* Glow effect on hover */ +@keyframes textGlow { + 0%, 100% { + text-shadow: 0 0 20px rgba(255, 255, 255, 0.3); + } + 50% { + text-shadow: 0 0 30px rgba(255, 161, 22, 0.5), 0 0 40px rgba(0, 102, 204, 0.3); + } +} + +.header-link:hover h1 { + animation: textGlow 2s ease-in-out infinite; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .header { + padding: 1rem 0; + } + + .header-content { + padding: 0 1rem; + } + + .header h1 { + font-size: 1.5rem; + } + + .header h1::after { + right: -1.5rem; + font-size: 1.2rem; + } + + .header-content::before, + .header-content::after { + display: none; /* Hide code symbols on mobile */ + } +} + +@media (max-width: 480px) { + .header h1 { + font-size: 1.25rem; + letter-spacing: -0.25px; + } + + .header h1::after { + display: none; /* Hide emoji on very small screens */ + } + + .header-link { + padding: 0.25rem 0.5rem; + } +} + +/* Reduced motion support */ +@media (prefers-reduced-motion: reduce) { + .header-link { + transition: none; + } + + .header-link:hover { + transform: none; + } + + .header h1::after { + animation: none; + } + + .header-link:hover h1 { + animation: none; + } +} + +/* Dark theme optimization */ +@media (prefers-color-scheme: dark) { + .header { + background: linear-gradient(135deg, #0a0a0a 0%, #1a0f4d 50%, #0a0a0a 100%); + } } \ No newline at end of file From 2898cfa7fd40553d20fa7e9657149b35c90d2a66 Mon Sep 17 00:00:00 2001 From: Swoyam Date: Sat, 11 Oct 2025 16:47:58 +0530 Subject: [PATCH 2/2] implemented pagination --- src/client/src/components/layout/Body.js | 18 +++- src/client/src/components/layout/Main.js | 27 +++++- .../src/components/layout/Pagination.js | 75 +++++++++++++++ src/client/src/services/api.js | 24 ++++- src/client/src/styles/layout/Body.css | 7 +- src/client/src/styles/layout/Pagination.css | 94 +++++++++++++++++++ 6 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 src/client/src/components/layout/Pagination.js create mode 100644 src/client/src/styles/layout/Pagination.css diff --git a/src/client/src/components/layout/Body.js b/src/client/src/components/layout/Body.js index 249e4c0..764138a 100644 --- a/src/client/src/components/layout/Body.js +++ b/src/client/src/components/layout/Body.js @@ -1,8 +1,17 @@ import React from "react"; import ProblemGrid from "../problems/ProblemGrid"; +import Pagination from "./Pagination"; // Add this import import "../../styles/layout/Body.css"; -const Body = ({ problems, loading, error, filters }) => { +const Body = ({ + problems, + loading, + error, + filters, + currentPage, + totalPages, + onPageChange +}) => { if (loading) { return (
@@ -26,9 +35,14 @@ const Body = ({ problems, loading, error, filters }) => {
+
); }; -export default Body; +export default Body; \ No newline at end of file diff --git a/src/client/src/components/layout/Main.js b/src/client/src/components/layout/Main.js index a493e2d..46136d3 100644 --- a/src/client/src/components/layout/Main.js +++ b/src/client/src/components/layout/Main.js @@ -13,14 +13,25 @@ const Main = () => { timePeriod: "", difficulty: "", }); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + + const PROBLEMS_PER_PAGE = 50; useEffect(() => { const loadProblems = async () => { setLoading(true); try { - const data = await fetchProblems(); + const data = await fetchProblems(currentPage, PROBLEMS_PER_PAGE, filters); setProblems(data); setError(null); + + // Estimate total pages based on response + if (data.length < PROBLEMS_PER_PAGE) { + setTotalPages(currentPage); + } else { + setTotalPages(currentPage + 1); + } } catch (err) { setError("Failed to fetch problems. Please try again later."); setProblems([]); @@ -30,7 +41,7 @@ const Main = () => { }; loadProblems(); - }, []); + }, [currentPage, filters]); // Extract unique company names from problems const companies = useMemo(() => { @@ -47,6 +58,13 @@ const Main = () => { const handleFilterChange = (newFilters) => { setFilters(newFilters); + setCurrentPage(1); // Reset to first page when filters change + }; + + const handlePageChange = (page) => { + setCurrentPage(page); + // Scroll to top when page changes + window.scrollTo({ top: 0, behavior: 'smooth' }); }; return ( @@ -61,9 +79,12 @@ const Main = () => { loading={loading} error={error} filters={filters} + currentPage={currentPage} + totalPages={totalPages} + onPageChange={handlePageChange} />
); }; -export default Main; +export default Main; \ No newline at end of file diff --git a/src/client/src/components/layout/Pagination.js b/src/client/src/components/layout/Pagination.js new file mode 100644 index 0000000..2458d84 --- /dev/null +++ b/src/client/src/components/layout/Pagination.js @@ -0,0 +1,75 @@ +// components/Pagination.js +import React from "react"; +import "../../styles/layout/Pagination.css"; + +const Pagination = ({ currentPage, totalPages, onPageChange }) => { + const getPageNumbers = () => { + const pages = []; + const maxVisiblePages = 5; + + let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2)); + let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); + + // Adjust start page if we're near the end + if (endPage - startPage + 1 < maxVisiblePages) { + startPage = Math.max(1, endPage - maxVisiblePages + 1); + } + + for (let i = startPage; i <= endPage; i++) { + pages.push(i); + } + + return pages; + }; + + if (totalPages <= 1) return null; + + return ( +
+ {/* First Page Button */} + + + {/* Previous Page Button */} + + + {/* Page Numbers */} +
+ {getPageNumbers().map(page => ( + + ))} +
+ + {/* Next Page Button */} + +
+ ); +}; + +export default Pagination; \ No newline at end of file diff --git a/src/client/src/services/api.js b/src/client/src/services/api.js index 3f4137e..b57069a 100644 --- a/src/client/src/services/api.js +++ b/src/client/src/services/api.js @@ -1,7 +1,25 @@ +// services/api.js const API_BASE_URL = "http://localhost:5164/api"; -export const fetchProblems = async () => { - const response = await fetch(`${API_BASE_URL}/problems`); +export const fetchProblems = async (page = 1, limit = 50, filters = {}) => { + const skip = (page - 1) * limit; + const params = new URLSearchParams({ + skip: skip.toString(), + limit: limit.toString() + }); + + // Add filter parameters if they exist + if (filters.company) { + params.append('companies', filters.company); + } + if (filters.difficulty) { + params.append('difficulties', filters.difficulty); + } + if (filters.timePeriod) { + params.append('tags', filters.timePeriod); + } + + const response = await fetch(`${API_BASE_URL}/problems?${params}`); if (!response.ok) { throw new Error(`Network response was not ok: ${response.status}`); } @@ -14,4 +32,4 @@ export const getProblemById = async (id) => { throw new Error(`Failed to fetch problem #${id}`); } return await response.json(); -}; +}; \ No newline at end of file diff --git a/src/client/src/styles/layout/Body.css b/src/client/src/styles/layout/Body.css index 7824269..c98c329 100644 --- a/src/client/src/styles/layout/Body.css +++ b/src/client/src/styles/layout/Body.css @@ -46,4 +46,9 @@ .error-message button:hover { background-color: #c82333; -} \ No newline at end of file +} + +/* Add to styles/layout/Body.css */ +.body-content { + min-height: 60vh; +} diff --git a/src/client/src/styles/layout/Pagination.css b/src/client/src/styles/layout/Pagination.css new file mode 100644 index 0000000..a29519d --- /dev/null +++ b/src/client/src/styles/layout/Pagination.css @@ -0,0 +1,94 @@ +/* styles/components/Pagination.css */ +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + margin: 2rem 0; + padding: 1rem; +} + +.pagination-pages { + display: flex; + gap: 0.25rem; +} + +.pagination-btn { + padding: 0.5rem 0.75rem; + border: 1px solid #e2e8f0; + background: white; + color: #4a5568; + border-radius: 6px; + cursor: pointer; + transition: all 0.2s ease; + font-weight: 500; + min-width: 2.5rem; + display: flex; + align-items: center; + justify-content: center; +} + +.pagination-btn:hover:not(:disabled) { + background: #f7fafc; + border-color: #cbd5e0; + transform: translateY(-1px); +} + +.pagination-btn.active { + background: #0066cc; + color: white; + border-color: #0066cc; +} + +.pagination-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + transform: none; +} + +/* Specific styles for navigation buttons */ +.pagination-first, +.pagination-last { + font-weight: bold; + font-size: 1.1rem; +} + +.pagination-prev, +.pagination-next { + font-weight: bold; +} + +.pagination-info { + text-align: center; + color: #666; + margin-bottom: 1rem; + font-size: 0.9rem; +} + +/* Responsive */ +@media (max-width: 768px) { + .pagination { + gap: 0.25rem; + } + + .pagination-btn { + padding: 0.4rem 0.6rem; + font-size: 0.9rem; + min-width: 2.25rem; + } + + .pagination-pages { + gap: 0.125rem; + } + + /* Hide page numbers on very small screens, keep only navigation */ + @media (max-width: 480px) { + .pagination-pages { + display: none; + } + + .pagination { + gap: 0.5rem; + } + } +} \ No newline at end of file