diff --git a/src/pages/Feedback.css b/src/pages/Feedback.css
index e48fca9..7e7f9bd 100644
--- a/src/pages/Feedback.css
+++ b/src/pages/Feedback.css
@@ -80,3 +80,48 @@
text-align: center;
font-size: 14px;
}
+
+.feedback-subtitle {
+ font-size: 0.9rem;
+ color: #aaa;
+ margin-bottom: 1rem;
+ text-align: center;
+}
+
+.feedback-select {
+ padding: 10px;
+ border-radius: 8px;
+ border: 1px solid #333;
+ background: #111;
+ color: #fff;
+}
+
+.feedback-rating {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin: 10px 0;
+ color: #ccc;
+}
+
+.star {
+ font-size: 1.5rem;
+ cursor: pointer;
+ color: #555;
+ transition: 0.3s;
+}
+
+.star:hover,
+.star.active {
+ color: #facc15;
+ transform: scale(1.2);
+}
+
+.feedback-success {
+ background: rgba(0, 255, 150, 0.1);
+ color: #00ffae;
+ padding: 10px;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+ text-align: center;
+}
\ No newline at end of file
diff --git a/src/pages/Feedback.jsx b/src/pages/Feedback.jsx
index 75b820e..f485cbb 100644
--- a/src/pages/Feedback.jsx
+++ b/src/pages/Feedback.jsx
@@ -1,14 +1,17 @@
import React, { useState } from "react";
-import "./Feedback.css"; // reuse existing theme styling
+import "./Feedback.css";
const Feedback = () => {
const [formData, setFormData] = useState({
name: "",
email: "",
+ category: "general",
+ rating: 0,
message: "",
});
const [submitted, setSubmitted] = useState(false);
+ const [loading, setLoading] = useState(false);
const handleChange = (e) => {
setFormData({
@@ -17,64 +20,116 @@ const Feedback = () => {
});
};
+ const handleRating = (value) => {
+ setFormData({ ...formData, rating: value });
+ };
+
const handleSubmit = (e) => {
e.preventDefault();
if (!formData.message.trim()) return;
- setSubmitted(true);
+ setLoading(true);
- setFormData({
- name: "",
- email: "",
- message: "",
- });
+ // simulate API call
+ setTimeout(() => {
+ setSubmitted(true);
+ setLoading(false);
+
+ setFormData({
+ name: "",
+ email: "",
+ category: "general",
+ rating: 0,
+ message: "",
+ });
+ }, 1200);
};
return (
-
-
-
Share Your Feedback
-
- {submitted && (
-
- ✅ Thank you! Your feedback has been submitted.
-
- )}
-
-
+
+
+
💬 Share Your Feedback
+
+ Help us improve CryptoHub with your valuable feedback.
+
+
+ {submitted && (
+
+ 🎉 Thanks! Your feedback helps us improve.
+
+ )}
+
+
+
-
-);
+ );
};
-export default Feedback;
+export default Feedback;
\ No newline at end of file
diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx
index 019837f..928a179 100644
--- a/src/pages/Home/Home.jsx
+++ b/src/pages/Home/Home.jsx
@@ -1,4 +1,4 @@
-import React, { useContext, useEffect, useState } from "react";
+import React, { useContext, useEffect, useState, useMemo } from "react";
import "./Home.css";
import { CoinContext } from "../../context/CoinContextInstance";
import { Link } from "react-router-dom";
@@ -18,62 +18,100 @@ import { useWatchlist } from "../../context/WatchlistContext";
const Home = () => {
const { allCoin, filteredCoins, currency } = useContext(CoinContext);
const { isInWatchlist, toggleWatchlist } = useWatchlist();
+
const [displayCoin, setDisplayCoin] = useState([]);
const [input, setInput] = useState("");
const [showFilters, setShowFilters] = useState(false);
const [minPrice, setMinPrice] = useState("");
const [maxPrice, setMaxPrice] = useState("");
- // Pagination State
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;
- const inputHandler = (e) => {
- setInput(e.target.value);
- if (e.target.value === "") {
- setDisplayCoin(filteredCoins);
- }
- setCurrentPage(1); // Reset to first page on search
- };
+ // =========================
+ // 🔍 SMART SEARCH (Debounce)
+ // =========================
+ useEffect(() => {
+ const delay = setTimeout(() => {
+ if (!input) {
+ setDisplayCoin(filteredCoins);
+ } else {
+ const filtered = filteredCoins.filter((coin) =>
+ coin.name.toLowerCase().includes(input.toLowerCase())
+ );
+ setDisplayCoin(filtered);
+ }
+ setCurrentPage(1);
+ }, 300);
- const searchHandler = (e) => {
- e.preventDefault();
- if (input && filteredCoins) {
- setDisplayCoin(
- filteredCoins.filter((item) =>
- item.name.toLowerCase().includes(input.toLowerCase()),
- ),
- );
- } else {
- setDisplayCoin(filteredCoins);
- }
- setCurrentPage(1); // Reset to first page on search
- };
+ return () => clearTimeout(delay);
+ }, [input, filteredCoins]);
+ // =========================
+ // 🎯 FILTER LOGIC
+ // =========================
const applyFilters = () => {
let filtered = [...filteredCoins];
- if (minPrice)
+
+ if (minPrice) {
filtered = filtered.filter(
- (coin) => coin.current_price >= Number(minPrice),
+ (coin) => coin.current_price >= Number(minPrice)
);
- if (maxPrice)
+ }
+
+ if (maxPrice) {
filtered = filtered.filter(
- (coin) => coin.current_price <= Number(maxPrice),
+ (coin) => coin.current_price <= Number(maxPrice)
);
+ }
+
setDisplayCoin(filtered);
setShowFilters(false);
- setCurrentPage(1); // Reset to first page on filter
+ setCurrentPage(1);
};
useEffect(() => {
setDisplayCoin(filteredCoins);
}, [filteredCoins]);
- // Pagination Logic
+ // =========================
+ // 📊 GLOBAL STATS
+ // =========================
+ const totalMarketCap = useMemo(
+ () => allCoin?.reduce((sum, c) => sum + c.market_cap, 0) || 0,
+ [allCoin]
+ );
+
+ const avgChange = useMemo(
+ () =>
+ allCoin?.reduce(
+ (sum, c) => sum + c.price_change_percentage_24h,
+ 0
+ ) / (allCoin?.length || 1),
+ [allCoin]
+ );
+
+ // =========================
+ // 🔥 TRENDING COINS
+ // =========================
+ const trendingCoins = useMemo(() => {
+ return [...(displayCoin || [])]
+ .sort(
+ (a, b) =>
+ b.price_change_percentage_24h -
+ a.price_change_percentage_24h
+ )
+ .slice(0, 5);
+ }, [displayCoin]);
+
+ // =========================
+ // 📄 PAGINATION
+ // =========================
const totalPages = Math.ceil((displayCoin.length || 0) / itemsPerPage);
+
const currentCoins = displayCoin.slice(
(currentPage - 1) * itemsPerPage,
- currentPage * itemsPerPage,
+ currentPage * itemsPerPage
);
const handlePageChange = (newPage) => {
@@ -81,337 +119,205 @@ const Home = () => {
setCurrentPage(newPage);
const section = document.querySelector(".market-section");
if (section) {
- window.scrollTo({ top: section.offsetTop - 100, behavior: "smooth" });
+ window.scrollTo({
+ top: section.offsetTop - 100,
+ behavior: "smooth",
+ });
}
}
};
+ // =========================
+ // 🧱 SKELETON UI
+ // =========================
+ const SkeletonRows = () => (
+
+ {Array.from({ length: 8 }).map((_, i) => (
+
+ ))}
+
+ );
+
return (
- {/* -------------------------------------------
- COSMIC HERO SECTION
- -------------------------------------------
- */}
-
-
-
-
- {/* Floating Elements (Orbitals) */}
-
- Bitcoin
-
- +5.2%
-
-
-
-
- Ethereum
-
- +1.5%
-
-
-
-
- Solana
-
- +8.5%
-
-
-
-
- Cardano
- -2.1%
-
-
-
- BNB
-
- +1.2%
-
-
+ {/* ================= HERO ================= */}
+
- {showFilters && (
-
- setMinPrice(e.target.value)}
- />
- setMaxPrice(e.target.value)}
- />
-
-
- )}
-
+ {/* ================= GLOBAL STATS ================= */}
+
+
+
Market Cap
+
{currency.symbol}{totalMarketCap.toLocaleString()}
+
+
+
+
Avg Change
+
0 ? "positive" : "negative"}>
+ {avgChange.toFixed(2)}%
+
+
+
+
+
Total Coins
+
{allCoin?.length}
+
+
+
+ {/* ================= TRENDING ================= */}
+
+ 🔥 Trending
+
+
+ {trendingCoins.map((coin) => (
+
+

+
{coin.symbol.toUpperCase()}
+
+ +{coin.price_change_percentage_24h.toFixed(2)}%
+
+
+ ))}
- {/* -------------------------------------------
- MARKET DATA SECTION
- -------------------------------------------
- */}
+ {/* ================= MARKET TABLE ================= */}
-
+
+
+ {/* HEADER */}
-
-
#
-
Asset
-
Price
-
24h Change
-
Market Cap
+
#
+
Name
+
Price
+
24h
+
Market Cap
-
- {currentCoins && currentCoins.length > 0 ? (
- currentCoins.map((item) => (
-
-
-
-
-
- {item.market_cap_rank}
-
-
-

-
-
- {item.symbol.toUpperCase()}
-
- {item.name}
-
-
-
- {currency.symbol || currency.Symbol}
- {item.current_price.toLocaleString()}
-
-
0 ? "positive" : "negative"}`}
- >
- {item.price_change_percentage_24h > 0 ? (
-
- ) : (
-
- )}
- {Math.abs(item.price_change_percentage_24h).toFixed(2)}%
-
-
- {currency.symbol || currency.Symbol}
- {item.market_cap.toLocaleString()}
-
-
- ))
- ) : (
-
+ ) : currentCoins.length === 0 ? (
+
No coins found
+ ) : (
+ currentCoins.map((item) => (
+
- {allCoin && allCoin.length === 0
- ? "Loading crypto data..."
- : "No coins found. Try adjusting your filters."}
-
- )}
-
+
{item.market_cap_rank}
+
+
+

+ {item.name}
+
+
+
+ {currency.symbol}
+ {item.current_price.toLocaleString()}
+
+
+
0
+ ? "positive"
+ : "negative"
+ }
+ >
+ {item.price_change_percentage_24h > 0 ? (
+
+ ) : (
+
+ )}
+ {item.price_change_percentage_24h.toFixed(2)}%
+
+
+
+ {currency.symbol}
+ {item.market_cap.toLocaleString()}
+
+
+ ))
+ )}
- {/* Pagination Controls */}
+ {/* PAGINATION */}
{totalPages > 1 && (
-
+
-
- {totalPages <= 5
- ? // Show all pages if 5 or fewer
- Array.from({ length: totalPages }, (_, i) => i + 1).map(
- (pageNum) => (
-
- ),
- )
- : // Logic to show a window of pages
- (() => {
- const pages = [];
- if (currentPage <= 3) {
- for (let i = 1; i <= 5; i++) pages.push(i);
- } else if (currentPage >= totalPages - 2) {
- for (let i = totalPages - 4; i <= totalPages; i++)
- pages.push(i);
- } else {
- for (let i = currentPage - 2; i <= currentPage + 2; i++)
- pages.push(i);
- }
- return pages.map((pageNum) => (
-
- ));
- })()}
-
+
+ Page {currentPage} of {totalPages}
+
)}
-
+ {/* BACK TO TOP */}
+
);
};
-export default Home;
+export default Home;
\ No newline at end of file