-
Notifications
You must be signed in to change notification settings - Fork 57
feat: Add 404 Not Found Page #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import React, { useState, useEffect } from 'react'; | ||
|
|
||
| /** | ||
| * ScrollToTop - Floating scroll-to-top button component | ||
| * | ||
| * Appears when user scrolls down more than 300px. | ||
| * Smooth scroll animation to top on click. | ||
| * Fade-in/fade-out animation on appear/disappear. | ||
| */ | ||
| export function ScrollToTop() { | ||
| const [isVisible, setIsVisible] = useState(false); | ||
|
|
||
| // Toggle visibility based on scroll position | ||
| useEffect(() => { | ||
| const handleScroll = () => { | ||
| const scrollPosition = window.scrollY; | ||
| setIsVisible(scrollPosition > 300); | ||
| }; | ||
|
|
||
| window.addEventListener('scroll', handleScroll); | ||
|
|
||
|
Comment on lines
+14
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n frontend/src/components/common/ScrollToTop.tsxRepository: SolFoundry/solfoundry Length of output: 2242 Address scroll performance and fix documentation mismatch. The scroll listener and state update pattern has two issues:
Minor: Adding 🤖 Prompt for AI Agents |
||
| // Initial check | ||
| handleScroll(); | ||
|
|
||
| return () => window.removeEventListener('scroll', handleScroll); | ||
| }, []); | ||
|
|
||
| // Smooth scroll to top | ||
| const scrollToTop = () => { | ||
| window.scrollTo({ | ||
| top: 0, | ||
| behavior: 'smooth' | ||
| }); | ||
| }; | ||
|
|
||
| if (!isVisible) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <button | ||
| onClick={scrollToTop} | ||
| className="fixed bottom-6 right-6 z-50 p-3 rounded-full | ||
| bg-[#9945FF] hover:bg-[#8a3fe6] text-white | ||
| border border-[#9945FF]/30 shadow-lg shadow-[#9945FF]/20 | ||
| transition-all duration-300 ease-in-out | ||
| hover:scale-110 active:scale-95 | ||
| opacity-100" | ||
| aria-label="Scroll to top" | ||
| title="Scroll to top" | ||
| > | ||
| {/* Up arrow SVG icon */} | ||
| <svg | ||
| className="w-5 h-5" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke="currentColor" | ||
| strokeWidth={2.5} | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| > | ||
| <path d="M5 15l7-7 7 7" /> | ||
| </svg> | ||
| </button> | ||
| ); | ||
| } | ||
|
|
||
| export default ScrollToTop; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { ScrollToTop } from './ScrollToTop'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import React, { useState, useEffect, useCallback } from 'react'; | ||
| import { ScrollToTop } from '../common/ScrollToTop'; | ||
|
|
||
| // ============================================================================ | ||
| // Types | ||
|
|
@@ -153,6 +154,9 @@ export function SiteLayout({ | |
|
|
||
| {/* Footer */} | ||
| <Footer /> | ||
|
|
||
| {/* Scroll to Top Button */} | ||
| <ScrollToTop /> | ||
|
Comment on lines
+158
to
+159
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Global placement introduces an overlay interaction edge case. With Line 159 mounting the floating button globally, it can remain clickable during mobile menu state because the overlay is As per coding guidelines, "frontend/**: React/TypeScript frontend. Check: Integration with existing components". 🤖 Prompt for AI Agents |
||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| /** | ||
| * 404 Not Found Page | ||
| * Displayed when user visits an invalid route. | ||
| * Matches SolFoundry dark theme styling. | ||
| */ | ||
| import { Link } from 'react-router-dom'; | ||
|
|
||
| export function NotFoundPage() { | ||
| return ( | ||
| <div className="flex flex-col items-center justify-center min-h-[60vh] px-4 text-center"> | ||
| {/* Logo */} | ||
| <div className="mb-8"> | ||
| <div className="w-20 h-20 mx-auto rounded-2xl bg-gradient-to-br from-[#9945FF] to-[#14F195] flex items-center justify-center"> | ||
| <svg | ||
| className="w-10 h-10 text-white" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke="currentColor" | ||
| strokeWidth={2} | ||
| > | ||
| <path | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| d="M13 10V3L4 14h7v7l9-11h-7z" | ||
| /> | ||
| </svg> | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* 404 Text */} | ||
| <h1 className="text-6xl font-bold text-white mb-2">404</h1> | ||
| <h2 className="text-xl font-semibold text-gray-300 mb-4">Page not found</h2> | ||
| <p className="text-sm text-gray-500 mb-8 max-w-md"> | ||
| The page you're looking for doesn't exist or has been moved. | ||
| </p> | ||
|
|
||
| {/* Action Buttons */} | ||
| <div className="flex flex-col sm:flex-row gap-4"> | ||
| <Link | ||
| to="/" | ||
| className="inline-flex items-center justify-center px-6 py-3 rounded-xl bg-gradient-to-r from-[#9945FF] to-[#14F195] text-white font-semibold hover:opacity-90 transition-opacity" | ||
| > | ||
| <svg | ||
| className="w-5 h-5 mr-2" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke="currentColor" | ||
| strokeWidth={2} | ||
| > | ||
| <path | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" | ||
| /> | ||
| </svg> | ||
| Back to Home | ||
| </Link> | ||
| <Link | ||
| to="/bounties" | ||
| className="inline-flex items-center justify-center px-6 py-3 rounded-xl border border-gray-700 text-gray-300 font-semibold hover:bg-gray-800 hover:border-gray-600 transition-colors" | ||
| > | ||
| Browse open bounties | ||
| <svg | ||
| className="w-5 h-5 ml-2" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke="currentColor" | ||
| strokeWidth={2} | ||
| > | ||
| <path | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| d="M17 8l4 4m0 0l-4 4m4-4H3" | ||
| /> | ||
| </svg> | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| export default NotFoundPage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documented fade-out behavior is not actually implemented.
Lines 8-9 describe fade-in/fade-out, but Lines 36-38 unmount immediately when hidden, so there is no fade-out transition path.
As per coding guidelines, "frontend/**: React/TypeScript frontend. Check: Component structure and state management".
Also applies to: 36-38
🤖 Prompt for AI Agents