Update UI page manage value

This commit is contained in:
Joseph Le 2026-02-27 12:54:34 +11:00
parent d5454ac29d
commit c73cf2d40e
3 changed files with 302 additions and 191 deletions

View File

@ -98,8 +98,11 @@ const ListLog = () => {
placeholder="Search file..." placeholder="Search file..."
value={nameSearch} value={nameSearch}
onChange={(e) => setNameSearch(e.target.value)} onChange={(e) => setNameSearch(e.target.value)}
onKeyDown={(key) => {
if (key.keyCode === 13 || key.key === "Enter") getListFile(1)
}}
/> />
<button className="btn success" onClick={() => getListFile(page)}> <button className="btn success" onClick={() => getListFile(1)}>
Search Search
</button> </button>
</div> </div>

View File

@ -0,0 +1,147 @@
body {
background: #f4f6f9;
font-family: "Segoe UI", sans-serif;
}
.manageContainer {
display: flex;
justify-content: center;
padding: 30px;
}
.card {
width: 90%;
max-width: 1200px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
padding: 25px;
}
.cardHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.tableWrapper {
height: 70vh;
overflow-y: auto;
border: 1px solid #d8d8d8;
border-radius: 8px;
}
.modernTable {
width: 100%;
border-collapse: collapse;
}
.modernTable thead th {
position: sticky;
top: 0;
background: #f9fafb;
padding: 12px;
text-align: left;
border-bottom: 1px solid #d8d8d8;
}
.modernTable tbody td {
padding: 10px 12px;
border-bottom: 1px solid #d8d8d8;
}
.modernTable tbody tr:hover {
background: #f9fafb;
}
.keyCell {
font-weight: 600;
color: #1f2937;
}
.input {
width: 100%;
padding: 6px 10px;
border-radius: 6px;
border: 1px solid #d1d5db;
outline: none;
}
.input:focus {
border-color: #2563eb;
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
}
.actionGroup {
display: flex;
gap: 8px;
}
.btn {
padding: 6px 12px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: 0.2s;
width: 70px;
}
.btn.primary {
background: #2563eb;
color: white;
}
.btn.success {
background: #16a34a;
color: white;
}
.btn.warning {
background: #f59e0b;
color: white;
}
.btn.danger {
background: #dc2626;
color: white;
}
.btn:hover {
opacity: 0.9;
}
.addSection {
margin-top: 20px;
display: flex;
gap: 10px;
justify-content: center;
}
.isDisabled {
pointer-events: none;
opacity: 0.7;
-moz-user-focus: none;
-webkit-user-focus: none;
-ms-user-focus: none;
-moz-user-modify: read-only;
-webkit-user-modify: read-only;
-ms-user-modify: read-only;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: not-allowed;
}
.btnLogs {
padding: 6px 12px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: 0.2s;
background: #2563eb;
color: white;
}

View File

@ -1,213 +1,174 @@
import axios from "axios"; import axios from "axios";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { addKeyValue, deleteValue, editValue, getKeyValues } from "../api/apiLog"; import {
addKeyValue,
deleteValue,
editValue,
getKeyValues,
} from "../api/apiLog";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import "./ManageValues.css";
const ManageValues = () => { const ManageValues = () => {
const [keyValue, setKeyValue] = useState([]); const [keyValue, setKeyValue] = useState([]);
const [key, setKey] = useState([]); const [key, setKey] = useState([]);
const [addValue, setAddValue] = useState({ key: "CATCH_FAULTY", value: "" }); const [addValue, setAddValue] = useState({
const [inputSelect, setInpuSelect] = useState(0) key: "CATCH_FAULTY",
const [inputValue, setInputValue] = useState("") value: "",
});
const [editingId, setEditingId] = useState(null);
const [inputValue, setInputValue] = useState("");
const [isDisabled, setIsDisabled] = useState(false);
const getValues = async () => { const getValues = async () => {
try {
const res = await axios.post(getKeyValues); const res = await axios.post(getKeyValues);
setKeyValue(res.data.sort((a, b) => a.key.localeCompare(b.key))); setKeyValue(res.data.sort((a, b) => a.key.localeCompare(b.key)));
setKey(res.data?.map((obj) => obj.key)); setKey(res.data?.map((obj) => obj.key));
} catch (error) {
console.log(error);
}
}; };
useEffect(() => { useEffect(() => {
getValues(); getValues();
}, []); }, []);
console.log(inputValue)
return ( return (
<div <div className="manageContainer">
style={{ <div className="card">
display: "flex", <div className="tableWrapper">
alignItems: "center", <table className="modernTable">
flexFlow: "column", <thead>
}}
>
<div style={{ height: "80vh", overflow: "auto" }}>
<table>
<thead style={{ width: "100%" }}>
<tr> <tr>
<th <th>Key</th>
style={{ position: "sticky", top: 0, backgroundColor: "white" }} <th>Value</th>
> <th style={{ width: "200px" }}>Actions</th>
Key
</th>
<th
style={{
position: "sticky",
top: 0,
backgroundColor: "white",
width: "400px",
}}
>
Value
</th>
<th
style={{ position: "sticky", top: 0, backgroundColor: "white" }}
>
Option
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{keyValue?.map((value) => ( {keyValue.map((item) => (
<tr <tr style={{ backgroundColor: item.key === "MODEL_SPECIAL" ? "rgb(219 219 255 / 47%)" : item.key === "CATCH_FAULTY" ? "rgb(166 255 165 / 43%)" : item.key === "MEMORY_DEFAULT" ? "rgb(253 244 52 / 41%)" : "rgb(253 189 152 / 41%)", }} key={item.id_key}>
style={{ <td className="keyCell">{item.key}</td>
backgroundColor:
value.key === "MODEL_SPECIAL"
? "rgb(219 219 255 / 47%)"
: value.key === "CATCH_FAULTY"
? "rgb(166 255 165 / 43%)"
: value.key === "MEMORY_DEFAULT"
? "rgb(253 244 52 / 41%)"
: "rgb(253 189 152 / 41%)",
}}
>
<td>{value.key}</td>
<td> <td>
<input <input
value={inputSelect===value.id_key?inputValue:value.value} className="input"
disabled={inputSelect===value.id_key?false:true} value={
style={{ width: "100%" }} editingId === item.id_key
onChange={(e)=>{ ? inputValue
: item.value
}
disabled={editingId !== item.id_key}
onChange={(e) =>
setInputValue(e.target.value) setInputValue(e.target.value)
}
}} />
></input>
</td> </td>
<td> <td>
<div className="actionGroup">
{editingId !== item.id_key ? (
<>
<button <button
style={{ className={`btn warning ${isDisabled ? "isDisabled" : ""}`}
cursor: "pointer", onClick={() => {
backgroundColor: "red", setEditingId(item.id_key);
color: "white", setInputValue(item.value);
}}
onClick={async () => {
const res = await axios.post(deleteValue, {
id: value.id_key,
});
if (res.status !== 200) {
alert(res.data);
} else {
getValues();
}
}}
>
Delete
</button>
<button
style={{
cursor: "pointer",
backgroundColor: "yellow",
color: "black",
display:inputSelect===value.id_key?"none":"inline"
}}
onClick={async () => {
setInpuSelect(value.id_key)
setInputValue(value.value)
}} }}
> >
Edit Edit
</button> </button>
<button <button
style={{ className={`btn danger ${isDisabled ? "isDisabled" : ""}`}
cursor: "pointer",
backgroundColor: "green",
color: "white",
display:inputSelect===value.id_key?"inline":"none"
}}
onClick={async () => { onClick={async () => {
setInpuSelect(0) setIsDisabled(true)
const res = await axios.post(editValue, { await axios.post(deleteValue, {
id: value.id_key, id: item.id_key,
value: inputValue
}); });
if (res.status !== 200) { setIsDisabled(false)
alert(res.data);
} else {
getValues(); getValues();
}
}} }}
> >
Done Delete
</button> </button>
</>
) : (
<>
<button
className={`btn success ${isDisabled ? "isDisabled" : ""}`}
onClick={async () => {
setEditingId(null);
setIsDisabled(true)
await axios.post(editValue, {
id: item.id_key,
value: inputValue,
});
setIsDisabled(false)
getValues();
}}
>
Save
</button>
<button
className="btn"
onClick={() => {
setEditingId(null);
}}
>
Cancel
</button>
</>
)}
</div>
</td> </td>
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
</div> </div>
<div style={{ width: "50%", display: "flex", margin: 10 }}>
{/* Add new value */}
<div className="addSection">
<select <select
style={{ width: "35%" }} className="input"
onChange={(e) => { onChange={(e) =>
setAddValue({ ...addValue, key: e.target.value }); setAddValue({ ...addValue, key: e.target.value })
}} }
> >
{key {[...new Set(key)].map((k) => (
.filter((value, index, self) => { <option key={k}>{k}</option>
return self.indexOf(value) === index;
})
?.map((u) => (
<option value={u}>{u}</option>
))} ))}
</select> </select>
<input <input
placeholder="value" value={addValue.value || ""}
style={{ margin: "0 5px", width: "50%" }} className="input"
onChange={(e) => { placeholder="Enter value..."
setAddValue({ ...addValue, value: e.target.value }); onChange={(e) =>
}} setAddValue({ ...addValue, value: e.target.value })
></input> }
/>
<button <button
style={{ className={`btn success ${isDisabled ? "isDisabled" : ""}`}
cursor: "pointer",
backgroundColor: "green",
color: "white",
}}
onClick={async () => { onClick={async () => {
if (addValue.key !== "" && addValue.value !== "") { if (!addValue.value) return alert("Value is empty");
const res = await axios.post(addKeyValue, { setAddValue({ ...addValue, value: "" });
key: addValue.key, setIsDisabled(true)
value: addValue.value, await axios.post(addKeyValue, addValue);
}); setIsDisabled(false)
if (res.status !== 200) {
alert(res.data);
} else {
getValues(); getValues();
}
} else {
alert("Value is empty!");
}
}} }}
> >
Add Add
</button> </button>
</div> </div>
<div>
<Link to={"/logs"}> <div className="addSection">
{" "} <Link to="/logs">
<button <button className="btnLogs">List of detected files</button>
style={{
color: "white",
backgroundColor: "blue",
cursor: "pointer",
}}
>
List of detected files
</button>
</Link> </Link>
</div> </div>
</div> </div>
</div>
); );
}; };