🐛 fix(PaginatorComponent): fix initial pageIndex value to start from 0 instead of 1 for consistency
✨ feat(PaginatorComponent): update rowsCount options to [10, 20, 50, 100] for more flexibility in choosing page size 🐛 fix(PaginatorComponent): fix maxIndex calculation to always be 100 for now, to avoid incorrect pagination 🐛 fix(PaginatorComponent): fix page index display to start from 1 instead of 0 for better user experience 🐛 fix(PaginatorComponent): fix disabled state of first and previous buttons to be disabled when index is 0 🐛 fix(PaginatorComponent): fix disabled state of next and last buttons to be disabled when index is equal to maxIndex ✨ feat(authGuard): add new ProtectedLoginRoute component to redirect authenticated users to home page 🐛 fix(genericIconComponent): fix icon size to be smaller with h-3 and w-3 classes 🐛 fix(ui/checkbox): fix checkbox icon size to be smaller with h-3 and w-3 classes ✨ feat(constants): add CONTROL_NEW_USER constant for new user form initialization 🐛 fix(authContext): fix getAuthentication function to correctly check for stored access and refresh tokens 🐛 fix(api.tsx): remove unused imports and reorganize imports for better readability ✨ feat(api.tsx): add support for retrying failed requests up to 3 times with a 5-second delay between retries 🐛 fix(api.tsx): fix logic for excluding certain URLs from error retries ✨ feat(api.tsx): add support for adding access token to every request as a request interceptor 🐛 fix(api.tsx): fix formatting and remove unnecessary code ✨ feat(index.ts): add support for fetching users, adding a user, fetching users with pagination, deleting a user, and updating a user 🐛 fix(UserManagementModal): fix typo in username state variable name ✨ feat(UserManagementModal): add support for password visibility toggle in password and confirm password fields ✨ feat(UserManagementModal): add support for is_disabled and is_superuser checkboxes 🐛 fix(LoginPage): fix typo in username state variable name ✨ feat(LoginPage): add support for handling input changes and signing in with username and password 🔨 refactor(AdminPage/index.tsx): import Checkbox component to improve code readability and maintainability 🔨 refactor(AdminPage/index.tsx): import API functions separately to improve code readability and maintainability 🔨 refactor(AdminPage/index.tsx): import alertContext from contexts/alertContext to improve code readability and maintainability 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused variables and functions to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused imports to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve code cleanliness and performance 🔨 refactor(AdminPage/index.tsx): remove unused code and dependencies to improve 🐛 fix(AdminPage/index.tsx): update text when there are no users registered to improve clarity and user experience ✨ feat(AdminPage/index.tsx): add loading state when fetching users to provide feedback to the user 🔨 refactor(AdminPage/index.tsx): refactor filter functionality to reset filter when input value is cleared 🔨 refactor(AdminPage/index.tsx): refactor filter functionality to use the current user list instead of the filtered list 🔨 refactor(AdminPage/index.tsx): refactor handleNewUser function to pass the user object instead of index 🔨 refactor(AdminPage/index.tsx): refactor handleEditUser function to pass the user id instead of index 🔨 refactor(AdminPage/index.tsx): refactor handleDeleteUser function to pass the user object instead of index 🔨 refactor(AdminPage/index.tsx): refactor TableRow key to use index instead of user.user 🔨 refactor(AdminPage/index.tsx): refactor TableCell content to use ShadTooltip component for better user experience 🔨 refactor(AdminPage/index.tsx): refactor Checkbox components to use id and checked props for better semantics 🔨 refactor(AdminPage/index.tsx): refactor date formatting to use toISOString and split methods for better readability 🔨 refactor(AdminPage/index.tsx): refactor UserManagementModal titleHeader to use user.id instead of user.user for better clarity 🔨 refactor(AdminPage/index.tsx): refactor handleEditUser function to pass the user id instead of index 🔨 refactor(AdminPage/index.tsx): refactor handleDeleteUser function to pass the user object instead of index 🔨 refactor(AdminPage/index.tsx): refactor PaginatorComponent to use handleChangePagination function for better readability and maintainability 🐛 fix(loginPage/index.tsx): remove unused import statement for 'error' from console module ✨ feat(loginPage/index.tsx): add support for displaying error message when signing in fails 🐛 fix(routes.tsx): add ProtectedLoginRoute component to protect login, signup, and login/admin routes 🐛 fix(api/index.ts): add missing 'is_disabled' and 'is_superuser' properties to Users type 🐛 fix(components/index.ts): change value type of inputHandlerEventType to include boolean ✨ feat(components/index.ts): add UserInputType type for user input data 🐛 fix(styleUtils.ts): add missing Eye and EyeOff icons to nodeIconsLucide object
This commit is contained in:
parent
35dcdb9e34
commit
ff72640e76
17 changed files with 609 additions and 458 deletions
|
|
@ -12,16 +12,16 @@ import { Button } from "../ui/button";
|
||||||
|
|
||||||
export default function PaginatorComponent({
|
export default function PaginatorComponent({
|
||||||
pageSize = 10,
|
pageSize = 10,
|
||||||
pageIndex = 1,
|
pageIndex = 0,
|
||||||
rowsCount = [10, 20, 30],
|
rowsCount = [10, 20, 50, 100],
|
||||||
totalRowsCount = 0,
|
totalRowsCount = 0,
|
||||||
paginate,
|
paginate,
|
||||||
}: PaginatorComponentType) {
|
}: PaginatorComponentType) {
|
||||||
|
|
||||||
const [size, setPageSize] = useState(pageSize);
|
const [size, setPageSize] = useState(pageSize);
|
||||||
const [index, setPageIndex] = useState(pageIndex);
|
const [index, setPageIndex] = useState(pageIndex);
|
||||||
|
|
||||||
const [maxIndex, setMaxPageIndex] = useState(
|
const [maxIndex, setMaxPageIndex] = useState(
|
||||||
Math.ceil(totalRowsCount / pageSize)
|
100
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -34,8 +34,8 @@ export default function PaginatorComponent({
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(pageSize: string) => {
|
onValueChange={(pageSize: string) => {
|
||||||
setPageSize(Number(pageSize));
|
setPageSize(Number(pageSize));
|
||||||
setMaxPageIndex(Math.ceil(totalRowsCount / Number(pageSize)));
|
setMaxPageIndex(100);
|
||||||
paginate(Number(pageSize), index);
|
paginate(Number(pageSize), 0);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-[100px]">
|
<SelectTrigger className="w-[100px]">
|
||||||
|
|
@ -51,30 +51,27 @@ export default function PaginatorComponent({
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||||
Page {index} of {maxIndex}
|
Page {index+1} of {maxIndex}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Button
|
<Button
|
||||||
|
disabled={index <= 0}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hidden h-8 w-8 p-0 lg:flex"
|
className="hidden h-8 w-8 p-0 lg:flex"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPageIndex(1);
|
setPageIndex(0);
|
||||||
paginate(size, 1);
|
paginate(size, 0);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Go to first page</span>
|
<span className="sr-only">Go to first page</span>
|
||||||
<IconComponent name="ChevronsLeft" className="h-4 w-4" />
|
<IconComponent name="ChevronsLeft" className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
disabled={index <= 0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (index <= 1) {
|
if (index > 0) {
|
||||||
setPageIndex(1);
|
setPageIndex(index - 1);
|
||||||
paginate(size, 1);
|
paginate(size, index - 1);
|
||||||
} else {
|
|
||||||
{
|
|
||||||
setPageIndex(index - 1);
|
|
||||||
paginate(size, index - 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -84,14 +81,10 @@ export default function PaginatorComponent({
|
||||||
<IconComponent name="ChevronLeft" className="h-4 w-4" />
|
<IconComponent name="ChevronLeft" className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
disabled={maxIndex === index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (index >= maxIndex) {
|
setPageIndex(index + 1);
|
||||||
setPageIndex(maxIndex);
|
paginate(size, index + 1);
|
||||||
paginate(size, maxIndex);
|
|
||||||
} else {
|
|
||||||
setPageIndex(index + 1);
|
|
||||||
paginate(size, index + 1);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-8 w-8 p-0"
|
className="h-8 w-8 p-0"
|
||||||
|
|
@ -100,6 +93,7 @@ export default function PaginatorComponent({
|
||||||
<IconComponent name="ChevronRight" className="h-4 w-4" />
|
<IconComponent name="ChevronRight" className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
disabled={maxIndex === index}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hidden h-8 w-8 p-0 lg:flex"
|
className="hidden h-8 w-8 p-0 lg:flex"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,13 @@ import { Navigate } from "react-router-dom";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
|
|
||||||
export const ProtectedRoute = ({ children }) => {
|
export const ProtectedRoute = ({ children }) => {
|
||||||
const { isAuthenticated, logout, getAuthentication } = useContext(AuthContext);
|
const { isAuthenticated, logout, getAuthentication } =
|
||||||
|
useContext(AuthContext);
|
||||||
if (!isAuthenticated && !getAuthentication()) {
|
|
||||||
logout();
|
if (!isAuthenticated && !getAuthentication()) {
|
||||||
return <Navigate to="/login" replace />;
|
logout();
|
||||||
}
|
return <Navigate to="/login" replace />;
|
||||||
|
}
|
||||||
return children;
|
|
||||||
};
|
return children;
|
||||||
|
};
|
||||||
|
|
|
||||||
14
src/frontend/src/components/authLoginGuard/index.tsx
Normal file
14
src/frontend/src/components/authLoginGuard/index.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { Navigate } from "react-router-dom";
|
||||||
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
|
|
||||||
|
export const ProtectedLoginRoute = ({ children }) => {
|
||||||
|
const { getAuthentication } = useContext(AuthContext);
|
||||||
|
|
||||||
|
if (getAuthentication()) {
|
||||||
|
window.location.replace('/');
|
||||||
|
return <Navigate to="/" replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
import { IconComponentProps } from "../../types/components";
|
import { IconComponentProps } from "../../types/components";
|
||||||
import { nodeIconsLucide } from "../../utils/styleUtils";
|
import { nodeIconsLucide } from "../../utils/styleUtils";
|
||||||
|
|
||||||
export default function IconComponent({
|
const ForwardedIconComponent = forwardRef(({
|
||||||
name,
|
name,
|
||||||
className,
|
className,
|
||||||
iconColor,
|
iconColor,
|
||||||
}: IconComponentProps): JSX.Element {
|
}: IconComponentProps, ref) => {
|
||||||
const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"];
|
const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"];
|
||||||
return (
|
return (
|
||||||
<TargetIcon
|
<TargetIcon
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
className={className}
|
className={className}
|
||||||
style={{ color: iconColor }}
|
style={{ color: iconColor }}
|
||||||
|
ref={ref}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
export default ForwardedIconComponent;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ const Checkbox = React.forwardRef<
|
||||||
<CheckboxPrimitive.Indicator
|
<CheckboxPrimitive.Indicator
|
||||||
className={cn("flex items-center justify-center text-current")}
|
className={cn("flex items-center justify-center text-current")}
|
||||||
>
|
>
|
||||||
<IconComponent name="Check" className="h-4 w-4" />
|
<IconComponent name="Check" className="h-3 w-3" />
|
||||||
</CheckboxPrimitive.Indicator>
|
</CheckboxPrimitive.Indicator>
|
||||||
</CheckboxPrimitive.Root>
|
</CheckboxPrimitive.Root>
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -520,6 +520,14 @@ export const CONTROL_LOGIN_STATE = {
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CONTROL_NEW_USER = {
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
is_disabled: false,
|
||||||
|
is_superuser: false,
|
||||||
|
};
|
||||||
|
|
||||||
export const tabsCode = [];
|
export const tabsCode = [];
|
||||||
|
|
||||||
export function tabsArray(codes: string[], method: number) {
|
export function tabsArray(codes: string[], method: number) {
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,8 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
function getAuthentication(){
|
function getAuthentication(){
|
||||||
const storedRefreshToken = cookies.get('refresh_token');
|
const storedRefreshToken = cookies.get('refresh_token');
|
||||||
const storedAccess = cookies.get('refresh_token');
|
const storedAccess = cookies.get('refresh_token');
|
||||||
if (storedAccess && storedRefreshToken) {
|
const auth = storedAccess && storedRefreshToken ? true : false;
|
||||||
setAccessToken(storedAccess);
|
return auth;
|
||||||
setRefreshToken(storedRefreshToken);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function login(newAccessToken: string, refreshToken: string) {
|
function login(newAccessToken: string, refreshToken: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import axios, { AxiosError, AxiosInstance } from "axios";
|
import axios, { AxiosError, AxiosInstance } from "axios";
|
||||||
import { useContext, useEffect, useRef, useState } from "react";
|
import { useContext, useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { renewAccessToken } from ".";
|
||||||
|
import { URL_EXCLUDED_FROM_ERROR_RETRIES } from "../../constants/constants";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { URL_EXCLUDED_FROM_ERROR_RETRIES } from "../../constants/constants";
|
|
||||||
import { renewAccessToken } from ".";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
// Create a new Axios instance
|
// Create a new Axios instance
|
||||||
const api: AxiosInstance = axios.create({
|
const api: AxiosInstance = axios.create({
|
||||||
|
|
@ -23,67 +23,69 @@ function ApiInterceptor() {
|
||||||
if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {
|
if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
if (error.response?.status === 401) {
|
||||||
if(error.response?.status === 401){
|
|
||||||
const refreshToken = localStorage.getItem("refresh_token");
|
const refreshToken = localStorage.getItem("refresh_token");
|
||||||
if (refreshToken) {
|
if (refreshToken) {
|
||||||
const res = await renewAccessToken(refreshToken);
|
const res = await renewAccessToken(refreshToken);
|
||||||
login(res.data.access_token, res.data.refresh_token);
|
login(res.data.access_token, res.data.refresh_token);
|
||||||
try {
|
try {
|
||||||
const accessToken = localStorage.getItem("access_token");
|
const accessToken = localStorage.getItem("access_token");
|
||||||
delete error.config.headers["Authorization"];
|
delete error.config.headers["Authorization"];
|
||||||
error.config.headers["Authorization"] = `Bearer ${accessToken}`;
|
error.config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||||
const response = await axios.request(error.config);
|
const response = await axios.request(error.config);
|
||||||
return response;
|
return response;
|
||||||
}
|
} catch (error) {
|
||||||
catch (error) {
|
if (error.response?.status === 401) {
|
||||||
if(error.response?.status === 401){
|
logout();
|
||||||
logout();
|
navigate("/login");
|
||||||
navigate("/login");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
else{
|
let retryCount = 0;
|
||||||
let retryCount = 0;
|
while (retryCount < 4) {
|
||||||
while (retryCount < 4) {
|
await sleep(5000); // Sleep for 5 seconds
|
||||||
await sleep(5000); // Sleep for 5 seconds
|
retryCount++;
|
||||||
retryCount++;
|
try {
|
||||||
try {
|
const response = await axios.request(error.config);
|
||||||
const response = await axios.request(error.config);
|
return response;
|
||||||
return response;
|
} catch (error) {
|
||||||
} catch (error) {
|
if (retryCount === 3) {
|
||||||
if (retryCount === 3) {
|
setErrorData({
|
||||||
setErrorData({
|
title: "There was an error on web connection, please: ",
|
||||||
title: "There was an error on web connection, please: ",
|
list: [
|
||||||
list: [
|
"Refresh the page",
|
||||||
"Refresh the page",
|
"Use a new flow tab",
|
||||||
"Use a new flow tab",
|
"Check if the backend is up",
|
||||||
"Check if the backend is up",
|
"Endpoint: " + error.config?.url,
|
||||||
"Endpoint: " + error.config?.url,
|
],
|
||||||
],
|
});
|
||||||
});
|
return Promise.reject(error);
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Request interceptor to add access token to every request
|
// Request interceptor to add access token to every request
|
||||||
const requestInterceptor = api.interceptors.request.use(
|
const requestInterceptor = api.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
|
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
config.headers["Authorization"] = `Bearer ${accessToken}`;
|
config.headers["Authorization"] = `Bearer ${accessToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(
|
if (
|
||||||
config?.url?.includes("https://raw.githubusercontent.com/logspace-ai/langflow_examples/main/examples") ||
|
config?.url?.includes(
|
||||||
config?.url?.includes("https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples"))
|
"https://raw.githubusercontent.com/logspace-ai/langflow_examples/main/examples"
|
||||||
{
|
) ||
|
||||||
|
config?.url?.includes(
|
||||||
|
"https://api.github.com/repos/logspace-ai/langflow_examples/contents/examples"
|
||||||
|
) ||
|
||||||
|
config?.url?.includes(
|
||||||
|
"https://api.github.com/repos/logspace-ai/langflow"
|
||||||
|
)
|
||||||
|
) {
|
||||||
delete config.headers["Authorization"];
|
delete config.headers["Authorization"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
import { AxiosResponse } from "axios";
|
import { AxiosResponse } from "axios";
|
||||||
import { ReactFlowJsonObject } from "reactflow";
|
import { ReactFlowJsonObject } from "reactflow";
|
||||||
import { api } from "../../controllers/API/api";
|
import { api } from "../../controllers/API/api";
|
||||||
import { APIObjectType, LoginAuthType, LoginType, sendAllProps } from "../../types/api/index";
|
import {
|
||||||
|
APIObjectType,
|
||||||
|
LoginType,
|
||||||
|
Users,
|
||||||
|
sendAllProps,
|
||||||
|
} from "../../types/api/index";
|
||||||
|
import { UserInputType } from "../../types/components";
|
||||||
import { FlowStyleType, FlowType } from "../../types/flow";
|
import { FlowStyleType, FlowType } from "../../types/flow";
|
||||||
import {
|
import {
|
||||||
APIClassType,
|
APIClassType,
|
||||||
|
|
@ -347,9 +353,7 @@ export async function postCustomComponent(
|
||||||
return await api.post(`/api/v1/custom_component`, { code });
|
return await api.post(`/api/v1/custom_component`, { code });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onLogin(
|
export async function onLogin(user: LoginType) {
|
||||||
user: LoginType
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
"http://localhost:7860/login",
|
"http://localhost:7860/login",
|
||||||
|
|
@ -373,9 +377,7 @@ export async function onLogin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function renewAccessToken(
|
export async function renewAccessToken(token: string) {
|
||||||
token: string
|
|
||||||
){
|
|
||||||
try {
|
try {
|
||||||
return await api.post(`http://localhost:7860/refresh?token=${token}`);
|
return await api.post(`http://localhost:7860/refresh?token=${token}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -384,4 +386,64 @@ export async function renewAccessToken(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUsers(): Promise<Users> {
|
||||||
|
try {
|
||||||
|
return await api.get(`http://localhost:7860/user`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addUser(user: UserInputType): Promise<Users> {
|
||||||
|
try {
|
||||||
|
const res = await api.post(`http://localhost:7860/user`, user);
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUsersPage(
|
||||||
|
skip: number,
|
||||||
|
limit: number
|
||||||
|
): Promise<[Users]> {
|
||||||
|
try {
|
||||||
|
const res = await api.get(
|
||||||
|
`http://localhost:7860/users?skip=${skip}&limit=${limit}`
|
||||||
|
);
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteUser(user_id: string) {
|
||||||
|
try {
|
||||||
|
const res = await api.delete(`http://localhost:7860/user/${user_id}`);
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUser(user_id: string, user: Users) {
|
||||||
|
try {
|
||||||
|
const res = await api.patch(`http://localhost:7860/user/${user_id}`, user);
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
import * as Form from "@radix-ui/react-form";
|
import * as Form from "@radix-ui/react-form";
|
||||||
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { UserManagementType } from "../../types/components";
|
import { Checkbox } from "../../components/ui/checkbox";
|
||||||
|
import { CONTROL_NEW_USER } from "../../constants/constants";
|
||||||
|
import {
|
||||||
|
UserInputType,
|
||||||
|
UserManagementType,
|
||||||
|
inputHandlerEventType,
|
||||||
|
} from "../../types/components";
|
||||||
import { nodeIconsLucide } from "../../utils/styleUtils";
|
import { nodeIconsLucide } from "../../utils/styleUtils";
|
||||||
import BaseModal from "../baseModal";
|
import BaseModal from "../baseModal";
|
||||||
|
|
||||||
|
|
@ -17,18 +24,30 @@ export default function UserManagementModal({
|
||||||
onConfirm,
|
onConfirm,
|
||||||
}: UserManagementType) {
|
}: UserManagementType) {
|
||||||
const Icon: any = nodeIconsLucide[icon];
|
const Icon: any = nodeIconsLucide[icon];
|
||||||
|
const [pwdVisible, setPwdVisible] = useState(false);
|
||||||
|
const [confirmPwdVisible, setConfirmPwdVisible] = useState(false);
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const [password, setPassword] = useState(data?.password ?? "");
|
const [password, setPassword] = useState(data?.password ?? "");
|
||||||
const [username, setUserName] = useState(data?.user ?? "");
|
const [username, setUserName] = useState(data?.username ?? "");
|
||||||
const [confirmPassword, setConfirmPassword] = useState(data?.password ?? "");
|
const [confirmPassword, setConfirmPassword] = useState(data?.password ?? "");
|
||||||
|
const [isDisabled, setIsDisabled] = useState(data?.is_disabled ?? false);
|
||||||
|
const [isSuperUser, setIsSuperUser] = useState(data?.is_superuser ?? false);
|
||||||
|
|
||||||
|
const [inputState, setInputState] = useState<UserInputType>(CONTROL_NEW_USER);
|
||||||
|
|
||||||
|
function handleInput({
|
||||||
|
target: { name, value },
|
||||||
|
}: inputHandlerEventType): void {
|
||||||
|
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
resetForm();
|
resetForm();
|
||||||
}
|
}
|
||||||
}, [data, open]);
|
}, [open]);
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
setPassword("");
|
setPassword("");
|
||||||
|
|
@ -54,10 +73,8 @@ export default function UserManagementModal({
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = Object.fromEntries(new FormData(event.currentTarget));
|
|
||||||
resetForm();
|
resetForm();
|
||||||
onConfirm(index ?? -1, data);
|
onConfirm(1, inputState);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
|
@ -78,8 +95,9 @@ export default function UserManagementModal({
|
||||||
</div>
|
</div>
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<input
|
<input
|
||||||
onChange={(input) => {
|
onChange={({ target: { value } }) => {
|
||||||
setUserName(input.target.value);
|
handleInput({ target: { name: "username", value } });
|
||||||
|
setUserName(value);
|
||||||
}}
|
}}
|
||||||
value={username}
|
value={username}
|
||||||
className="primary-input"
|
className="primary-input"
|
||||||
|
|
@ -104,21 +122,38 @@ export default function UserManagementModal({
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Label className="data-[invalid]:label-invalid">
|
<Form.Label className="data-[invalid]:label-invalid flex">
|
||||||
Password{" "}
|
Password{" "}
|
||||||
<span className="font-medium text-destructive">*</span>
|
<span className="ml-1 mr-1 font-medium text-destructive">
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
{pwdVisible && (
|
||||||
|
<Eye
|
||||||
|
onClick={() => setPwdVisible(!pwdVisible)}
|
||||||
|
className="h-5 cursor-pointer" strokeWidth={1.5} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!pwdVisible && (
|
||||||
|
<EyeOff
|
||||||
|
onClick={() => setPwdVisible(!pwdVisible)}
|
||||||
|
className="h-5 cursor-pointer" strokeWidth={1.5} />
|
||||||
|
)}
|
||||||
|
|
||||||
</Form.Label>
|
</Form.Label>
|
||||||
</div>
|
</div>
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<input
|
<input
|
||||||
onChange={(input) => {
|
onChange={({ target: { value } }) => {
|
||||||
setPassword(input.target.value);
|
handleInput({ target: { name: "password", value } });
|
||||||
|
setPassword(value);
|
||||||
}}
|
}}
|
||||||
value={password}
|
value={password}
|
||||||
className="primary-input"
|
className="primary-input"
|
||||||
required
|
required
|
||||||
|
type={pwdVisible ? "text" : "password"}
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
|
|
||||||
<Form.Message className="field-invalid" match="valueMissing">
|
<Form.Message className="field-invalid" match="valueMissing">
|
||||||
Please enter a password
|
Please enter a password
|
||||||
</Form.Message>
|
</Form.Message>
|
||||||
|
|
@ -143,9 +178,22 @@ export default function UserManagementModal({
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Label className="data-[invalid]:label-invalid">
|
<Form.Label className="data-[invalid]:label-invalid flex">
|
||||||
Confirm password{" "}
|
Confirm password{" "}
|
||||||
<span className="font-medium text-destructive">*</span>
|
<span className="ml-1 mr-1 font-medium text-destructive">
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
{confirmPwdVisible && (
|
||||||
|
<Eye
|
||||||
|
onClick={() => setConfirmPwdVisible(!confirmPwdVisible)}
|
||||||
|
className="h-5 cursor-pointer" strokeWidth={1.5} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!confirmPwdVisible && (
|
||||||
|
<EyeOff
|
||||||
|
onClick={() => setConfirmPwdVisible(!confirmPwdVisible)}
|
||||||
|
className="h-5 cursor-pointer" strokeWidth={1.5} />
|
||||||
|
)}
|
||||||
</Form.Label>
|
</Form.Label>
|
||||||
</div>
|
</div>
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
|
|
@ -156,6 +204,7 @@ export default function UserManagementModal({
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
className="primary-input"
|
className="primary-input"
|
||||||
required
|
required
|
||||||
|
type={confirmPwdVisible ? "text" : "password"}
|
||||||
/>
|
/>
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
<Form.Message className="field-invalid" match="valueMissing">
|
<Form.Message className="field-invalid" match="valueMissing">
|
||||||
|
|
@ -164,57 +213,49 @@ export default function UserManagementModal({
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex gap-8">
|
||||||
|
<Form.Field name="is_disabled">
|
||||||
|
<div>
|
||||||
|
<Form.Label className="data-[invalid]:label-invalid mr-3">
|
||||||
|
Disabled
|
||||||
|
</Form.Label>
|
||||||
|
<Form.Control asChild>
|
||||||
|
<Checkbox
|
||||||
|
value={isDisabled}
|
||||||
|
checked={isDisabled}
|
||||||
|
id="is_disabled"
|
||||||
|
className="relative top-0.5"
|
||||||
|
onCheckedChange={(value) => {
|
||||||
|
handleInput({ target: { name: "is_disabled", value } });
|
||||||
|
setIsDisabled(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Control>
|
||||||
|
</div>
|
||||||
|
</Form.Field>
|
||||||
|
|
||||||
{/*
|
<Form.Field name="is_superuser">
|
||||||
<Form.Field name="email">
|
<div>
|
||||||
<div
|
<Form.Label className="data-[invalid]:label-invalid mr-3">
|
||||||
style={{
|
Superuser
|
||||||
display: "flex",
|
</Form.Label>
|
||||||
alignItems: "baseline",
|
<Form.Control asChild>
|
||||||
justifyContent: "space-between",
|
<Checkbox
|
||||||
}}
|
checked={isSuperUser}
|
||||||
>
|
value={isSuperUser}
|
||||||
<Form.Label className="data-[invalid]:label-invalid">
|
id="is_superuser"
|
||||||
Email <span className="font-medium text-destructive">*</span>
|
className="relative top-0.5"
|
||||||
</Form.Label>
|
onCheckedChange={(value) => {
|
||||||
<Form.Message className="field-invalid" match="valueMissing">
|
handleInput({
|
||||||
Please enter your email
|
target: { name: "is_superuser", value },
|
||||||
</Form.Message>
|
});
|
||||||
<Form.Message className="field-invalid" match="typeMismatch">
|
setIsSuperUser(value);
|
||||||
Please provide a valid email
|
}}
|
||||||
</Form.Message>
|
/>
|
||||||
</div>
|
</Form.Control>
|
||||||
<Form.Control asChild>
|
</div>
|
||||||
<input className="primary-input" type="email" required />
|
</Form.Field>
|
||||||
</Form.Control>
|
</div>
|
||||||
</Form.Field> */}
|
|
||||||
|
|
||||||
{/*
|
|
||||||
<Form.Field name="birth">
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "baseline",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Form.Label className="data-[invalid]:label-invalid">
|
|
||||||
Date of birth{" "}
|
|
||||||
<span className="font-medium text-destructive">*</span>
|
|
||||||
</Form.Label>
|
|
||||||
<Form.Message className="field-invalid" match="valueMissing">
|
|
||||||
Please enter your date of birth
|
|
||||||
</Form.Message>
|
|
||||||
</div>
|
|
||||||
<Form.Control asChild>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
className="primary-input"
|
|
||||||
required
|
|
||||||
max={new Date().toISOString().split("T")[0]}
|
|
||||||
/>
|
|
||||||
</Form.Control>
|
|
||||||
</Form.Field> */}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="float-right">
|
<div className="float-right">
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,49 @@
|
||||||
|
import { useContext, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Button } from "../../../components/ui/button";
|
import { Button } from "../../../components/ui/button";
|
||||||
import { Input } from "../../../components/ui/input";
|
import { Input } from "../../../components/ui/input";
|
||||||
|
import { CONTROL_LOGIN_STATE } from "../../../constants/constants";
|
||||||
|
import { alertContext } from "../../../contexts/alertContext";
|
||||||
|
import { AuthContext } from "../../../contexts/authContext";
|
||||||
|
import { onLogin } from "../../../controllers/API";
|
||||||
|
import { LoginType } from "../../../types/api";
|
||||||
|
import {
|
||||||
|
inputHandlerEventType,
|
||||||
|
loginInputStateType,
|
||||||
|
} from "../../../types/components";
|
||||||
|
|
||||||
export default function LoginAdminPage() {
|
export default function LoginAdminPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
function loginAdmin() {
|
const [inputState, setInputState] =
|
||||||
navigate("/admin/");
|
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
|
||||||
|
const { login } = useContext(AuthContext);
|
||||||
|
|
||||||
|
const { password, username } = inputState;
|
||||||
|
const { setErrorData } = useContext(alertContext);
|
||||||
|
|
||||||
|
function handleInput({
|
||||||
|
target: { name, value },
|
||||||
|
}: inputHandlerEventType): void {
|
||||||
|
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function signIn() {
|
||||||
|
const user: LoginType = {
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
onLogin(user)
|
||||||
|
.then((user) => {
|
||||||
|
login(user.access_token, user.refresh_token);
|
||||||
|
navigate("/admin/");
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setErrorData({
|
||||||
|
title: "Error signing in",
|
||||||
|
list: [error["response"]["data"]["detail"]],
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -14,11 +51,24 @@ export default function LoginAdminPage() {
|
||||||
<div className="flex w-72 flex-col items-center justify-center gap-2">
|
<div className="flex w-72 flex-col items-center justify-center gap-2">
|
||||||
<span className="mb-4 text-5xl">⛓️</span>
|
<span className="mb-4 text-5xl">⛓️</span>
|
||||||
<span className="mb-6 text-2xl font-semibold text-primary">Admin</span>
|
<span className="mb-6 text-2xl font-semibold text-primary">Admin</span>
|
||||||
<Input className="bg-background" placeholder="Email address" />
|
<Input
|
||||||
<Input className="bg-background" placeholder="Password" />
|
onChange={({ target: { value } }) => {
|
||||||
|
handleInput({ target: { name: "username", value } });
|
||||||
|
}}
|
||||||
|
className="bg-background"
|
||||||
|
placeholder="Username"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
onChange={({ target: { value } }) => {
|
||||||
|
handleInput({ target: { name: "password", value } });
|
||||||
|
}}
|
||||||
|
className="bg-background"
|
||||||
|
placeholder="Password"
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
loginAdmin();
|
signIn();
|
||||||
}}
|
}}
|
||||||
variant="default"
|
variant="default"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState,useContext } from "react";
|
||||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
|
import { Checkbox } from "../../components/ui/checkbox";
|
||||||
import { Input } from "../../components/ui/input";
|
import { Input } from "../../components/ui/input";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
|
|
@ -14,237 +15,125 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "../../components/ui/table";
|
} from "../../components/ui/table";
|
||||||
|
import {
|
||||||
|
addUser,
|
||||||
|
deleteUser,
|
||||||
|
getUsersPage,
|
||||||
|
updateUser,
|
||||||
|
} from "../../controllers/API";
|
||||||
import ConfirmationModal from "../../modals/ConfirmationModal";
|
import ConfirmationModal from "../../modals/ConfirmationModal";
|
||||||
import UserManagementModal from "../../modals/UserManagementModal";
|
import UserManagementModal from "../../modals/UserManagementModal";
|
||||||
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
|
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
|
||||||
const [size, setPageSize] = useState(10);
|
const [size, setPageSize] = useState(10);
|
||||||
const [index, setPageIndex] = useState(1);
|
const [index, setPageIndex] = useState(0);
|
||||||
|
const [loadingUsers, setLoadingUsers] = useState(true);
|
||||||
|
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||||
|
|
||||||
const userList = useRef([
|
const userList = useRef([]);
|
||||||
{
|
|
||||||
user: generateRandomString(50),
|
useEffect(() => {
|
||||||
email: generateRandomString(50) + "@example.com",
|
setTimeout(() => {
|
||||||
password: generateRandomString(50),
|
getUsers();
|
||||||
register_date: generateRandomDate(),
|
}, 500);
|
||||||
},
|
}, []);
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
user: generateRandomString(8),
|
|
||||||
email: generateRandomString(10) + "@example.com",
|
|
||||||
password: generateRandomString(12),
|
|
||||||
register_date: generateRandomDate(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [filterUserList, setFilterUserList] = useState(userList.current);
|
const [filterUserList, setFilterUserList] = useState(userList.current);
|
||||||
|
|
||||||
function generateRandomString(length) {
|
function getUsers() {
|
||||||
const characters =
|
setLoadingUsers(true);
|
||||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
getUsersPage(index, size)
|
||||||
let result = "";
|
.then((users) => {
|
||||||
for (let i = 0; i < length; i++) {
|
userList.current = users;
|
||||||
const randomIndex = Math.floor(Math.random() * characters.length);
|
setFilterUserList(users);
|
||||||
result += characters.charAt(randomIndex);
|
setLoadingUsers(false);
|
||||||
}
|
})
|
||||||
return result;
|
.catch((error) => {
|
||||||
|
setLoadingUsers(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateRandomDate() {
|
|
||||||
const start = new Date(2010, 0, 1);
|
|
||||||
const end = new Date();
|
|
||||||
const randomTimestamp =
|
|
||||||
start.getTime() + Math.random() * (end.getTime() - start.getTime());
|
|
||||||
const randomDate = new Date(randomTimestamp);
|
|
||||||
|
|
||||||
const options = { year: "numeric", month: "short", day: "numeric" };
|
|
||||||
return randomDate.toLocaleDateString("en-US");
|
|
||||||
}
|
|
||||||
|
|
||||||
const [editUser, setEditUser] = useState(-1);
|
|
||||||
const [editedUser, setEditedUser] = useState("");
|
|
||||||
const [modalEditOpen, setModalEditOpen] = useState(false);
|
|
||||||
const [modalDeleteOpen, setModalDeleteOpen] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
resetFilter();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleInputChange = (event, index) => {
|
|
||||||
const user = _.cloneDeepWith(userList.current);
|
|
||||||
user[index].password = event.target.value;
|
|
||||||
userList.current = user;
|
|
||||||
|
|
||||||
const userFilter = _.cloneDeepWith(filterUserList);
|
|
||||||
userFilter[index].password = event.target.value;
|
|
||||||
setFilterUserList(userFilter);
|
|
||||||
|
|
||||||
setEditedUser(event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleChangePagination(pageIndex: number, pageSize: number) {
|
function handleChangePagination(pageIndex: number, pageSize: number) {
|
||||||
setPageIndex(pageIndex);
|
setLoadingUsers(true);
|
||||||
setPageSize(pageSize);
|
getUsersPage(pageIndex, pageSize)
|
||||||
|
.then((users) => {
|
||||||
const startIndex = (pageIndex - 1) * pageSize;
|
userList.current = users;
|
||||||
const endIndex = startIndex + pageSize;
|
setFilterUserList(users);
|
||||||
const newList = userList.current.slice(startIndex, endIndex);
|
setLoadingUsers(false);
|
||||||
|
})
|
||||||
setFilterUserList(newList);
|
.catch((error) => {
|
||||||
|
setLoadingUsers(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetFilter() {
|
function resetFilter() {
|
||||||
setFilterUserList(userList.current);
|
setPageIndex(0);
|
||||||
setPageIndex(1);
|
|
||||||
setPageSize(10);
|
setPageSize(10);
|
||||||
|
getUsers();
|
||||||
const startIndex = (index - 1) * size;
|
|
||||||
const endIndex = index + size - 1;
|
|
||||||
const newList = userList.current.slice(startIndex, endIndex);
|
|
||||||
|
|
||||||
console.log(userList.current);
|
|
||||||
|
|
||||||
setFilterUserList(newList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleFilterUsers(input: string) {
|
function handleFilterUsers(input: string) {
|
||||||
setInputValue(input);
|
setInputValue(input);
|
||||||
|
|
||||||
if (input === "") {
|
if (input === "") {
|
||||||
resetFilter();
|
setFilterUserList(userList.current);
|
||||||
} else {
|
} else {
|
||||||
const filteredList = userList.current.filter(
|
const filteredList = userList.current.filter((user) =>
|
||||||
(user) =>
|
user.username.toLowerCase().includes(input.toLowerCase())
|
||||||
user.user.toLowerCase().includes(input.toLowerCase()) ||
|
|
||||||
user.email.toLowerCase().includes(input.toLowerCase())
|
|
||||||
);
|
);
|
||||||
setFilterUserList(filteredList);
|
setFilterUserList(filteredList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteUser(index) {
|
function handleDeleteUser(user) {
|
||||||
const user = _.cloneDeepWith(userList.current);
|
deleteUser(user.id)
|
||||||
user.splice(index, 1);
|
.then((res) => {
|
||||||
userList.current = user;
|
resetFilter();
|
||||||
|
setSuccessData({
|
||||||
const userFilter = _.cloneDeepWith(filterUserList);
|
title: "Success! User deleted!"
|
||||||
userFilter.splice(index, 1);
|
});
|
||||||
setFilterUserList(userFilter);
|
})
|
||||||
|
.catch((error) => {
|
||||||
resetFilter();
|
setErrorData({
|
||||||
|
title: "Error on delete user",
|
||||||
|
list: [error["response"]["data"]["detail"]],
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditUser(index, user) {
|
function handleEditUser(userId, user) {
|
||||||
const newUser = _.cloneDeepWith(userList.current);
|
updateUser(userId, user)
|
||||||
newUser[index].password = user.password;
|
.then((res) => {
|
||||||
newUser[index].user = user.username;
|
resetFilter();
|
||||||
userList.current = newUser;
|
setSuccessData({
|
||||||
resetFilter();
|
title: "Success! User edited!"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setErrorData({
|
||||||
|
title: "Error on edit user",
|
||||||
|
list: [error["response"]["data"]["detail"]],
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewUser(user) {
|
function handleNewUser(user) {
|
||||||
const newUser = {
|
addUser(user)
|
||||||
user: user.username,
|
.then((res) => {
|
||||||
email: generateRandomString(50) + "@example.com",
|
resetFilter();
|
||||||
password: user.password,
|
setSuccessData({
|
||||||
register_date: generateRandomDate(),
|
title: "Success! New user added!"
|
||||||
};
|
});
|
||||||
|
})
|
||||||
userList.current.unshift(newUser);
|
.catch((error) => {
|
||||||
console.log(userList.current);
|
setErrorData({
|
||||||
|
title: "Error on add new user",
|
||||||
resetFilter();
|
list: [error["response"]["data"]["detail"]],
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -265,85 +154,138 @@ export default function AdminPage() {
|
||||||
<div className="flex items-center space-x-2"></div>
|
<div className="flex items-center space-x-2"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{userList.current.length === 0 && (
|
{userList.current.length === 0 && !loadingUsers && (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2>There's no users left :)</h2>
|
<h2>There's no users registered :)</h2>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{userList.current.length > 0 && (
|
<>
|
||||||
<>
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex flex-1 items-center space-x-2">
|
||||||
<div className="flex flex-1 items-center space-x-2">
|
<Input
|
||||||
<Input
|
value={inputValue}
|
||||||
value={inputValue}
|
placeholder="Filter users..."
|
||||||
placeholder="Filter users..."
|
className="h-8 w-[150px] lg:w-[250px]"
|
||||||
className="h-8 w-[150px] lg:w-[250px]"
|
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
/>
|
||||||
/>
|
{inputValue.length > 0 && (
|
||||||
{inputValue.length > 0 && (
|
<Button
|
||||||
<Button
|
onClick={() => {
|
||||||
onClick={() => {
|
setInputValue("");
|
||||||
resetFilter();
|
setFilterUserList(userList.current);
|
||||||
setInputValue("");
|
|
||||||
}}
|
|
||||||
variant="ghost"
|
|
||||||
className="h-8 px-2 lg:px-3"
|
|
||||||
>
|
|
||||||
Reset
|
|
||||||
<X className="ml-2 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<UserManagementModal
|
|
||||||
title="New User"
|
|
||||||
titleHeader={"Add a new user"}
|
|
||||||
cancelText="Cancel"
|
|
||||||
confirmationText="Save"
|
|
||||||
icon={"UserPlus2"}
|
|
||||||
onConfirm={(index, user) => {
|
|
||||||
handleNewUser(user);
|
|
||||||
}}
|
}}
|
||||||
|
variant="ghost"
|
||||||
|
className="h-8 px-2 lg:px-3"
|
||||||
>
|
>
|
||||||
<Button>New User</Button>
|
Reset
|
||||||
</UserManagementModal>
|
<X className="ml-2 h-4 w-4" />
|
||||||
</div>
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div>
|
||||||
className="overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted
|
<UserManagementModal
|
||||||
custom-scroll"
|
title="New User"
|
||||||
>
|
titleHeader={"Add a new user"}
|
||||||
<Table className="table-fixed bg-muted outline-1 ">
|
cancelText="Cancel"
|
||||||
<TableHeader>
|
confirmationText="Save"
|
||||||
<TableRow>
|
icon={"UserPlus2"}
|
||||||
<TableHead className="h-10">User</TableHead>
|
onConfirm={(index, user) => {
|
||||||
<TableHead className="h-10">Password</TableHead>
|
handleNewUser(user);
|
||||||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
}}
|
||||||
</TableRow>
|
>
|
||||||
</TableHeader>
|
<Button>New User</Button>
|
||||||
|
</UserManagementModal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{loadingUsers && (
|
||||||
|
<div>
|
||||||
|
<strong>Loading...</strong>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll max-h-[26rem]" +
|
||||||
|
(loadingUsers ? " border-0" : "")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Table className={"table-fixed bg-muted outline-1"}>
|
||||||
|
<TableHeader
|
||||||
|
className={
|
||||||
|
loadingUsers
|
||||||
|
? "hidden"
|
||||||
|
: "table-fixed bg-muted outline-1"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead className="h-10">Id</TableHead>
|
||||||
|
<TableHead className="h-10">Username</TableHead>
|
||||||
|
<TableHead className="h-10">Disabled</TableHead>
|
||||||
|
<TableHead className="h-10">Superuser</TableHead>
|
||||||
|
<TableHead className="h-10">Created At</TableHead>
|
||||||
|
<TableHead className="h-10">Updated At</TableHead>
|
||||||
|
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
{!loadingUsers && (
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{filterUserList.map((user, index) => (
|
{filterUserList.map((user, index) => (
|
||||||
<TableRow key={user.user}>
|
<TableRow key={index}>
|
||||||
<TableCell className="truncate py-2 font-medium">
|
<TableCell className="truncate py-2 font-medium">
|
||||||
{user.user}
|
<ShadTooltip content={user.id}>
|
||||||
|
<span className="cursor-default">
|
||||||
|
{user.id}
|
||||||
|
</span>
|
||||||
|
</ShadTooltip>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="truncate py-2">
|
<TableCell className="truncate py-2">
|
||||||
{user.password}
|
<ShadTooltip content={user.username}>
|
||||||
|
<span className="cursor-default">
|
||||||
|
{user.username}
|
||||||
|
</span>
|
||||||
|
</ShadTooltip>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="relative left-5 truncate py-2">
|
||||||
|
<Checkbox
|
||||||
|
id="is_disabled"
|
||||||
|
checked={user.is_disabled}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="relative left-5 truncate py-2">
|
||||||
|
<Checkbox
|
||||||
|
id="is_superuser"
|
||||||
|
checked={user.is_superuser}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="truncate py-2 ">
|
||||||
|
{
|
||||||
|
new Date(user.create_at)
|
||||||
|
.toISOString()
|
||||||
|
.split("T")[0]
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="truncate py-2">
|
||||||
|
{
|
||||||
|
new Date(user.updated_at)
|
||||||
|
.toISOString()
|
||||||
|
.split("T")[0]
|
||||||
|
}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="flex w-[100px] py-2 text-right">
|
<TableCell className="flex w-[100px] py-2 text-right">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<UserManagementModal
|
<UserManagementModal
|
||||||
title="Edit"
|
title="Edit"
|
||||||
titleHeader={`${user.user}`}
|
titleHeader={`${user.id}`}
|
||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
confirmationText="Edit"
|
confirmationText="Edit"
|
||||||
icon={"UserPlus2"}
|
icon={"UserPlus2"}
|
||||||
data={user}
|
data={user}
|
||||||
index={index}
|
index={index}
|
||||||
onConfirm={(index, user) => {
|
onConfirm={(index, editUser) => {
|
||||||
handleEditUser(index, user);
|
handleEditUser(user.id, editUser);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ShadTooltip content="Edit" side="top">
|
<ShadTooltip content="Edit" side="top">
|
||||||
|
|
@ -365,7 +307,7 @@ export default function AdminPage() {
|
||||||
data={user}
|
data={user}
|
||||||
index={index}
|
index={index}
|
||||||
onConfirm={(index, user) => {
|
onConfirm={(index, user) => {
|
||||||
handleDeleteUser(index);
|
handleDeleteUser(user);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ShadTooltip content="Delete" side="top">
|
<ShadTooltip content="Delete" side="top">
|
||||||
|
|
@ -380,18 +322,19 @@ export default function AdminPage() {
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
)}
|
||||||
</div>
|
</Table>
|
||||||
<PaginatorComponent
|
</div>
|
||||||
pageIndex={index}
|
|
||||||
pageSize={size}
|
<PaginatorComponent
|
||||||
totalRowsCount={filterUserList.length}
|
pageIndex={index}
|
||||||
paginate={(pageIndex, pageSize) => {
|
pageSize={size}
|
||||||
handleChangePagination(pageSize, pageIndex);
|
totalRowsCount={filterUserList.length}
|
||||||
}}
|
paginate={(pageIndex, pageSize) => {
|
||||||
></PaginatorComponent>
|
handleChangePagination(pageSize, pageIndex);
|
||||||
</>
|
}}
|
||||||
)}
|
></PaginatorComponent>
|
||||||
|
</>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,14 @@ import InputComponent from "../../components/inputComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { Input } from "../../components/ui/input";
|
import { Input } from "../../components/ui/input";
|
||||||
import { CONTROL_LOGIN_STATE } from "../../constants/constants";
|
import { CONTROL_LOGIN_STATE } from "../../constants/constants";
|
||||||
|
import { alertContext } from "../../contexts/alertContext";
|
||||||
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
|
import { onLogin } from "../../controllers/API";
|
||||||
|
import { LoginType } from "../../types/api";
|
||||||
import {
|
import {
|
||||||
inputHandlerEventType,
|
inputHandlerEventType,
|
||||||
loginInputStateType,
|
loginInputStateType,
|
||||||
} from "../../types/components";
|
} from "../../types/components";
|
||||||
import { onLogin } from "../../controllers/API";
|
|
||||||
import { LoginType } from "../../types/api";
|
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
|
||||||
import { error } from "console";
|
|
||||||
|
|
||||||
export default function LoginPage(): JSX.Element {
|
export default function LoginPage(): JSX.Element {
|
||||||
const [inputState, setInputState] =
|
const [inputState, setInputState] =
|
||||||
|
|
@ -31,25 +30,22 @@ export default function LoginPage(): JSX.Element {
|
||||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function signIn(){
|
function signIn() {
|
||||||
|
|
||||||
const user: LoginType = {
|
const user: LoginType = {
|
||||||
username: username,
|
username: username,
|
||||||
password: password
|
password: password,
|
||||||
};
|
};
|
||||||
|
onLogin(user)
|
||||||
onLogin(
|
.then((user) => {
|
||||||
user
|
|
||||||
).then((user) => {
|
|
||||||
login(user.access_token, user.refresh_token);
|
login(user.access_token, user.refresh_token);
|
||||||
navigate("/");
|
navigate("/");
|
||||||
}).catch(error => {
|
})
|
||||||
|
.catch((error) => {
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Error signing in",
|
title: "Error signing in",
|
||||||
list: [error['response']['data']['detail']],
|
list: [error["response"]["data"]["detail"]],
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -137,9 +133,9 @@ export default function LoginPage(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Form.Submit asChild>
|
<Form.Submit asChild>
|
||||||
<Button
|
<Button onClick={() => signIn()} className="mr-3 mt-6 w-full">
|
||||||
onClick={()=>signIn()}
|
Sign in
|
||||||
className="mr-3 mt-6 w-full">Sign in</Button>
|
</Button>
|
||||||
</Form.Submit>
|
</Form.Submit>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Route, Routes } from "react-router-dom";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import { ProtectedRoute } from "./components/authGuard";
|
import { ProtectedRoute } from "./components/authGuard";
|
||||||
|
import { ProtectedLoginRoute } from "./components/authLoginGuard";
|
||||||
import AdminPage from "./pages/AdminPage";
|
import AdminPage from "./pages/AdminPage";
|
||||||
import LoginAdminPage from "./pages/AdminPage/LoginPage";
|
import LoginAdminPage from "./pages/AdminPage/LoginPage";
|
||||||
import CommunityPage from "./pages/CommunityPage";
|
import CommunityPage from "./pages/CommunityPage";
|
||||||
|
|
@ -47,9 +48,30 @@ const Router = () => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route
|
||||||
<Route path="/signup" element={<SignUp />} />
|
path="/login"
|
||||||
<Route path="/login/admin" element={<LoginAdminPage />} />
|
element={
|
||||||
|
<ProtectedLoginRoute>
|
||||||
|
<LoginPage />
|
||||||
|
</ProtectedLoginRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/signup"
|
||||||
|
element={
|
||||||
|
<ProtectedLoginRoute>
|
||||||
|
<SignUp />
|
||||||
|
</ProtectedLoginRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/login/admin"
|
||||||
|
element={
|
||||||
|
<ProtectedLoginRoute>
|
||||||
|
<LoginAdminPage />
|
||||||
|
</ProtectedLoginRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/admin"
|
path="/admin"
|
||||||
|
|
|
||||||
|
|
@ -77,3 +77,12 @@ export type LoginAuthType = {
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
token_type?: string;
|
token_type?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Users = {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
is_disabled: boolean;
|
||||||
|
is_superuser: boolean;
|
||||||
|
create_at: Date;
|
||||||
|
updated_at: Date;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ export type signUpInputStateType = {
|
||||||
|
|
||||||
export type inputHandlerEventType = {
|
export type inputHandlerEventType = {
|
||||||
target: {
|
target: {
|
||||||
value: string;
|
value: string | boolean;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -224,3 +224,10 @@ export type loginInputStateType = {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UserInputType = {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
is_disabled:boolean;
|
||||||
|
is_superuser: boolean;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import {
|
||||||
Edit,
|
Edit,
|
||||||
Eraser,
|
Eraser,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
File,
|
File,
|
||||||
FileDown,
|
FileDown,
|
||||||
FileSearch,
|
FileSearch,
|
||||||
|
|
@ -288,4 +290,6 @@ export const nodeIconsLucide = {
|
||||||
ChevronsLeft,
|
ChevronsLeft,
|
||||||
FaGithub,
|
FaGithub,
|
||||||
FaApple,
|
FaApple,
|
||||||
|
EyeOff,
|
||||||
|
Eye,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue