merge dev
This commit is contained in:
commit
0838baa50a
76 changed files with 2486 additions and 1555 deletions
567
src/frontend/package-lock.json
generated
567
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -141,45 +141,54 @@ export default function ParameterComponent({
|
|||
nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
|
||||
|
||||
return (
|
||||
<span
|
||||
key={index}
|
||||
className={classNames(
|
||||
index > 0 ? "mt-2 flex items-center" : "flex items-center"
|
||||
<>
|
||||
{index === 0 && (
|
||||
<span>
|
||||
{left
|
||||
? "Avaliable input components:"
|
||||
: "Avaliable output components:"}
|
||||
</span>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className="h-5 w-5"
|
||||
style={{
|
||||
color: nodeColors[item.family],
|
||||
}}
|
||||
<span
|
||||
key={index}
|
||||
className={classNames(
|
||||
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center"
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
<div
|
||||
className="h-5 w-5"
|
||||
strokeWidth={1.5}
|
||||
style={{
|
||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
||||
color: nodeColors[item.family],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="ps-2 text-xs text-foreground">
|
||||
{nodeNames[item.family] ?? "Other"}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
{item.type === "" ? "" : " - "}
|
||||
{item.type.split(", ").length > 2
|
||||
? item.type.split(", ").map((el, index) => (
|
||||
<React.Fragment key={el + index}>
|
||||
<span>
|
||||
{index === item.type.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))
|
||||
: item.type}
|
||||
>
|
||||
<Icon
|
||||
className="h-5 w-5"
|
||||
strokeWidth={1.5}
|
||||
style={{
|
||||
color: nodeColors[item.family] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="ps-2 text-xs text-foreground">
|
||||
{nodeNames[item.family] ?? "Other"}{" "}
|
||||
<span className="text-xs">
|
||||
{" "}
|
||||
{item.type === "" ? "" : " - "}
|
||||
{item.type.split(", ").length > 2
|
||||
? item.type.split(", ").map((el, index) => (
|
||||
<React.Fragment key={el + index}>
|
||||
<span>
|
||||
{index === item.type.split(", ").length - 1
|
||||
? el
|
||||
: (el += `, `)}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))
|
||||
: item.type}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -158,8 +158,17 @@ export default function GenericNode({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="generic-node-desc">
|
||||
<div className="generic-node-desc-text">{data.node?.description}</div>
|
||||
<div
|
||||
className={
|
||||
"generic-node-desc " +
|
||||
(data.node?.description !== "" ? "py-5" : "pb-5")
|
||||
}
|
||||
>
|
||||
{data.node?.description !== "" && (
|
||||
<div className="generic-node-desc-text">
|
||||
{data.node?.description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
|
|
|
|||
|
|
@ -47,21 +47,9 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
setInvalidName!(true);
|
||||
}
|
||||
setName(value);
|
||||
setCurrentName(value);
|
||||
};
|
||||
|
||||
const [currentName, setCurrentName] = useState(name);
|
||||
|
||||
const [currentDescription, setCurrentDescription] = useState(description);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentName(name);
|
||||
setCurrentDescription(description);
|
||||
}, [name, description]);
|
||||
|
||||
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
flows.find((f) => f.id === tabId).description = event.target.value;
|
||||
setCurrentDescription(flows.find((f) => f.id === tabId).description);
|
||||
setDescription(event.target.value);
|
||||
};
|
||||
|
||||
|
|
@ -82,7 +70,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
value={currentName ?? ""}
|
||||
value={name ?? ""}
|
||||
placeholder="File name"
|
||||
id="name"
|
||||
maxLength={maxLength}
|
||||
|
|
@ -97,7 +85,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
name="description"
|
||||
id="description"
|
||||
onChange={handleDescriptionChange}
|
||||
value={currentDescription}
|
||||
value={description}
|
||||
placeholder="Flow description"
|
||||
className="mt-2 max-h-[100px] font-normal"
|
||||
rows={3}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,15 @@ import { Button } from "../ui/button";
|
|||
|
||||
export default function PaginatorComponent({
|
||||
pageSize = 10,
|
||||
pageIndex = 0,
|
||||
pageIndex = 1,
|
||||
rowsCount = [10, 20, 50, 100],
|
||||
totalRowsCount = 0,
|
||||
paginate,
|
||||
}: PaginatorComponentType) {
|
||||
const [size, setPageSize] = useState(pageSize);
|
||||
const [index, setPageIndex] = useState(pageIndex);
|
||||
const [maxIndex, setMaxPageIndex] = useState(
|
||||
Math.ceil(totalRowsCount / pageSize)
|
||||
);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
setMaxPageIndex(Math.ceil(totalRowsCount / size));
|
||||
|
|
@ -39,8 +37,9 @@ export default function PaginatorComponent({
|
|||
onValueChange={(pageSize: string) => {
|
||||
setPageSize(Number(pageSize));
|
||||
setMaxPageIndex(Math.ceil(totalRowsCount / Number(pageSize)));
|
||||
paginate(Number(pageSize), 0);
|
||||
paginate(Number(pageSize), 1);
|
||||
}}
|
||||
value={pageSize.toString()}
|
||||
>
|
||||
<SelectTrigger className="w-[100px]">
|
||||
<SelectValue placeholder="10" />
|
||||
|
|
@ -55,30 +54,25 @@ export default function PaginatorComponent({
|
|||
</Select>
|
||||
</div>
|
||||
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||
Page {currentPage} of {maxIndex}
|
||||
Page {pageIndex} of {maxIndex}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
disabled={index <= 0}
|
||||
disabled={pageIndex <= 1}
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => {
|
||||
setPageIndex(0);
|
||||
setCurrentPage(1);
|
||||
paginate(size, 0);
|
||||
paginate(size, 1);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<IconComponent name="ChevronsLeft" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={index <= 0}
|
||||
disabled={pageIndex <= 1}
|
||||
onClick={() => {
|
||||
if (index > 0) {
|
||||
const pgIndex = size - index;
|
||||
setCurrentPage(currentPage - 1);
|
||||
setPageIndex(pgIndex);
|
||||
paginate(size, pgIndex);
|
||||
if (pageIndex > 0) {
|
||||
paginate(size, pageIndex - 1);
|
||||
}
|
||||
}}
|
||||
variant="outline"
|
||||
|
|
@ -88,12 +82,9 @@ export default function PaginatorComponent({
|
|||
<IconComponent name="ChevronLeft" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={currentPage === maxIndex}
|
||||
disabled={pageIndex === maxIndex}
|
||||
onClick={() => {
|
||||
const pgIndex = size + index;
|
||||
setPageIndex(pgIndex);
|
||||
setCurrentPage(currentPage + 1);
|
||||
paginate(size, pgIndex);
|
||||
paginate(size, pageIndex + 1);
|
||||
}}
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
|
|
@ -102,13 +93,11 @@ export default function PaginatorComponent({
|
|||
<IconComponent name="ChevronRight" className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
disabled={currentPage === maxIndex}
|
||||
disabled={pageIndex === maxIndex}
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => {
|
||||
setPageIndex(maxIndex - 1);
|
||||
setCurrentPage(maxIndex);
|
||||
paginate(size, size);
|
||||
paginate(size, maxIndex);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ export default function CodeTabsComponent({
|
|||
key={idx} // Remember to add a unique key prop
|
||||
>
|
||||
{idx < 4 ? (
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<div className="flex h-full w-full flex-col">
|
||||
{tab.description && (
|
||||
<div
|
||||
className="mb-2 w-full text-left text-sm"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { gradients } from "../../utils/styleUtils";
|
||||
|
||||
export default function GradientChooserComponent({ value, onChange }) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-center gap-4">
|
||||
{gradients.map((gradient, idx) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
onChange(gradient);
|
||||
}}
|
||||
className={
|
||||
"duration-400 h-12 w-12 cursor-pointer rounded-full transition-all " +
|
||||
gradient +
|
||||
(value === gradient ? " shadow-lg ring-2 ring-primary" : "")
|
||||
}
|
||||
key={idx}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ export default function Header(): JSX.Element {
|
|||
const { notificationCenter } = useContext(alertContext);
|
||||
const location = useLocation();
|
||||
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
|
||||
const { stars } = useContext(darkContext);
|
||||
const { stars, gradientIndex } = useContext(darkContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
|
|
@ -139,9 +139,7 @@ export default function Header(): JSX.Element {
|
|||
<button
|
||||
className={
|
||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||
gradients[
|
||||
parseInt(userData?.id ?? "", 10) % gradients.length
|
||||
]
|
||||
(userData?.profile_image ?? gradients[gradientIndex])
|
||||
}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
@ -156,6 +154,12 @@ export default function Header(): JSX.Element {
|
|||
Admin Page
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => navigate("/account/settings")}
|
||||
>
|
||||
Profile Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ export default function InputListComponent({
|
|||
}
|
||||
}, [disabled]);
|
||||
|
||||
// @TODO Recursive Character Text Splitter - the value might be in string format, whereas the InputListComponent specifically requires an array format. To ensure smooth operation and prevent potential errors, it's crucial that we handle the conversion from a string to an array with the string as its element.
|
||||
typeof value === "string" ? (value = [value]) : (value = value);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ export default function LoadingComponent({
|
|||
remSize,
|
||||
}: LoadingComponentProps): JSX.Element {
|
||||
return (
|
||||
<div role="status" className="m-auto w-min">
|
||||
<div role="status" className="flex flex-col items-center justify-center">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={`w-${remSize} h-${remSize} mr-2 animate-spin fill-almost-medium-blue text-muted`}
|
||||
className={`w-${remSize} h-${remSize} animate-spin fill-almost-medium-blue text-muted`}
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import * as React from "react";
|
||||
import { cn } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ const DialogOverlay = React.forwardRef<
|
|||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed top-0 right-0 left-0 bottom-0 overflow-auto inset-0 z-50 bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"fixed inset-0 bottom-0 left-0 right-0 top-0 z-50 overflow-auto bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -44,14 +44,14 @@ const DialogContent = React.forwardRef<
|
|||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"noundo nocopy flex text-start overflow-auto z-50 justify-center items-left flex-col w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-bottom-[48%] data-[state=open]:slide-in-from-bottom-[48%] sm:rounded-lg md:w-full",
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<IconComponent name="X" className="h-4 w-4" />
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
|
|
|
|||
|
|
@ -503,6 +503,21 @@ export const NOUNS: string[] = [
|
|||
*/
|
||||
export const USER_PROJECTS_HEADER = "My Collection";
|
||||
|
||||
/**
|
||||
* Header text for admin page
|
||||
* @constant
|
||||
*
|
||||
*/
|
||||
export const ADMIN_HEADER_TITLE = "Admin Page";
|
||||
|
||||
/**
|
||||
* Header description for admin page
|
||||
* @constant
|
||||
*
|
||||
*/
|
||||
export const ADMIN_HEADER_DESCRIPTION =
|
||||
"Navigate through this section to efficiently oversee all application users. From here, you can seamlessly manage user accounts.";
|
||||
|
||||
/**
|
||||
* URLs excluded from error retries.
|
||||
* @constant
|
||||
|
|
@ -523,6 +538,12 @@ export const CONTROL_INPUT_STATE = {
|
|||
username: "",
|
||||
};
|
||||
|
||||
export const CONTROL_PATCH_USER_STATE = {
|
||||
password: "",
|
||||
cnfPassword: "",
|
||||
gradient: "",
|
||||
};
|
||||
|
||||
export const CONTROL_LOGIN_STATE = {
|
||||
username: "",
|
||||
password: "",
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
.then((user) => {
|
||||
setUserData(user);
|
||||
setLoading(false);
|
||||
const isSuperUser = user.is_superuser;
|
||||
const isSuperUser = user!.is_superuser;
|
||||
setIsAdmin(isSuperUser);
|
||||
})
|
||||
.catch((error) => {});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ const initialValue = {
|
|||
setDark: () => {},
|
||||
stars: 0,
|
||||
setStars: (stars) => 0,
|
||||
gradientIndex: 0,
|
||||
setGradientIndex: () => 0,
|
||||
};
|
||||
|
||||
export const darkContext = createContext<darkContextType>(initialValue);
|
||||
|
|
@ -16,6 +18,7 @@ export function DarkProvider({ children }) {
|
|||
JSON.parse(window.localStorage.getItem("isDark")!) ?? false
|
||||
);
|
||||
const [stars, setStars] = useState<number>(0);
|
||||
const [gradientIndex, setGradientIndex] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchStars() {
|
||||
|
|
@ -23,6 +26,9 @@ export function DarkProvider({ children }) {
|
|||
setStars(starsCount);
|
||||
}
|
||||
fetchStars();
|
||||
const min = 0;
|
||||
const max = 30;
|
||||
setGradientIndex(Math.floor(Math.random() * (max - min + 1)) + min);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -41,6 +47,8 @@ export function DarkProvider({ children }) {
|
|||
stars,
|
||||
dark,
|
||||
setDark,
|
||||
setGradientIndex,
|
||||
gradientIndex,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -238,7 +238,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
function hardReset() {
|
||||
newNodeId.current = uid();
|
||||
setTabId("");
|
||||
|
||||
setFlows([]);
|
||||
setIsLoading(true);
|
||||
setId(uid());
|
||||
|
|
|
|||
|
|
@ -33,7 +33,10 @@ function ApiInterceptor() {
|
|||
}
|
||||
|
||||
const res = await renewAccessToken(refreshToken);
|
||||
login(res?.data?.access_token, res?.data?.refresh_token);
|
||||
if (res?.data?.access_token && res?.data?.refresh_token) {
|
||||
login(res?.data?.access_token, res?.data?.refresh_token);
|
||||
}
|
||||
|
||||
try {
|
||||
if (error?.config?.headers) {
|
||||
delete error.config.headers["Authorization"];
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import {
|
|||
APIObjectType,
|
||||
LoginType,
|
||||
Users,
|
||||
changeUser,
|
||||
resetPasswordType,
|
||||
sendAllProps,
|
||||
} from "../../types/api/index";
|
||||
import { UserInputType } from "../../types/components";
|
||||
|
|
@ -393,16 +395,18 @@ export async function autoLogin() {
|
|||
|
||||
export async function renewAccessToken(token: string) {
|
||||
try {
|
||||
return await api.post(`${BASE_URL_API}refresh?token=${token}`);
|
||||
if (token) {
|
||||
return await api.post(`${BASE_URL_API}refresh?token=${token}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLoggedUser(): Promise<Users> {
|
||||
export async function getLoggedUser(): Promise<Users | null> {
|
||||
try {
|
||||
const res = await api.get(`${BASE_URL_API}user`);
|
||||
const res = await api.get(`${BASE_URL_API}users/whoami`);
|
||||
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
|
|
@ -411,14 +415,16 @@ export async function getLoggedUser(): Promise<Users> {
|
|||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function addUser(user: UserInputType): Promise<Users> {
|
||||
export async function addUser(user: UserInputType): Promise<Array<Users>> {
|
||||
try {
|
||||
const res = await api.post(`${BASE_URL_API}user`, user);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
const res = await api.post(`${BASE_URL_API}users/`, user);
|
||||
if (res.status !== 201) {
|
||||
throw new Error(res.data.detail);
|
||||
}
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
|
|
@ -428,10 +434,10 @@ export async function addUser(user: UserInputType): Promise<Users> {
|
|||
export async function getUsersPage(
|
||||
skip: number,
|
||||
limit: number
|
||||
): Promise<[Users]> {
|
||||
): Promise<Array<Users>> {
|
||||
try {
|
||||
const res = await api.get(
|
||||
`${BASE_URL_API}users?skip=${skip}&limit=${limit}`
|
||||
`${BASE_URL_API}users/?skip=${skip}&limit=${limit}`
|
||||
);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
|
|
@ -440,11 +446,12 @@ export async function getUsersPage(
|
|||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function deleteUser(user_id: string) {
|
||||
try {
|
||||
const res = await api.delete(`${BASE_URL_API}user/${user_id}`);
|
||||
const res = await api.delete(`${BASE_URL_API}users/${user_id}`);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
|
|
@ -454,9 +461,24 @@ export async function deleteUser(user_id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function updateUser(user_id: string, user: Users) {
|
||||
export async function updateUser(user_id: string, user: changeUser) {
|
||||
try {
|
||||
const res = await api.patch(`${BASE_URL_API}user/${user_id}`, user);
|
||||
const res = await api.patch(`${BASE_URL_API}users/${user_id}`, user);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetPassword(user_id: string, user: resetPasswordType) {
|
||||
try {
|
||||
const res = await api.patch(
|
||||
`${BASE_URL_API}users/${user_id}/reset-password`,
|
||||
user
|
||||
);
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,39 @@
|
|||
import { Infinity } from "lucide-react";
|
||||
import { forwardRef } from "react";
|
||||
|
||||
const GradientSparkles = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
|
||||
(props, ref) => {
|
||||
return (
|
||||
<>
|
||||
<svg width="0" height="0" style={{ position: "absolute" }}>
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop className="gradient-start" offset="0%" />
|
||||
<stop className="gradient-end" offset="100%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<Infinity stroke="url(#grad1)" ref={ref} {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default GradientSparkles;
|
||||
export const GradientSparkles = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<>
|
||||
<svg width="0" height="0" style={{ position: "absolute" }}>
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop className="gradient-start" offset="0%" />
|
||||
<stop className="gradient-end" offset="100%" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
{/* this svg comes from the source code of lucide,
|
||||
we do not use the import because it crashes the ui (why? no one knows...).
|
||||
source code from the used svg:
|
||||
https://github.com/lucide-icons/lucide/blob/a4076db69b52ff0debc383f76d4d671c3bad5345/icons/infinity.svg?short_path=f79de91
|
||||
*/}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke="url(#grad1)"
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
<path d="M12 12c-2-2.67-4-4-6-4a4 4 0 1 0 0 8c2 0 4-1.33 6-4Zm0 0c2 2.67 4 4 6 4a4 4 0 0 0 0-8c-2 0-4 1.33-6 4Z" />
|
||||
</svg>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,11 +33,9 @@ const ApiModal = forwardRef(
|
|||
{
|
||||
flow,
|
||||
children,
|
||||
disable,
|
||||
}: {
|
||||
flow: FlowType;
|
||||
children: ReactNode;
|
||||
disable: boolean;
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
|
|
@ -201,8 +199,8 @@ const ApiModal = forwardRef(
|
|||
}
|
||||
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen} disable={disable}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
|
||||
<span className="pr-2">Code</span>
|
||||
<IconComponent
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import BaseModal from "../baseModal";
|
|||
|
||||
export default function ConfirmationModal({
|
||||
title,
|
||||
asChild,
|
||||
titleHeader,
|
||||
modalContent,
|
||||
modalContentTitle,
|
||||
|
|
@ -22,7 +23,7 @@ export default function ConfirmationModal({
|
|||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<BaseModal size="x-small" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Trigger asChild={asChild}>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={titleHeader}>
|
||||
<span className="pr-2">{title}</span>
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export default function UserManagementModal({
|
|||
data,
|
||||
index,
|
||||
onConfirm,
|
||||
asChild,
|
||||
}: UserManagementType) {
|
||||
const Icon: any = nodeIconsLucide[icon];
|
||||
const [pwdVisible, setPwdVisible] = useState(false);
|
||||
|
|
@ -60,7 +61,7 @@ export default function UserManagementModal({
|
|||
|
||||
return (
|
||||
<BaseModal size="medium-h-full" open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Trigger asChild={asChild}>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={titleHeader}>
|
||||
<span className="pr-2">{title}</span>
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -14,13 +14,25 @@ import { modalHeaderType } from "../../types/components";
|
|||
type ContentProps = { children: ReactNode };
|
||||
type HeaderProps = { children: ReactNode; description: string };
|
||||
type FooterProps = { children: ReactNode };
|
||||
type TriggerProps = { children: ReactNode };
|
||||
type TriggerProps = {
|
||||
children: ReactNode;
|
||||
asChild?: boolean;
|
||||
disable?: boolean;
|
||||
};
|
||||
|
||||
const Content: React.FC<ContentProps> = ({ children }) => {
|
||||
return <div className="h-full w-full">{children}</div>;
|
||||
};
|
||||
const Trigger: React.FC<ContentProps> = ({ children }) => {
|
||||
return <>{children}</>;
|
||||
const Trigger: React.FC<TriggerProps> = ({ children, asChild, disable }) => {
|
||||
return (
|
||||
<DialogTrigger
|
||||
className={asChild ? "" : "w-full"}
|
||||
hidden={children ? false : true}
|
||||
asChild={asChild}
|
||||
>
|
||||
{children}
|
||||
</DialogTrigger>
|
||||
);
|
||||
};
|
||||
|
||||
const Header: React.FC<{ children: ReactNode; description: string | null }> = ({
|
||||
|
|
@ -47,7 +59,6 @@ interface BaseModalProps {
|
|||
];
|
||||
open?: boolean;
|
||||
setOpen?: (open: boolean) => void;
|
||||
disable?: boolean;
|
||||
size?:
|
||||
| "x-small"
|
||||
| "smaller"
|
||||
|
|
@ -61,7 +72,6 @@ interface BaseModalProps {
|
|||
function BaseModal({
|
||||
open,
|
||||
setOpen,
|
||||
disable = false,
|
||||
children,
|
||||
size = "large",
|
||||
}: BaseModalProps) {
|
||||
|
|
@ -120,17 +130,12 @@ function BaseModal({
|
|||
//UPDATE COLORS AND STYLE CLASSSES
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger
|
||||
className={"w-full " + (disable ? "button-disable" : "")}
|
||||
hidden={triggerChild ? false : true}
|
||||
>
|
||||
{triggerChild}
|
||||
</DialogTrigger>
|
||||
{triggerChild}
|
||||
<DialogContent className={minWidth}>
|
||||
<div className="truncate-doubleline word-break-break-word">
|
||||
{headerChild}
|
||||
</div>
|
||||
<div className={`mt-2 flex flex-col ${height} w-full `}>
|
||||
<div className={`mt-2 flex flex-col ${height!} w-full `}>
|
||||
{ContentChild}
|
||||
</div>
|
||||
{ContentFooter && (
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ export default function CodeAreaModal({
|
|||
const { dark } = useContext(darkContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const [height, setHeight] = useState<string | null>(null);
|
||||
const { setErrorData, setSuccessData } =
|
||||
useContext(alertContext);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const [error, setError] = useState<{
|
||||
detail: { error: string | undefined; traceback: string | undefined };
|
||||
} | null>(null);
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ import BaseModal from "../baseModal";
|
|||
|
||||
const ExportModal = forwardRef(
|
||||
(props: { children: ReactNode }, ref): JSX.Element => {
|
||||
const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
|
||||
const { flows, tabId, downloadFlow } = useContext(TabsContext);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const flow = flows.find((f) => f.id === tabId);
|
||||
useEffect(() => {
|
||||
setName(flow.name);
|
||||
setDescription(flow.description);
|
||||
}, [flow.name, flow.description]);
|
||||
const [name, setName] = useState(flow.name);
|
||||
const [description, setDescription] = useState(flow.description);
|
||||
setName(flow!.name);
|
||||
setDescription(flow!.description);
|
||||
}, [flow!.name, flow!.description]);
|
||||
const [name, setName] = useState(flow!.name);
|
||||
const [description, setDescription] = useState(flow!.description);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
|
@ -40,7 +40,6 @@ const ExportModal = forwardRef(
|
|||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
updateFlow={updateFlow}
|
||||
/>
|
||||
<div className="mt-3 flex items-center space-x-2">
|
||||
<Checkbox
|
||||
|
|
|
|||
|
|
@ -194,7 +194,8 @@ export default function FormModal({
|
|||
}
|
||||
|
||||
function handleWsMessage(data: any) {
|
||||
if (Array.isArray(data)) {
|
||||
console.log(data);
|
||||
if (Array.isArray(data) && data.length > 0) {
|
||||
//set chat history
|
||||
setChatHistory((_) => {
|
||||
let newChatHistory: ChatMessageType[] = [];
|
||||
|
|
@ -313,7 +314,7 @@ export default function FormModal({
|
|||
}
|
||||
};
|
||||
// do not add connectWS on dependencies array
|
||||
}, []);
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ export default function GenericModal({
|
|||
setNoticeData({
|
||||
title: "Your template does not have any variables.",
|
||||
});
|
||||
setModalOpen(false);
|
||||
} else {
|
||||
setIsEdit(false);
|
||||
setSuccessData({
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default function LoginAdminPage() {
|
|||
}
|
||||
|
||||
function getUser() {
|
||||
if (getAuthentication) {
|
||||
if (getAuthentication()) {
|
||||
setTimeout(() => {
|
||||
getLoggedUser()
|
||||
.then((user) => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import { X } from "lucide-react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import LoadingComponent from "../../components/loadingComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
|
@ -16,8 +16,13 @@ import {
|
|||
TableHeader,
|
||||
TableRow,
|
||||
} from "../../components/ui/table";
|
||||
import {
|
||||
ADMIN_HEADER_DESCRIPTION,
|
||||
ADMIN_HEADER_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import {
|
||||
addUser,
|
||||
deleteUser,
|
||||
|
|
@ -33,12 +38,19 @@ export default function AdminPage() {
|
|||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const [size, setPageSize] = useState(10);
|
||||
const [index, setPageIndex] = useState(0);
|
||||
const [index, setPageIndex] = useState(1);
|
||||
const [loadingUsers, setLoadingUsers] = useState(true);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const { userData } = useContext(AuthContext);
|
||||
const [totalRowsCount, setTotalRowsCount] = useState(0);
|
||||
|
||||
const { setTabId } = useContext(TabsContext);
|
||||
|
||||
// set null id
|
||||
useEffect(() => {
|
||||
setTabId("");
|
||||
}, []);
|
||||
|
||||
const userList = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -65,7 +77,9 @@ export default function AdminPage() {
|
|||
|
||||
function handleChangePagination(pageIndex: number, pageSize: number) {
|
||||
setLoadingUsers(true);
|
||||
getUsersPage(pageIndex, pageSize)
|
||||
setPageSize(pageSize);
|
||||
setPageIndex(pageIndex);
|
||||
getUsersPage(pageSize * (pageIndex - 1), pageSize)
|
||||
.then((users) => {
|
||||
setTotalRowsCount(users["total_count"]);
|
||||
userList.current = users["users"];
|
||||
|
|
@ -78,7 +92,7 @@ export default function AdminPage() {
|
|||
}
|
||||
|
||||
function resetFilter() {
|
||||
setPageIndex(0);
|
||||
setPageIndex(1);
|
||||
setPageSize(10);
|
||||
getUsers();
|
||||
}
|
||||
|
|
@ -168,270 +182,264 @@ export default function AdminPage() {
|
|||
function handleNewUser(user: UserInputType) {
|
||||
addUser(user)
|
||||
.then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! New user added!",
|
||||
updateUser(res["id"], {
|
||||
is_active: user.is_active,
|
||||
is_superuser: user.is_superuser,
|
||||
}).then((res) => {
|
||||
resetFilter();
|
||||
setSuccessData({
|
||||
title: "Success! New user added!",
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Error on add new user",
|
||||
list: [error["response"]["data"]["detail"]],
|
||||
title: "Error when adding new user",
|
||||
list: [error.response.data.detail],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<Header />
|
||||
{userData && (
|
||||
<div className="main-page-panel">
|
||||
<div className="m-auto flex h-full flex-row justify-center">
|
||||
<div className="basis-5/6">
|
||||
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
|
||||
<div className="flex items-center justify-between space-y-2">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold tracking-tight">
|
||||
Welcome back!
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Navigate through this section to efficiently oversee all
|
||||
application users. From here, you can seamlessly manage
|
||||
user accounts.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2"></div>
|
||||
</div>
|
||||
|
||||
{userList.current.length === 0 && !loadingUsers && (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2>There's no users registered :)</h2>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<Input
|
||||
value={inputValue}
|
||||
placeholder="Filter users..."
|
||||
className="h-8 w-[150px] lg:w-[250px]"
|
||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||
/>
|
||||
{inputValue.length > 0 && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setInputValue("");
|
||||
setFilterUserList(userList.current);
|
||||
}}
|
||||
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);
|
||||
}}
|
||||
>
|
||||
<Button>New User</Button>
|
||||
</UserManagementModal>
|
||||
</div>
|
||||
</div>
|
||||
{loadingUsers && (
|
||||
<div>
|
||||
<strong>Loading...</strong>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||
(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">Active</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>
|
||||
{filterUserList.map(
|
||||
(user: UserInputType, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2 font-medium">
|
||||
<ShadTooltip content={user.id}>
|
||||
<span className="cursor-default">
|
||||
{user.id}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDisableUser(
|
||||
user.is_active,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
checked={user.is_active}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleSuperUserEdit(
|
||||
user.is_superuser,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_superuser"
|
||||
checked={user.is_superuser}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</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 className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
<UserManagementModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.id}`}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, editUser) => {
|
||||
handleEditUser(user.id, editUser);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</UserManagementModal>
|
||||
|
||||
<ConfirmationModal
|
||||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip
|
||||
content="Delete"
|
||||
side="top"
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<PaginatorComponent
|
||||
pageIndex={index}
|
||||
pageSize={size}
|
||||
totalRowsCount={totalRowsCount}
|
||||
paginate={(pageIndex, pageSize) => {
|
||||
handleChangePagination(pageSize, pageIndex);
|
||||
}}
|
||||
></PaginatorComponent>
|
||||
</>
|
||||
<Header />
|
||||
{userData && (
|
||||
<div className="admin-page-panel flex h-full flex-col pb-8">
|
||||
<div className="main-page-nav-arrangement">
|
||||
<span className="main-page-nav-title">
|
||||
<IconComponent name="Shield" className="w-6" />
|
||||
{ADMIN_HEADER_TITLE}
|
||||
</span>
|
||||
</div>
|
||||
<span className="admin-page-description-text">
|
||||
{ADMIN_HEADER_DESCRIPTION}
|
||||
</span>
|
||||
<div className="flex w-full justify-between px-4">
|
||||
<div className="flex w-96 items-center gap-4">
|
||||
<Input
|
||||
placeholder="Search Username"
|
||||
value={inputValue}
|
||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||
/>
|
||||
{inputValue.length > 0 ? (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
setInputValue("");
|
||||
setFilterUserList(userList.current);
|
||||
}}
|
||||
>
|
||||
<IconComponent name="X" className="w-6 text-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<IconComponent
|
||||
name="Search"
|
||||
className="w-6 text-foreground"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<UserManagementModal
|
||||
title="New User"
|
||||
titleHeader={"Add a new user"}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
onConfirm={(index, user) => {
|
||||
handleNewUser(user);
|
||||
}}
|
||||
asChild
|
||||
>
|
||||
<Button variant="primary">New User</Button>
|
||||
</UserManagementModal>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{loadingUsers ? (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<LoadingComponent remSize={12} />
|
||||
</div>
|
||||
) : userList.current.length === 0 ? (
|
||||
<>
|
||||
<div className="m-4 flex items-center justify-between text-sm">
|
||||
No users registered.
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
"m-4 h-full overflow-x-hidden overflow-y-scroll rounded-md border-2 bg-background custom-scroll" +
|
||||
(loadingUsers ? " border-0" : "")
|
||||
}
|
||||
>
|
||||
<Table className={"table-fixed 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">Active</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>
|
||||
{filterUserList.map((user: UserInputType, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2 font-medium">
|
||||
<ShadTooltip content={user.id}>
|
||||
<span className="cursor-default">{user.id}</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-1 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
asChild
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDisableUser(
|
||||
user.is_active,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-fit">
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
checked={user.is_active}
|
||||
/>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-1 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
asChild
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleSuperUserEdit(
|
||||
user.is_superuser,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-fit">
|
||||
<Checkbox
|
||||
id="is_superuser"
|
||||
checked={user.is_superuser}
|
||||
/>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
</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 className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
<UserManagementModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.id}`}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, editUser) => {
|
||||
handleEditUser(user.id, editUser);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</UserManagementModal>
|
||||
|
||||
<ConfirmationModal
|
||||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Delete" side="top">
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<PaginatorComponent
|
||||
pageIndex={index}
|
||||
pageSize={size}
|
||||
totalRowsCount={totalRowsCount}
|
||||
paginate={(pageSize, pageIndex) => {
|
||||
handleChangePagination(pageIndex, pageSize);
|
||||
}}
|
||||
></PaginatorComponent>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useNavigate } from "react-router-dom";
|
|||
import { CardComponent } from "../../components/cardComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||
import { getExamples } from "../../controllers/API";
|
||||
import { FlowType } from "../../types/flow";
|
||||
export default function CommunityPage(): JSX.Element {
|
||||
|
|
@ -74,7 +75,14 @@ export default function CommunityPage(): JSX.Element {
|
|||
new and powerful features.
|
||||
</span>
|
||||
<div className="community-pages-flows-panel">
|
||||
{!loadingExamples &&
|
||||
{loadingExamples ? (
|
||||
<>
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
</>
|
||||
) : (
|
||||
examples.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
|
|
@ -99,7 +107,8 @@ export default function CommunityPage(): JSX.Element {
|
|||
</Button>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -298,7 +298,14 @@ export default function Page({
|
|||
setNodes((nds) => nds.concat(newNode));
|
||||
} else if (event.dataTransfer.types.some((types) => types === "Files")) {
|
||||
takeSnapshot();
|
||||
uploadFlow(false, event.dataTransfer.files.item(0)!);
|
||||
if (event.dataTransfer.files.item(0)!.type === "application/json") {
|
||||
uploadFlow(true, event.dataTransfer.files.item(0)!);
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Invalid file type",
|
||||
list: ["Please upload a JSON file"],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
// Specify dependencies for useCallback
|
||||
|
|
|
|||
|
|
@ -99,16 +99,20 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
<ShadTooltip content={"Code"} side="top">
|
||||
<div className="side-bar-button">
|
||||
{flow && flow.data && (
|
||||
<ApiModal flow={flow} disable={!isBuilt}>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isBuilt ? " " : " extra-side-bar-save-disable")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<ApiModal flow={flow}>
|
||||
<button
|
||||
className={"w-full " + (!isBuilt ? "button-disable" : "")}
|
||||
>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className={
|
||||
"side-bar-button-size" +
|
||||
(isBuilt ? " " : " extra-side-bar-save-disable")
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</ApiModal>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useContext, useEffect } from "react";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||
import { CardComponent } from "../../components/cardComponent";
|
||||
|
|
@ -7,6 +7,7 @@ import Header from "../../components/headerComponent";
|
|||
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
export default function HomePage(): JSX.Element {
|
||||
const {
|
||||
|
|
@ -19,6 +20,7 @@ export default function HomePage(): JSX.Element {
|
|||
uploadFlow,
|
||||
isLoading,
|
||||
} = useContext(TabsContext);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const dropdownOptions = [
|
||||
{
|
||||
name: "Import from JSON",
|
||||
|
|
@ -35,9 +37,36 @@ export default function HomePage(): JSX.Element {
|
|||
}, []);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(isLoading);
|
||||
}, [isLoading]);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
const dragOver = (e) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
const dragEnter = (e) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
const dragLeave = () => {
|
||||
setIsDragging(false);
|
||||
};
|
||||
|
||||
const fileDrop = (e) => {
|
||||
e.preventDefault();
|
||||
setIsDragging(false);
|
||||
if (e.dataTransfer.types.some((types) => types === "Files")) {
|
||||
if (e.dataTransfer.files.item(0).type === "application/json") {
|
||||
uploadFlow(true, e.dataTransfer.files.item(0)!);
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Invalid file type",
|
||||
list: ["Please upload a JSON file"],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Personal flows display
|
||||
return (
|
||||
|
|
@ -82,40 +111,63 @@ export default function HomePage(): JSX.Element {
|
|||
<span className="main-page-description-text">
|
||||
Manage your personal projects. Download or upload your collection.
|
||||
</span>
|
||||
<div className="main-page-flows-display">
|
||||
{isLoading && flows.length == 0 ? (
|
||||
<div
|
||||
onDragOver={dragOver}
|
||||
onDragEnter={dragEnter}
|
||||
onDragLeave={dragLeave}
|
||||
onDrop={fileDrop}
|
||||
className={
|
||||
"h-full w-full " +
|
||||
(isDragging
|
||||
? "mb-24 flex flex-col items-center justify-center gap-4 text-2xl font-light"
|
||||
: "")
|
||||
}
|
||||
>
|
||||
{isDragging ? (
|
||||
<>
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<IconComponent
|
||||
name="ArrowUpToLine"
|
||||
className="h-12 w-12 stroke-1"
|
||||
/>
|
||||
Drop your flow here
|
||||
</>
|
||||
) : (
|
||||
flows.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Link to={"/flow/" + flow.id}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
onDelete={() => {
|
||||
removeFlow(flow.id);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
<div className="main-page-flows-display">
|
||||
{isLoading && flows.length == 0 ? (
|
||||
<>
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
<SkeletonCardComponent />
|
||||
</>
|
||||
) : (
|
||||
flows.map((flow, idx) => (
|
||||
<CardComponent
|
||||
key={idx}
|
||||
flow={flow}
|
||||
id={flow.id}
|
||||
button={
|
||||
<Link to={"/flow/" + flow.id}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="whitespace-nowrap "
|
||||
>
|
||||
<IconComponent
|
||||
name="ExternalLink"
|
||||
className="main-page-nav-button"
|
||||
/>
|
||||
Edit Flow
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
onDelete={() => {
|
||||
removeFlow(flow.id);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
173
src/frontend/src/pages/ProfileSettingsPage/index.tsx
Normal file
173
src/frontend/src/pages/ProfileSettingsPage/index.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import GradientChooserComponent from "../../components/gradientChooserComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { CONTROL_PATCH_USER_STATE } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { resetPassword, updateUser } from "../../controllers/API";
|
||||
import {
|
||||
inputHandlerEventType,
|
||||
patchUserInputStateType,
|
||||
} from "../../types/components";
|
||||
import { gradients } from "../../utils/styleUtils";
|
||||
export default function ProfileSettingsPage(): JSX.Element {
|
||||
const { setTabId } = useContext(TabsContext);
|
||||
|
||||
const [inputState, setInputState] = useState<patchUserInputStateType>(
|
||||
CONTROL_PATCH_USER_STATE
|
||||
);
|
||||
|
||||
// set null id
|
||||
useEffect(() => {
|
||||
setTabId("");
|
||||
}, []);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const { userData, setUserData } = useContext(AuthContext);
|
||||
const { password, cnfPassword, gradient } = inputState;
|
||||
|
||||
async function handlePatchUser() {
|
||||
if (password !== cnfPassword) {
|
||||
setErrorData({
|
||||
title: "Error changing password",
|
||||
list: ["Passwords do not match"],
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (password !== "") await resetPassword(userData!.id, { password });
|
||||
if (gradient !== "")
|
||||
await updateUser(userData!.id, { profile_image: gradient });
|
||||
if (gradient !== "") {
|
||||
let newUserData = cloneDeep(userData);
|
||||
newUserData!.profile_image = gradient;
|
||||
|
||||
setUserData(newUserData);
|
||||
}
|
||||
handleInput({ target: { name: "password", value: "" } });
|
||||
handleInput({ target: { name: "cnfPassword", value: "" } });
|
||||
setSuccessData({ title: "Changes saved successfully!" });
|
||||
} catch (error) {
|
||||
setErrorData({
|
||||
title: "Error saving changes",
|
||||
list: [(error as any).response.data.detail],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleInput({
|
||||
target: { name, value },
|
||||
}: inputHandlerEventType): void {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
|
||||
<div className="community-page-arrangement">
|
||||
<div className="community-page-nav-arrangement">
|
||||
<span className="community-page-nav-title">
|
||||
<IconComponent name="User" className="w-6" />
|
||||
Profile Settings
|
||||
</span>
|
||||
</div>
|
||||
<span className="community-page-description-text">
|
||||
Change your profile settings like your password and your profile
|
||||
picture.
|
||||
</span>
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
handlePatchUser();
|
||||
const data = Object.fromEntries(new FormData(event.currentTarget));
|
||||
event.preventDefault();
|
||||
}}
|
||||
className="flex h-full flex-col px-6 pb-16"
|
||||
>
|
||||
<div className="flex h-full flex-col gap-4">
|
||||
<div className="flex gap-4">
|
||||
<div className="mb-3 w-96">
|
||||
<Form.Field name="password">
|
||||
<Form.Label className="data-[invalid]:label-invalid">
|
||||
Password{" "}
|
||||
</Form.Label>
|
||||
<InputComponent
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "password", value } });
|
||||
}}
|
||||
value={password}
|
||||
isForm
|
||||
password={true}
|
||||
placeholder="Password"
|
||||
className="w-full"
|
||||
/>
|
||||
<Form.Message match="valueMissing" className="field-invalid">
|
||||
Please enter your password
|
||||
</Form.Message>
|
||||
</Form.Field>
|
||||
</div>
|
||||
<div className="mb-3 w-96">
|
||||
<Form.Field name="cnfPassword">
|
||||
<Form.Label className="data-[invalid]:label-invalid">
|
||||
Confirm Password{" "}
|
||||
</Form.Label>
|
||||
|
||||
<InputComponent
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "cnfPassword", value } });
|
||||
}}
|
||||
value={cnfPassword}
|
||||
isForm
|
||||
password={true}
|
||||
placeholder="Confirm Password"
|
||||
className="w-full"
|
||||
/>
|
||||
|
||||
<Form.Message className="field-invalid" match="valueMissing">
|
||||
Please confirm your password
|
||||
</Form.Message>
|
||||
</Form.Field>
|
||||
</div>
|
||||
</div>
|
||||
<Form.Field name="gradient">
|
||||
<Form.Label className="data-[invalid]:label-invalid">
|
||||
Profile Gradient{" "}
|
||||
</Form.Label>
|
||||
|
||||
<div className="mt-4 w-[1010px]">
|
||||
<GradientChooserComponent
|
||||
value={
|
||||
gradient == ""
|
||||
? userData!.profile_image ??
|
||||
gradients[
|
||||
parseInt(userData!.id ?? "", 30) % gradients.length
|
||||
]
|
||||
: gradient
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "gradient", value } });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Form.Field>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-end">
|
||||
<div className="w-32">
|
||||
<Form.Submit asChild>
|
||||
<Button className="mr-3 mt-6 w-full" type="submit">
|
||||
Save
|
||||
</Button>
|
||||
</Form.Submit>
|
||||
</div>
|
||||
</div>
|
||||
</Form.Root>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ export default function LoginPage(): JSX.Element {
|
|||
setTimeout(() => {
|
||||
getLoggedUser()
|
||||
.then((user) => {
|
||||
const isSuperUser = user.is_superuser;
|
||||
const isSuperUser = user!.is_superuser;
|
||||
setIsAdmin(isSuperUser);
|
||||
setUserData(user);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import ApiKeysPage from "./pages/ApiKeysPage";
|
|||
import CommunityPage from "./pages/CommunityPage";
|
||||
import FlowPage from "./pages/FlowPage";
|
||||
import HomePage from "./pages/MainPage";
|
||||
import ProfileSettingsPage from "./pages/ProfileSettingsPage";
|
||||
import ViewPage from "./pages/ViewPage";
|
||||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||
import LoginPage from "./pages/loginPage";
|
||||
|
|
@ -95,6 +96,14 @@ const Router = () => {
|
|||
/>
|
||||
|
||||
<Route path="/account">
|
||||
<Route
|
||||
path="settings"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<ProfileSettingsPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="delete"
|
||||
element={
|
||||
|
|
|
|||
|
|
@ -212,6 +212,10 @@
|
|||
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
||||
}
|
||||
|
||||
.admin-page-panel {
|
||||
@apply flex-max-width h-full flex-col overflow-auto bg-muted px-16;
|
||||
}
|
||||
|
||||
.main-page-nav-arrangement {
|
||||
@apply flex-max-width justify-between px-6 py-12 pb-2;
|
||||
}
|
||||
|
|
@ -228,6 +232,10 @@
|
|||
@apply flex w-[60%] px-6 pb-14 text-muted-foreground;
|
||||
}
|
||||
|
||||
.admin-page-description-text {
|
||||
@apply flex w-[80%] px-6 pb-8 text-muted-foreground;
|
||||
}
|
||||
|
||||
.main-page-flows-display {
|
||||
@apply grid w-full gap-4 p-4 md:grid-cols-2 lg:grid-cols-4;
|
||||
}
|
||||
|
|
@ -303,7 +311,7 @@
|
|||
@apply hover:text-accent-foreground hover:transition-all;
|
||||
}
|
||||
.generic-node-desc {
|
||||
@apply h-full w-full py-5 text-foreground;
|
||||
@apply h-full w-full text-foreground;
|
||||
}
|
||||
.generic-node-desc-text {
|
||||
@apply w-full px-5 pb-3 text-sm text-muted-foreground;
|
||||
|
|
|
|||
|
|
@ -78,11 +78,25 @@ export type LoginAuthType = {
|
|||
token_type?: string;
|
||||
};
|
||||
|
||||
export type changeUser = {
|
||||
username?: string;
|
||||
is_active?: boolean;
|
||||
is_superuser?: boolean;
|
||||
password?: string;
|
||||
profile_image?: string;
|
||||
};
|
||||
|
||||
export type resetPasswordType = {
|
||||
password?: string;
|
||||
profile_image?: string;
|
||||
};
|
||||
|
||||
export type Users = {
|
||||
id: string;
|
||||
username: string;
|
||||
is_active: boolean;
|
||||
is_superuser: boolean;
|
||||
profile_image: string;
|
||||
create_at: Date;
|
||||
updated_at: Date;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ export type PaginatorComponentType = {
|
|||
export type ConfirmationModalType = {
|
||||
title: string;
|
||||
titleHeader: string;
|
||||
asChild?: boolean;
|
||||
modalContent: string;
|
||||
modalContentTitle: string;
|
||||
cancelText: string;
|
||||
|
|
@ -268,6 +269,7 @@ export type UserManagementType = {
|
|||
icon: string;
|
||||
data?: any;
|
||||
index?: number;
|
||||
asChild?: boolean;
|
||||
onConfirm: (index, data) => void;
|
||||
};
|
||||
|
||||
|
|
@ -276,6 +278,12 @@ export type loginInputStateType = {
|
|||
password: string;
|
||||
};
|
||||
|
||||
export type patchUserInputStateType = {
|
||||
password: string;
|
||||
cnfPassword: string;
|
||||
gradient: string;
|
||||
};
|
||||
|
||||
export type UserInputType = {
|
||||
username: string;
|
||||
password: string;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ export type darkContextType = {
|
|||
setDark: (newState: {}) => void;
|
||||
stars: number;
|
||||
setStars: (stars: number) => void;
|
||||
gradientIndex: number;
|
||||
setGradientIndex: (index: number) => void;
|
||||
};
|
||||
|
||||
export type locationContextType = {
|
||||
|
|
|
|||
|
|
@ -254,7 +254,9 @@ export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
|
|||
}
|
||||
|
||||
export function handleKeyDown(
|
||||
e: React.KeyboardEvent<HTMLInputElement>,
|
||||
e:
|
||||
| React.KeyboardEvent<HTMLInputElement>
|
||||
| React.KeyboardEvent<HTMLTextAreaElement>,
|
||||
inputValue: string | string[] | null,
|
||||
block: string
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
ArrowUpToLine,
|
||||
Bell,
|
||||
Check,
|
||||
CheckCircle2,
|
||||
|
|
@ -58,6 +59,7 @@ import {
|
|||
Scissors,
|
||||
Search,
|
||||
Settings2,
|
||||
Shield,
|
||||
Sparkles,
|
||||
SunIcon,
|
||||
TerminalSquare,
|
||||
|
|
@ -65,6 +67,7 @@ import {
|
|||
Undo,
|
||||
Unplug,
|
||||
Upload,
|
||||
User,
|
||||
UserCog2,
|
||||
UserMinus2,
|
||||
UserPlus2,
|
||||
|
|
@ -87,7 +90,7 @@ import { EvernoteIcon } from "../icons/Evernote";
|
|||
import { FBIcon } from "../icons/FacebookMessenger";
|
||||
import { GitBookIcon } from "../icons/GitBook";
|
||||
import { GoogleIcon } from "../icons/Google";
|
||||
import GradientSparkles from "../icons/GradientSparkles";
|
||||
import { GradientSparkles } from "../icons/GradientSparkles";
|
||||
import { HuggingFaceIcon } from "../icons/HuggingFace";
|
||||
import { IFixIcon } from "../icons/IFixIt";
|
||||
import { MetaIcon } from "../icons/Meta";
|
||||
|
|
@ -187,6 +190,7 @@ export const nodeNames: { [char: string]: string } = {
|
|||
};
|
||||
|
||||
export const nodeIconsLucide: iconsType = {
|
||||
ArrowUpToLine: ArrowUpToLine,
|
||||
Chroma: ChromaIcon,
|
||||
AirbyteJSONLoader: AirbyteIcon,
|
||||
Anthropic: AnthropicIcon,
|
||||
|
|
@ -221,6 +225,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
ChatVertexAI: VertexAIIcon,
|
||||
VertexAIEmbeddings: VertexAIIcon,
|
||||
agents: Rocket,
|
||||
User,
|
||||
WikipediaAPIWrapper: SvgWikipedia,
|
||||
chains: Link,
|
||||
memories: Cpu,
|
||||
|
|
@ -261,6 +266,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
Bell,
|
||||
ChevronLeft,
|
||||
ChevronDown,
|
||||
Shield,
|
||||
Plus,
|
||||
Redo,
|
||||
Settings2,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue