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