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 Users from "./pages/admin/Users";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
function App() {
const [showNotif, setShowNotif] = useState(false);
return (
<BrowserRouter>
<ToastContainer />
<Navigation>
<Routes>
<Route path="/" element={<Dashboard />} />
@ -31,18 +32,7 @@ function App() {
<Route path="/settings" element={<CreateTp />} />
</Routes>
</Navigation>
<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>
);
}

View File

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

View File

@ -1,52 +1,105 @@
import { useEffect, useState } from "react";
import Card from "../component/Card/Card";
import axios from "axios";
import { Link } from "react-router-dom";
const Dashboard: React.FC = () => {
const [showNotif, setShowNotif] = useState(false);
function Dashboard() {
const [dashboard, setDashboard] = useState(null);
useEffect(() => {
fetch("http://localhost:8080/api/users").then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
axios.get("/api/dashboard").then((res) => {
setDashboard(res.data);
});
}, []);
const handleButtonClick = () => {
setShowNotif(true);
setTimeout(() => {
setShowNotif(false);
}, 3000);
};
return (
<>
<div className="flex flex-col items-center p-4 gap-5">
<h1 className="text-4xl font-bold mb-8" onClick={handleButtonClick}>
Welcome !
</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-10 w-[85%] h-full">
<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>
<section className="bg-blue-500 text-white py-16">
<div className="container mx-auto text-center">
<h2 className="text-3xl font-bold">Welcome to La Banquise</h2>
<p className="mt-4 text-lg">
You will find here all of your instances and practicals
</p>
</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;

View File

@ -2,88 +2,64 @@ import axios from "axios";
import { useState } from "react";
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 (
<div className="container mx-auto p-6">
<div className="bg-white shadow-lg rounded-lg p-6">
<h1 className="text-3xl font-bold text-blue-600 mb-6">Manage Users</h1>
{/* Users Table */}
<div className="overflow-x-auto">
<table className="table w-full">
<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>
<>
<section className="bg-blue-500 text-white py-16">
<div className="container mx-auto text-center">
<h2 className="text-3xl font-bold">Welcome to La Banquise</h2>
<p className="mt-4 text-lg">
You will find here all of your instances and practicals
</p>
</div>
</div>
</div>
</section>
<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) => {
navigator.clipboard.writeText(copy);
toast("Copied!");
toast.success("Copied!", {
draggable: true,
theme: localStorage.getItem("theme") || "dark",
});
};
useEffect(() => {
console.log("akljsbcascb");
axios.get(`/api/tps/${id}`).then((res) => {
setTp(res.data);
});
@ -83,7 +85,7 @@ function Practical() {
<span>Password:</span>
<div
className="flex gap-2 items-center"
onClick={() => navigator.clipboard.writeText(tp.pwd)}
onClick={() => copyText(tp.pwd)}
>
<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">

View File

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