diff --git a/client/src/Style/UserDashboard.css b/client/src/Style/UserDashboard.css index 835646c..4cf9454 100644 --- a/client/src/Style/UserDashboard.css +++ b/client/src/Style/UserDashboard.css @@ -1114,4 +1114,89 @@ input:checked + .slider:before { flex-direction: column; gap: 10px; } -} \ No newline at end of file +} +/* =============================== + ADMIN FEEDBACK TABLE FIX + =============================== */ + +.table-wrapper { + width: 100%; + overflow-x: auto; + margin-top: 20px; +} + +.admin-table { + width: 100%; + border-collapse: collapse; + background: #0f0f14; + color: #ffffff; + border: 1px solid rgba(255, 255, 255, 0.25); + table-layout: fixed; +} + +/* Table Head */ +.admin-table thead th { + background: #161622; + color: #ffffff; + font-weight: 600; + padding: 14px 16px; + border: 1px solid rgba(255, 255, 255, 0.25); + text-align: left; + white-space: nowrap; +} + +/* Table Body */ +.admin-table tbody td { + padding: 14px 16px; + border: 1px solid rgba(255, 255, 255, 0.18); + vertical-align: top; + word-wrap: break-word; + font-size: 14px; +} + +/* Row hover */ +.admin-table tbody tr:hover { + background: rgba(255, 255, 255, 0.05); +} + +/* Column width control */ +.admin-table th:nth-child(1), +.admin-table td:nth-child(1) { + width: 15%; +} + +.admin-table th:nth-child(2), +.admin-table td:nth-child(2) { + width: 22%; +} + +.admin-table th:nth-child(3), +.admin-table td:nth-child(3) { + width: 33%; +} + +.admin-table th:nth-child(4), +.admin-table td:nth-child(4) { + width: 18%; +} + +.admin-table th:nth-child(5), +.admin-table td:nth-child(5) { + width: 12%; + text-align: center; +} + +/* Delete button */ +.btn-danger { + background: #e63946; + color: #fff; + border: none; + padding: 6px 14px; + border-radius: 6px; + cursor: pointer; + font-size: 13px; +} + +.btn-danger:hover { + background: #d62828; +} diff --git a/client/src/components/Dashboard/DashboardSidebar.jsx b/client/src/components/Dashboard/DashboardSidebar.jsx index 4ac8721..0e93ba9 100644 --- a/client/src/components/Dashboard/DashboardSidebar.jsx +++ b/client/src/components/Dashboard/DashboardSidebar.jsx @@ -86,6 +86,14 @@ const DashboardSidebar = ({ {!sidebarCollapsed && Settings} + {/* Quick Links Section */} diff --git a/client/src/components/Dashboard/FeedbackTab.jsx b/client/src/components/Dashboard/FeedbackTab.jsx new file mode 100644 index 0000000..b0d2c2b --- /dev/null +++ b/client/src/components/Dashboard/FeedbackTab.jsx @@ -0,0 +1,91 @@ +import React, { useEffect, useState } from 'react'; +import { collection, getDocs, deleteDoc, doc } from 'firebase/firestore'; +import { db } from '../../firebase'; +import '../../Style/UserDashboard.css'; + +const FeedbackTab = () => { + const [feedbackList, setFeedbackList] = useState([]); + const [loading, setLoading] = useState(true); + + const fetchFeedback = async () => { + try { + const querySnapshot = await getDocs(collection(db, 'feedback')); + const feedbackData = querySnapshot.docs.map((docSnap) => ({ + id: docSnap.id, + ...docSnap.data(), + })); + setFeedbackList(feedbackData); + } catch (error) { + console.error('Error fetching feedback:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchFeedback(); + }, []); + + const handleDelete = async (id) => { + const confirmDelete = window.confirm('Are you sure you want to delete this feedback?'); + if (!confirmDelete) return; + + try { + await deleteDoc(doc(db, 'feedback', id)); + setFeedbackList((prev) => prev.filter((item) => item.id !== id)); + } catch (error) { + console.error('Error deleting feedback:', error); + alert('Failed to delete feedback. Please try again.'); + } + }; + + return ( +
+

User Feedback

+ + {loading ? ( +

Loading feedback...

+ ) : feedbackList.length === 0 ? ( +

No feedback submitted yet.

+ ) : ( +
+ + + + + + + + + + + + {feedbackList.map((feedback) => ( + + + + + + + + ))} + +
UserEmailMessageDateAction
{feedback.name || 'Anonymous'}{feedback.email || '-'}{feedback.message} + {feedback.createdAt?.seconds + ? new Date(feedback.createdAt.seconds * 1000).toLocaleString() + : '-'} + + +
+
+ )} +
+ ); +}; + +export default FeedbackTab; diff --git a/client/src/components/UserDashboard.jsx b/client/src/components/UserDashboard.jsx index 5636e8b..7f6ab33 100644 --- a/client/src/components/UserDashboard.jsx +++ b/client/src/components/UserDashboard.jsx @@ -18,6 +18,7 @@ import OverviewTab from './Dashboard/OverviewTab'; import SnippetsTab from './Dashboard/SnippetsTab'; import AchievementsTab from './Dashboard/AchievementsTab'; import SettingsTab from './Dashboard/SettingsTab'; +import FeedbackTab from './Dashboard/FeedbackTab'; /** * UserDashboard Component - Comprehensive User Dashboard @@ -332,6 +333,9 @@ const UserDashboard = () => { identityLabel={identityLabel} /> )} + {activeTab === 'feedback' && ( + + )} {/* Placeholder for other tabs */} {activeTab === 'coding-stats' && ( diff --git a/client/src/firebase.js b/client/src/firebase.js index abe0185..0c25cc4 100644 --- a/client/src/firebase.js +++ b/client/src/firebase.js @@ -1,5 +1,6 @@ // src/firebase.js import { initializeApp, getApps } from "firebase/app"; +import { getFirestore } from "firebase/firestore"; import { getAuth, GoogleAuthProvider, @@ -34,6 +35,7 @@ const isFirebaseConfigured = () => { */ let app = null; let auth = null; +let db = null; let googleProvider = null; let githubProvider = null; @@ -41,6 +43,7 @@ try { if (isFirebaseConfigured()) { app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0]; auth = getAuth(app); + db = getFirestore(app); googleProvider = new GoogleAuthProvider(); githubProvider = new GithubAuthProvider(); googleProvider.setCustomParameters({ prompt: "select_account" }); @@ -53,6 +56,7 @@ try { export { app, auth, + db, googleProvider, githubProvider, -}; +}; \ No newline at end of file