feat: dashboard

This commit is contained in:
Malopieds 2024-10-17 22:18:34 +02:00
parent f42bc99a9d
commit 9b75ecf868
Signed by: malopieds
GPG Key ID: 2E9430D75356529B
6 changed files with 156 additions and 138 deletions

View File

@ -12,11 +12,12 @@ import CreateTp from "./pages/admin/CreateTp";
import BulkUsers from "./pages/admin/BulkCreateUser"; import BulkUsers from "./pages/admin/BulkCreateUser";
import Users from "./pages/admin/Users"; import Users from "./pages/admin/Users";
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
function App() { function App() {
const [showNotif, setShowNotif] = useState(false);
return ( return (
<BrowserRouter> <BrowserRouter>
<ToastContainer />
<Navigation> <Navigation>
<Routes> <Routes>
<Route path="/" element={<Dashboard />} /> <Route path="/" element={<Dashboard />} />
@ -31,18 +32,7 @@ function App() {
<Route path="/settings" element={<CreateTp />} /> <Route path="/settings" element={<CreateTp />} />
</Routes> </Routes>
</Navigation> </Navigation>
<ToastContainer /> <ToastContainer />
{showNotif && (
<div className="toast toast-top toast-end">
<div className="alert alert-success">
<span>Message sent successfully.</span>
<button className="btn btn-sm" onClick={() => setShowNotif(false)}>
close
</button>
</div>
</div>
)}
</BrowserRouter> </BrowserRouter>
); );
} }

View File

@ -10,6 +10,7 @@ import {
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import axios from "axios"; import axios from "axios";
import { ToastContainer } from "react-toastify";
interface NavigationProps { interface NavigationProps {
children: React.ReactElement; children: React.ReactElement;
@ -20,9 +21,7 @@ const Navigation: React.FC<NavigationProps> = ({
}: NavigationProps) => { }: NavigationProps) => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const toggleDrawer = () => setIsDrawerOpen(!isDrawerOpen); const toggleDrawer = () => setIsDrawerOpen(!isDrawerOpen);
const [user, setUser] = useState(null);
const navigate = useNavigate(); const navigate = useNavigate();
const [theme, setTheme] = useState( const [theme, setTheme] = useState(
localStorage.getItem("theme") ? localStorage.getItem("theme") : "dark", localStorage.getItem("theme") ? localStorage.getItem("theme") : "dark",
); );
@ -47,7 +46,6 @@ const Navigation: React.FC<NavigationProps> = ({
if (res.data.username.trim() === "") { if (res.data.username.trim() === "") {
navigate("/login"); navigate("/login");
} }
setUser(res.data);
}); });
localStorage.setItem("theme", theme); localStorage.setItem("theme", theme);
const localTheme = localStorage.getItem("theme"); const localTheme = localStorage.getItem("theme");
@ -63,6 +61,7 @@ const Navigation: React.FC<NavigationProps> = ({
type="checkbox" type="checkbox"
className="drawer-toggle" className="drawer-toggle"
checked={isDrawerOpen} checked={isDrawerOpen}
onChange={() => {}}
/> />
<div className="drawer-content h-full"> <div className="drawer-content h-full">
{location.pathname !== "/login" && ( {location.pathname !== "/login" && (
@ -70,9 +69,8 @@ const Navigation: React.FC<NavigationProps> = ({
<div className="btn btn-ghost" onClick={toggleDrawer}> <div className="btn btn-ghost" onClick={toggleDrawer}>
<Bars3Icon className="size-6" /> <Bars3Icon className="size-6" />
</div> </div>
<label className="swap swap-rotate"> <label className="swap swap-rotate btn btn-ghost">
<input type="checkbox" onChange={handleToggle} /> <input type="checkbox" onChange={handleToggle} />
<SunIcon className="size-6 swap-off" /> <SunIcon className="size-6 swap-off" />
<MoonIcon className="size-6 swap-on" /> <MoonIcon className="size-6 swap-on" />
</label> </label>

View File

@ -1,52 +1,105 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Card from "../component/Card/Card"; import Card from "../component/Card/Card";
import axios from "axios";
import { Link } from "react-router-dom";
const Dashboard: React.FC = () => { function Dashboard() {
const [showNotif, setShowNotif] = useState(false); const [dashboard, setDashboard] = useState(null);
useEffect(() => { useEffect(() => {
fetch("http://localhost:8080/api/users").then((response) => { axios.get("/api/dashboard").then((res) => {
if (!response.ok) { setDashboard(res.data);
throw new Error("Network response was not ok");
}
return response.json();
}); });
}, []); }, []);
const handleButtonClick = () => {
setShowNotif(true);
setTimeout(() => {
setShowNotif(false);
}, 3000);
};
return ( return (
<> <>
<div className="flex flex-col items-center p-4 gap-5"> <section className="bg-blue-500 text-white py-16">
<h1 className="text-4xl font-bold mb-8" onClick={handleButtonClick}> <div className="container mx-auto text-center">
Welcome ! <h2 className="text-3xl font-bold">Welcome to La Banquise</h2>
</h1> <p className="mt-4 text-lg">
You will find here all of your instances and practicals
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 w-[85%] h-full"> </p>
<Card title="TPs" cards={["Bot Discord", "Websocket Chat"]} />
<Card title="Instances" cards={["VPS discord"]} />
<Card
title="Messages"
cards={["Incident BDE raclette", "Confirmation commande"]}
/>
</div>
</div>
{showNotif && (
<div className="toast toast-top toast-end">
<div className="alert alert-success">
<span>Message sent successfully.</span>
<button className="btn btn-sm" onClick={() => setShowNotif(false)}>
close
</button>
</div>
</div> </div>
</section>
{dashboard && (
<>
<section className="py-16">
<div className="container mx-auto">
<h3 className="text-2xl font-bold text-center mb-6">
Practicals
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{dashboard.tps.map((tp) => (
<div
key={tp.id}
className="card card-compact bg-base-200 shadow-lg p-4"
>
<div className="card-body">
<h2 className="card-title">{tp.name}</h2>
<p>{tp.description}</p>
<div className="card-actions justify-end">
<Link to={`/tps/${tp.id}`} className="btn btn-primary">
Learn More
</Link>
</div>
</div>
</div>
))}
</div>
{dashboard.tps.length === 0 && (
<h1 className="text-xl">You have no tps</h1>
)}
</div>
</section>
<section className="py-8">
<div className="container mx-auto">
<h3 className="text-2xl font-bold text-center mb-6">Instances</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{dashboard.instances.map((instance) => (
<div
key={instance.id}
className="card card-compact bg-base-200 shadow-lg p-4"
>
<div className="card-body">
<h2 className="card-title">{instance.name}</h2>
{instance.tp && (
<p className="text-lg">Linked to: {instance.tp.name}</p>
)}
<div className="card-actions justify-end">
<Link
to={`/instances/${instance.id}`}
className="btn btn-primary"
>
Learn More
</Link>
{instance.tp && (
<Link
to={`/tps/${instance.tp.id}`}
className="btn btn-primary"
>
See TP
</Link>
)}
</div>
</div>
</div>
))}
{dashboard.instances.length === 0 && (
<h1 className="text-xl">You have no instances</h1>
)}
</div>
</div>
</section>
<section className="py-8">
<div className="container mx-auto text-center">
<h4 className="text-2xl font-semibold">Messages</h4>
<p className="mt-4 text-gray-700">You have no messages</p>
</div>
</section>
</>
)} )}
</> </>
); );
}; }
export default Dashboard; export default Dashboard;

View File

@ -2,88 +2,64 @@ import axios from "axios";
import { useState } from "react"; import { useState } from "react";
function PageTest() { function PageTest() {
const [users, setUsers] = useState([
{
id: 1,
name: "Alice Smith",
email: "alice@example.com",
role: "Admin",
},
{
id: 2,
name: "Bob Johnson",
email: "bob@example.com",
role: "User",
},
{
id: 3,
name: "Charlie Brown",
email: "charlie@example.com",
role: "User",
},
// Add more user data here as needed
]);
const handleEditUser = (userId) => {
// Logic for editing the user (e.g., opening a modal)
alert(`Edit user with ID: ${userId}`);
};
const handleDeleteUser = (userId) => {
// Logic for deleting the user
const confirmDelete = window.confirm(
"Are you sure you want to delete this user?",
);
if (confirmDelete) {
setUsers(users.filter((user) => user.id !== userId));
}
};
return ( return (
<div className="container mx-auto p-6"> <>
<div className="bg-white shadow-lg rounded-lg p-6"> <section className="bg-blue-500 text-white py-16">
<h1 className="text-3xl font-bold text-blue-600 mb-6">Manage Users</h1> <div className="container mx-auto text-center">
<h2 className="text-3xl font-bold">Welcome to La Banquise</h2>
{/* Users Table */} <p className="mt-4 text-lg">
<div className="overflow-x-auto"> You will find here all of your instances and practicals
<table className="table w-full"> </p>
<thead>
<tr>
<th className="p-4">#</th>
<th className="p-4">Name</th>
<th className="p-4">Email</th>
<th className="p-4">Role</th>
<th className="p-4">Actions</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => (
<tr key={user.id} className="hover:bg-gray-100">
<td className="p-4">{index + 1}</td>
<td className="p-4">{user.name}</td>
<td className="p-4">{user.email}</td>
<td className="p-4">{user.role}</td>
<td className="p-4">
<button
className="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-400 mr-2"
onClick={() => handleEditUser(user.id)}
>
Edit
</button>
<button
className="bg-red-500 text-white px-4 py-2 rounded-lg hover:bg-red-400"
onClick={() => handleDeleteUser(user.id)}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div> </div>
</div> </section>
</div> <section className="py-16">
<div className="container mx-auto">
<h3 className="text-2xl font-bold text-center mb-8">Practicals</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div className="card shadow-md p-6 rounded-lg">
<h4 className="text-xl font-bold mb-4">Practical 1</h4>
<p>
This practical teaches the basics of web development using HTML,
CSS, and JavaScript.
</p>
<button className="btn btn-primary mt-4">View Practical</button>
</div>
</div>
</div>
</section>
<section className="py-16 bg-gray-50">
<div className="container mx-auto">
<h3 className="text-2xl font-bold text-center mb-8">Instances</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div className="card bg-white shadow-md p-6 rounded-lg">
<h5 className="text-lg font-bold mb-4">Instance 1</h5>
<p className="text-gray-700">
Linked to Practical 1 - Live hands-on experience for web
development.
</p>
<button className="btn btn-outline btn-secondary mt-4">
Go to Instance
</button>
</div>
<div className="card bg-white shadow-md p-6 rounded-lg">
<h5 className="text-lg font-bold mb-4">Instance 2</h5>
<p className="text-gray-700">
Linked to Practical 2 - Test advanced JavaScript features.
</p>
<button className="btn btn-outline btn-secondary mt-4">
Go to Instance
</button>
</div>
</div>
</div>
</section>
<section className="bg-gray-200 py-8">
<div className="container mx-auto text-center">
<h4 className="text-xl font-semibold">Messages</h4>
<p className="mt-2 text-gray-700">You have no messages</p>
</div>
</section>
</>
); );
} }

View File

@ -10,11 +10,13 @@ function Practical() {
const copyText = (copy: string) => { const copyText = (copy: string) => {
navigator.clipboard.writeText(copy); navigator.clipboard.writeText(copy);
toast("Copied!"); toast.success("Copied!", {
draggable: true,
theme: localStorage.getItem("theme") || "dark",
});
}; };
useEffect(() => { useEffect(() => {
console.log("akljsbcascb");
axios.get(`/api/tps/${id}`).then((res) => { axios.get(`/api/tps/${id}`).then((res) => {
setTp(res.data); setTp(res.data);
}); });
@ -83,7 +85,7 @@ function Practical() {
<span>Password:</span> <span>Password:</span>
<div <div
className="flex gap-2 items-center" className="flex gap-2 items-center"
onClick={() => navigator.clipboard.writeText(tp.pwd)} onClick={() => copyText(tp.pwd)}
> >
<span className="font-normal">{tp.pwd}</span> <span className="font-normal">{tp.pwd}</span>
<div className="h-8 w-8 hover:bg-base-100 align-middle cursor-pointer flex items-center justify-center rounded"> <div className="h-8 w-8 hover:bg-base-100 align-middle cursor-pointer flex items-center justify-center rounded">

View File

@ -23,7 +23,6 @@ function Practicals() {
<div className="card-body"> <div className="card-body">
<h2 className="card-title">{practical.name}</h2> <h2 className="card-title">{practical.name}</h2>
<p>{practical.description}</p> <p>{practical.description}</p>
<p className="text-sm text-gray-500">Respo: {practical.respo}</p>
<div className="card-actions justify-end"> <div className="card-actions justify-end">
<Link to={`/tps/${practical.id}`} className="btn btn-primary"> <Link to={`/tps/${practical.id}`} className="btn btn-primary">
Learn More Learn More