WIP: update: adaptations to use the new backend #1
@ -5,11 +5,15 @@ import Dashboard from "./pages/Dashboard";
|
|||||||
import Immersions from "./pages/Immersions";
|
import Immersions from "./pages/Immersions";
|
||||||
import Instances from "./pages/Instances";
|
import Instances from "./pages/Instances";
|
||||||
import Immersion from "./pages/Immersion";
|
import Immersion from "./pages/Immersion";
|
||||||
|
import Site from "./pages/Site";
|
||||||
import LoginPage from "./pages/Login";
|
import LoginPage from "./pages/Login";
|
||||||
import PageTest from "./pages/PageTest";
|
import PageTest from "./pages/PageTest";
|
||||||
import CreateTp from "./pages/admin/CreateTp";
|
import CreateTp from "./pages/admin/CreateTp";
|
||||||
|
import CreateSite from "./pages/admin/CreateSite";
|
||||||
|
import CreateJi from "./pages/admin/CreateJi";
|
||||||
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 Sites from "./pages/admin/Sites";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
import "react-toastify/dist/ReactToastify.css";
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
import AdminPage from "./pages/admin/AdminPage";
|
import AdminPage from "./pages/admin/AdminPage";
|
||||||
@ -23,13 +27,17 @@ function App() {
|
|||||||
<Route path="/" element={<Dashboard />} />
|
<Route path="/" element={<Dashboard />} />
|
||||||
<Route path="/immersion" element={<Immersions />} />
|
<Route path="/immersion" element={<Immersions />} />
|
||||||
<Route path="/immersion/:id" element={<Immersion />} />
|
<Route path="/immersion/:id" element={<Immersion />} />
|
||||||
|
<Route path="/site/:id" element={<Site />} />
|
||||||
<Route path="/instances" element={<Instances />} />
|
<Route path="/instances" element={<Instances />} />
|
||||||
<Route path="/profile" element={<PageTest />} />
|
<Route path="/profile" element={<PageTest />} />
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="admin" element={<AdminPage />} />
|
<Route path="admin" element={<AdminPage />} />
|
||||||
<Route path="/admin/jdmi" element={<BulkUsers />} />
|
<Route path="/admin/jdmi" element={<BulkUsers />} />
|
||||||
<Route path="/admin/tps" element={<CreateTp />} />
|
<Route path="/admin/tps" element={<CreateTp />} />
|
||||||
|
<Route path="/admin/sites-create" element={<CreateSite />} />
|
||||||
|
<Route path="/admin/ji-create" element={<CreateJi />} />
|
||||||
<Route path="/admin/users" element={<Users />} />
|
<Route path="/admin/users" element={<Users />} />
|
||||||
|
<Route path="/admin/sites" element={<Sites />} />
|
||||||
<Route path="/settings" element={<CreateTp />} />
|
<Route path="/settings" element={<CreateTp />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
|
|||||||
@ -147,6 +147,41 @@ const Navigation: React.FC<NavigationProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
to="/admin/sites-create"
|
||||||
|
onClick={toggleDrawer}
|
||||||
|
className="w-60"
|
||||||
|
>
|
||||||
|
Create Site
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
to="/admin/ji-create"
|
||||||
|
onClick={toggleDrawer}
|
||||||
|
className="w-60"
|
||||||
|
>
|
||||||
|
Create new JDMI
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
<Link
|
||||||
|
to="/admin/sites"
|
||||||
|
onClick={toggleDrawer}
|
||||||
|
className="w-60"
|
||||||
|
>
|
||||||
|
List Sites
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
<div>
|
<div>
|
||||||
<Link
|
<Link
|
||||||
to="/admin/users"
|
to="/admin/users"
|
||||||
|
|||||||
@ -30,26 +30,16 @@ function Immersion() {
|
|||||||
<h1 className="text-3xl font-bold mb-2 ms-10">{ji.name}</h1>
|
<h1 className="text-3xl font-bold mb-2 ms-10">{ji.name}</h1>
|
||||||
<div className="px-6">
|
<div className="px-6">
|
||||||
<div className="flex flex-col lg:flex-row">
|
<div className="flex flex-col lg:flex-row">
|
||||||
<div className="lg:w-2/3 p-4">
|
|
||||||
<div className="w-full h-[80vh] bg-gray-200 rounded-lg overflow-hidden mb-4">
|
|
||||||
<iframe
|
|
||||||
src={ji.pdfLink}
|
|
||||||
className="w-full h-full"
|
|
||||||
title={`${ji.name} PDF`}
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="lg:w-1/3 px-4">
|
<div className="lg:w-1/3 px-4">
|
||||||
<div className="bg-base-200 p-6 rounded-lg shadow-md mt-4">
|
<div className="bg-base-200 p-6 rounded-lg shadow-md mt-4">
|
||||||
<h2 className="text-2xl font-semibold text-blue-600 mb-2">
|
<h2 className="text-2xl font-semibold text-blue-600 mb-2">
|
||||||
Information
|
{ji.name} - Information
|
||||||
</h2>
|
</h2>
|
||||||
<ul className="space-y-4">
|
<ul className="space-y-4">
|
||||||
<li>{ji.description}</li>
|
<li>{ji.description}</li>
|
||||||
<li className="flex justify-between">
|
<li className="flex justify-between">
|
||||||
<span>Duration:</span>
|
<span>Description:</span>
|
||||||
<span className="font-medium">2 hours</span>
|
<span className="font-medium">{ji.description}</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex justify-between">
|
<li className="flex justify-between">
|
||||||
<span>Tools Used:</span>
|
<span>Tools Used:</span>
|
||||||
@ -61,7 +51,7 @@ function Immersion() {
|
|||||||
</li>
|
</li>
|
||||||
<li className="flex justify-between">
|
<li className="flex justify-between">
|
||||||
<span>Date:</span>
|
<span>Date:</span>
|
||||||
<span className="font-medium">Oct 23, 2024</span>
|
<span className="font-medium">{ji.date}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const LoginPage: React.FC = () => {
|
|||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
axios.get("/api/users/me").then((res) => {
|
axios.get("/api/user/me").then((res) => {
|
||||||
localStorage.setItem("username", res.data.username);
|
localStorage.setItem("username", res.data.username);
|
||||||
if (res.data.roles.includes("ROOT")) {
|
if (res.data.roles.includes("ROOT")) {
|
||||||
localStorage.setItem("root", "true");
|
localStorage.setItem("root", "true");
|
||||||
|
|||||||
88
src/pages/Site.tsx
Normal file
88
src/pages/Site.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { ArrowDownTrayIcon, ClipboardIcon } from "@heroicons/react/24/outline";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { Site } from "../type/SiteType";
|
||||||
|
|
||||||
|
function Site() {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [site, setSite] = useState<Site>();
|
||||||
|
|
||||||
|
const copyText = (copy: string) => {
|
||||||
|
navigator.clipboard.writeText(copy);
|
||||||
|
toast.success("Copied!", {
|
||||||
|
draggable: true,
|
||||||
|
theme: localStorage.getItem("theme") || "dark",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios.get(`/api/sites/${id}`).then((res) => {
|
||||||
|
setSite(res.data);
|
||||||
|
});
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{site && (
|
||||||
|
<>
|
||||||
|
<h1 className="text-3xl font-bold mb-2 ms-10">{site.name}</h1>
|
||||||
|
<div className="px-6">
|
||||||
|
<div className="flex flex-col lg:flex-row">
|
||||||
|
<div className="lg:w-2/3 p-4">
|
||||||
|
<div className="w-full h-[80vh] bg-gray-200 rounded-lg overflow-hidden mb-4">
|
||||||
|
{site.listJi}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="lg:w-1/3 px-4">
|
||||||
|
<div className="bg-base-200 p-6 rounded-lg shadow-md mt-4">
|
||||||
|
<h2 className="text-2xl font-semibold text-blue-600 mb-2">
|
||||||
|
{site.name} - Informations
|
||||||
|
</h2>
|
||||||
|
<ul className="space-y-4">
|
||||||
|
<li className="flex justify-between">
|
||||||
|
<span>Description:</span>
|
||||||
|
<span className="font-medium">{site.description}</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex justify-between">
|
||||||
|
<span>Address:</span>
|
||||||
|
<span className="font-medium">{site.address}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div className="bg-base-200 p-6 rounded-lg shadow-md mt-6">
|
||||||
|
<h2 className="text-2xl font-semibold text-blue-600 mb-2">
|
||||||
|
Responsables du site
|
||||||
|
</h2>
|
||||||
|
mettre une liste des respo site
|
||||||
|
<ul className="space-y-2">
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={site.name}
|
||||||
|
target="_blank"
|
||||||
|
className="card w-full bg-base-200 shadow-md hover:shadow-lg transition-shadow rounded-lg mt-6"
|
||||||
|
>
|
||||||
|
<div className="card-body flex-row justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<div className="card-title text-lg font-bold">
|
||||||
|
Subject
|
||||||
|
</div>
|
||||||
|
<p className="text-base-content">Download</p>
|
||||||
|
</div>
|
||||||
|
<ArrowDownTrayIcon className="size-6" />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Site;
|
||||||
122
src/pages/admin/CreateJi.tsx
Normal file
122
src/pages/admin/CreateJi.tsx
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
function CreateJi() {
|
||||||
|
const [ji, setJi] = useState({
|
||||||
|
name: "",
|
||||||
|
respo: "",
|
||||||
|
site_id: "",
|
||||||
|
date: "",
|
||||||
|
});
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleInputChange = (
|
||||||
|
e: React.ChangeEvent<
|
||||||
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setJi({ ...ji, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Créer les query parameters
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
name: ji.name,
|
||||||
|
respo: ji.respo,
|
||||||
|
site_id: ji.site_id,
|
||||||
|
date: ji.date,
|
||||||
|
});
|
||||||
|
|
||||||
|
axios.post(`/api/ji/create?${params.toString()}`).then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
navigate(`/immersion/${res.data.id}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto p-6">
|
||||||
|
<div className="shadow-lg rounded-lg p-6">
|
||||||
|
<h1 className="text-3xl font-bold text-blue-600 mb-6">
|
||||||
|
Create Ji
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Name:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={ji.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter ji name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Respo:
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
type="text"
|
||||||
|
name="respo"
|
||||||
|
value={ji.respo}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter ji description"
|
||||||
|
rows={4}
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Site ID:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="site_id"
|
||||||
|
value={ji.site_id}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter ji's address"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Date:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
name="date"
|
||||||
|
value={ji.date}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-right">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
Create Ji
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateJi;
|
||||||
106
src/pages/admin/CreateSite.tsx
Normal file
106
src/pages/admin/CreateSite.tsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
function CreateSite() {
|
||||||
|
const [site, setSite] = useState({
|
||||||
|
name: "",
|
||||||
|
desc: "",
|
||||||
|
address: "",
|
||||||
|
});
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleInputChange = (
|
||||||
|
e: React.ChangeEvent<
|
||||||
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||||
|
>,
|
||||||
|
) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setSite({ ...site, [name]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Créer les query parameters
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
name: site.name,
|
||||||
|
description: site.description,
|
||||||
|
address: site.address,
|
||||||
|
});
|
||||||
|
|
||||||
|
axios.post(`/api/sites?${params.toString()}`).then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
navigate(`/site/${res.data.id}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto p-6">
|
||||||
|
<div className="shadow-lg rounded-lg p-6">
|
||||||
|
<h1 className="text-3xl font-bold text-blue-600 mb-6">
|
||||||
|
Create Site
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Name:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={site.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter site name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Description:
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
type="text"
|
||||||
|
name="desc"
|
||||||
|
value={site.desc}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter site description"
|
||||||
|
rows={4}
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-gray-700 font-semibold mb-2">
|
||||||
|
Address:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="address"
|
||||||
|
value={site.address}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter site's address"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-right">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-blue-600 text-white px-6 py-2 rounded-lg hover:bg-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
>
|
||||||
|
Create Site
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateSite;
|
||||||
75
src/pages/admin/ListJi.tsx
Normal file
75
src/pages/admin/ListJi.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Ji } from "../../type/JiType";
|
||||||
|
|
||||||
|
function ListJI() {
|
||||||
|
const [jis, setJis] = useState([]);
|
||||||
|
const [reload, setReload] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios.get("/api/ji/listall").then((res) => {
|
||||||
|
setJis(res.data);
|
||||||
|
});
|
||||||
|
}, [reload]);
|
||||||
|
|
||||||
|
const handleEditJi = (jiId: number) => {
|
||||||
|
alert(`Edit ji with ID: ${siteId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*const deleteJis = () => {
|
||||||
|
axios.delete("/api/jis").then(() => {
|
||||||
|
setReload(reload + 1);
|
||||||
|
});
|
||||||
|
};*/
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto p-6">
|
||||||
|
<div className="bg-white shadow-lg rounded-lg p-6">
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<h1 className="text-3xl font-bold text-blue-600 mb-6">
|
||||||
|
Manage Jis
|
||||||
|
</h1>
|
||||||
|
{/*<button className="btn btn-error" onClick={deleteJDMIUsers}>
|
||||||
|
Delete JDMI jis
|
||||||
|
</button>*/}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 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">Address</th>
|
||||||
|
<th className="p-4">Roles</th>
|
||||||
|
<th className="p-4">Instances</th>
|
||||||
|
<th className="p-4">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{jis.map((site: Ji, index: number) => (
|
||||||
|
<tr key={ji.id} className="hover:bg-gray-100">
|
||||||
|
<td className="p-4">{index + 1}</td>
|
||||||
|
<td className="p-4">{ji.name}</td>
|
||||||
|
<td className="p-4">{ji.address}</td>
|
||||||
|
<td className="p-4">{ji.listJi}</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={() => handleEditJi(ji.id)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Jis;
|
||||||
75
src/pages/admin/Sites.tsx
Normal file
75
src/pages/admin/Sites.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Site } from "../../type/SiteType";
|
||||||
|
|
||||||
|
function Sites() {
|
||||||
|
const [sites, setSites] = useState([]);
|
||||||
|
const [reload, setReload] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios.get("/api/sites").then((res) => {
|
||||||
|
setSites(res.data);
|
||||||
|
});
|
||||||
|
}, [reload]);
|
||||||
|
|
||||||
|
const handleEditSite = (siteId: number) => {
|
||||||
|
alert(`Edit site with ID: ${siteId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*const deleteSites = () => {
|
||||||
|
axios.delete("/api/sites").then(() => {
|
||||||
|
setReload(reload + 1);
|
||||||
|
});
|
||||||
|
};*/
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto p-6">
|
||||||
|
<div className="bg-white shadow-lg rounded-lg p-6">
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<h1 className="text-3xl font-bold text-blue-600 mb-6">
|
||||||
|
Manage Sites
|
||||||
|
</h1>
|
||||||
|
{/*<button className="btn btn-error" onClick={deleteJDMIUsers}>
|
||||||
|
Delete JDMI sites
|
||||||
|
</button>*/}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 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">Address</th>
|
||||||
|
<th className="p-4">Roles</th>
|
||||||
|
<th className="p-4">Instances</th>
|
||||||
|
<th className="p-4">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{sites.map((site: Site, index: number) => (
|
||||||
|
<tr key={site.id} className="hover:bg-gray-100">
|
||||||
|
<td className="p-4">{index + 1}</td>
|
||||||
|
<td className="p-4">{site.name}</td>
|
||||||
|
<td className="p-4">{site.address}</td>
|
||||||
|
<td className="p-4">{site.listJi}</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={() => handleEditSite(site.id)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sites;
|
||||||
@ -2,11 +2,12 @@ import { Sujet } from "./SujetType";
|
|||||||
import { User } from "./UserType";
|
import { User } from "./UserType";
|
||||||
|
|
||||||
export interface Ji {
|
export interface Ji {
|
||||||
|
id: number,
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
respos: User[];
|
|
||||||
date: string;
|
date: string;
|
||||||
site: Site;
|
site: Site;
|
||||||
participants: User[];
|
respos: User[];
|
||||||
|
participants: User[]
|
||||||
//instances: Instances[];
|
//instances: Instances[];
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/type/SiteType.ts
Normal file
9
src/type/SiteType.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Ji } from "./JiType";
|
||||||
|
|
||||||
|
export interface Site {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
address: string;
|
||||||
|
listJi: List<Ji>;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user