📦 chore(frontend): add moment package as a dependency to handle date and time formatting

🐛 fix(headerComponent): add onClick event handler to navigate to "/account/api-keys" when button is clicked

🐛 fix(API): add missing API functions for getting, creating, and deleting API keys

🐛 fix(SecretKeyModal): add functionality to create a new API key and set the value in the input field

🔧 fix(ApiKeysPage): remove unused imports and refactor code to improve readability and performance
 feat(ApiKeysPage): add support for fetching and displaying API keys for the logged-in user
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
🔧 fix(ApiKeysPage): refactor code to improve readability and remove unnecessary functions and variables
This commit is contained in:
Cristhian Zanforlin Lousa 2023-08-16 18:41:40 -03:00
commit ad8cbddd91
7 changed files with 120 additions and 175 deletions

View file

@ -45,6 +45,7 @@
"esbuild": "^0.17.18",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-cookie": "^4.1.1",
@ -7726,6 +7727,14 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",

View file

@ -40,6 +40,7 @@
"esbuild": "^0.17.18",
"lodash": "^4.17.21",
"lucide-react": "^0.233.0",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-cookie": "^4.1.1",

View file

@ -146,7 +146,11 @@ export default function Header() {
/>
</div>
</AlertDropdown>
<button>
<button
onClick={() => {
navigate("/account/api-keys");
}}
>
<IconComponent
name="Key"
className="side-bar-button-size text-muted-foreground hover:text-accent-foreground"

View file

@ -467,3 +467,41 @@ export async function updateUser(user_id: string, user: Users) {
throw error;
}
}
export async function getApiKey(user_id: String) {
try {
const res = await api.get(`${BASE_URL_API}api_key/${user_id}`);
if (res.status === 200) {
return res.data;
}
} catch (error) {
console.log("Error:", error);
throw error;
}
}
export async function createApiKey(user_id: string) {
try {
const res = await api.post(`${BASE_URL_API}api_key/${user_id}`);
if (res.status === 200) {
return res.data;
}
} catch (error) {
console.log("Error:", error);
throw error;
}
}
export async function deleteApiKey(user_id: string) {
try {
const res = await api.delete(`${BASE_URL_API}api_key/${user_id}`);
if (res.status === 200) {
return res.data;
}
} catch (error) {
console.log("Error:", error);
throw error;
}
}

View file

@ -5,6 +5,7 @@ import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { CONTROL_NEW_API_KEY } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { createApiKey } from "../../controllers/API";
import {
ApiKeyInputType,
ApiKeyType,
@ -20,13 +21,11 @@ export default function SecretKeyModal({
children,
icon,
data,
index,
onConfirm,
}: ApiKeyType) {
const Icon: any = nodeIconsLucide[icon];
const [open, setOpen] = useState(false);
const [apiKeyName, setApiKeyName] = useState(data?.apikeyname ?? "");
const [apiKeyValue, setApiKeyValue] = useState("Value");
const [apiKeyValue, setApiKeyValue] = useState("");
const [inputState, setInputState] =
useState<ApiKeyInputType>(CONTROL_NEW_API_KEY);
const [renderKey, setRenderKey] = useState(false);
@ -49,7 +48,7 @@ export default function SecretKeyModal({
function resetForm() {
setApiKeyName("");
setApiKeyValue("Value");
setApiKeyValue("");
}
const handleCopyClick = async () => {
@ -68,6 +67,14 @@ export default function SecretKeyModal({
}
};
function handleAddNewKey() {
createApiKey(data)
.then((res) => {
setApiKeyValue(res["api_key"]);
})
.catch((err) => {});
}
return (
<BaseModal size="small-h-full" open={open} setOpen={setOpen}>
<BaseModal.Trigger>{children}</BaseModal.Trigger>
@ -122,6 +129,7 @@ export default function SecretKeyModal({
<Form.Root
onSubmit={(event) => {
setRenderKey(true);
handleAddNewKey();
event.preventDefault();
}}
>

View file

@ -1,4 +1,3 @@
import { cloneDeep } from "lodash";
import { useContext, useEffect, useRef, useState } from "react";
import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent";
@ -13,165 +12,55 @@ import {
} from "../../components/ui/table";
import { alertContext } from "../../contexts/alertContext";
import { AuthContext } from "../../contexts/authContext";
import {
addUser,
deleteUser,
getUsersPage,
updateUser,
} from "../../controllers/API";
import { deleteApiKey, getApiKey } from "../../controllers/API";
import ConfirmationModal from "../../modals/ConfirmationModal";
import SecretKeyModal from "../../modals/SecretKeyModal";
import { UserInputType } from "../../types/components";
import moment from "moment";
export default function ApiKeysPage() {
const [inputValue, setInputValue] = useState("");
const [size, setPageSize] = useState(10);
const [index, setPageIndex] = useState(0);
const [loadingUsers, setLoadingUsers] = useState(true);
const [loadingKeys, setLoadingKeys] = useState(true);
const { setErrorData, setSuccessData } = useContext(alertContext);
const { userData } = useContext(AuthContext);
const [totalRowsCount, setTotalRowsCount] = useState(0);
const userList = useRef([]);
const [userId, setUserId] = useState("");
const keysList = useRef([]);
useEffect(() => {
setTimeout(() => {
getUsers();
getKeys();
}, 500);
}, []);
}, [userData]);
const [filterUserList, setFilterUserList] = useState(userList.current);
function getUsers() {
setLoadingUsers(true);
getUsersPage(index, size)
.then((users) => {
setTotalRowsCount(users["total_count"]);
userList.current = users["users"];
setFilterUserList(users["users"]);
setLoadingUsers(false);
})
.catch((error) => {
setLoadingUsers(false);
});
}
function handleChangePagination(pageIndex: number, pageSize: number) {
setLoadingUsers(true);
getUsersPage(pageIndex, pageSize)
.then((users) => {
setTotalRowsCount(users["total_count"]);
userList.current = users["users"];
setFilterUserList(users["users"]);
setLoadingUsers(false);
})
.catch((error) => {
setLoadingUsers(false);
});
}
function resetFilter() {
setPageIndex(0);
setPageSize(10);
getUsers();
}
function handleFilterUsers(input: string) {
setInputValue(input);
if (input === "") {
setFilterUserList(userList.current);
} else {
const filteredList = userList.current.filter((user) =>
user.username.toLowerCase().includes(input.toLowerCase())
);
setFilterUserList(filteredList);
function getKeys() {
setLoadingKeys(true);
if (userData) {
getApiKey(userData.id)
.then((keys) => {
keysList.current = keys["api_keys"];
setUserId(keys["user_id"]);
setLoadingKeys(false);
})
.catch((error) => {
setLoadingKeys(false);
});
}
}
function handleDeleteUser(user) {
deleteUser(user.id)
.then((res) => {
resetFilter();
setSuccessData({
title: "Success! User deleted!",
});
})
.catch((error) => {
setErrorData({
title: "Error on delete user",
list: [error["response"]["data"]["detail"]],
});
});
function resetFilter() {
getKeys();
}
function handleEditUser(userId, user) {
updateUser(userId, user)
function handleDeleteKey(keys) {
deleteApiKey(keys)
.then((res) => {
resetFilter();
setSuccessData({
title: "Success! User edited!",
title: "Success! Key deleted!",
});
})
.catch((error) => {
setErrorData({
title: "Error on edit user",
list: [error["response"]["data"]["detail"]],
});
});
}
function handleDisableUser(check, userId, user) {
const userEdit = cloneDeep(user);
userEdit.is_active = !check;
updateUser(userId, userEdit)
.then((res) => {
console.log(res);
resetFilter();
setSuccessData({
title: "Success! User edited!",
});
})
.catch((error) => {
setErrorData({
title: "Error on edit user",
list: [error["response"]["data"]["detail"]],
});
});
}
function handleSuperUserEdit(check, userId, user) {
const userEdit = cloneDeep(user);
userEdit.is_superuser = !check;
updateUser(userId, userEdit)
.then((res) => {
resetFilter();
setSuccessData({
title: "Success! User edited!",
});
})
.catch((error) => {
setErrorData({
title: "Error on edit user",
list: [error["response"]["data"]["detail"]],
});
});
}
function handleNewUser(user: UserInputType) {
addUser(user)
.then((res) => {
resetFilter();
setSuccessData({
title: "Success! New user added!",
});
})
.catch((error) => {
setErrorData({
title: "Error on add new user",
title: "Error on delete key",
list: [error["response"]["data"]["detail"]],
});
});
@ -188,6 +77,12 @@ export default function ApiKeysPage() {
);
}
function getIdKeyHidden(apiKey: string) {
const firstTwoChars = apiKey.slice(0, 2);
const lastFourChars = apiKey.slice(-4);
return firstTwoChars + "..." + lastFourChars;
}
return (
<>
{userData && (
@ -211,7 +106,7 @@ export default function ApiKeysPage() {
<div className="flex items-center space-x-2"></div>
</div>
{userList.current.length === 0 && !loadingUsers && (
{keysList.current.length === 0 && !loadingKeys && (
<>
<div className="flex items-center justify-between">
<h2>There's no users registered :)</h2>
@ -219,21 +114,21 @@ export default function ApiKeysPage() {
</>
)}
<>
{loadingUsers && (
{loadingKeys && (
<div>
<strong>Loading...</strong>
</div>
)}
<div
className={
"max-h-[15rem] min-h-[15rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
(loadingUsers ? " border-0" : "")
"max-h-[15rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
(loadingKeys ? " border-0" : "")
}
>
<Table className={"table-fixed bg-muted outline-1"}>
<TableHeader
className={
loadingUsers
loadingKeys
? "hidden"
: "table-fixed bg-muted outline-1"
}
@ -256,37 +151,31 @@ export default function ApiKeysPage() {
<TableHead className="h-10 w-[100px] text-right"></TableHead>
</TableRow>
</TableHeader>
{!loadingUsers && (
{!loadingKeys && (
<TableBody>
{filterUserList.map((user, index) => (
{keysList.current.map((api_keys, index) => (
<TableRow key={index}>
<TableCell className="truncate py-2">
<ShadTooltip content={user.id}>
<ShadTooltip content={api_keys.name}>
<span className="cursor-default">
{user.id}
{api_keys.name}
</span>
</ShadTooltip>
</TableCell>
<TableCell className="truncate py-2">
<ShadTooltip content={user.username}>
<span className="cursor-default">
{user.username}
</span>
</ShadTooltip>
<span className="cursor-default">
{getIdKeyHidden(api_keys.id)}
</span>
</TableCell>
<TableCell className="truncate py-2 ">
{
new Date(user.create_at)
.toISOString()
.split("T")[0]
}
{moment(api_keys.create_at).format(
"YYYY-MM-DD HH:mm"
)}
</TableCell>
<TableCell className="truncate py-2">
{
new Date(user.updated_at)
.toISOString()
.split("T")[0]
}
{moment(api_keys.last_used_at).format(
"YYYY-MM-DD HH:mm"
)}
</TableCell>
<TableCell className="flex w-[100px] py-2 text-right">
<div className="flex">
@ -294,14 +183,14 @@ export default function ApiKeysPage() {
title="Delete"
titleHeader="Delete User"
modalContentTitle="Attention!"
modalContent="Are you sure you want to delete this user? This action cannot be undone."
modalContent="Are you sure you want to delete this key? This action cannot be undone."
cancelText="Cancel"
confirmationText="Delete"
icon={"UserMinus2"}
data={user}
data={api_keys.id}
index={index}
onConfirm={(index, user) => {
handleDeleteUser(user);
onConfirm={(index, keys) => {
handleDeleteKey(keys);
}}
>
<ShadTooltip content="Delete" side="top">
@ -327,9 +216,7 @@ export default function ApiKeysPage() {
cancelText="Cancel"
confirmationText="Create secret key"
icon={"Key"}
onConfirm={(index, user) => {
handleNewUser(user);
}}
data={userId}
>
<Button>
<IconComponent name="Plus" className="mr-1 h-5 w-5" />

View file

@ -241,8 +241,6 @@ export type ApiKeyType = {
children: ReactElement;
icon: string;
data?: any;
index?: number;
onConfirm: (index, data) => void;
};
export type ApiKeyInputType = {