Feature Enhancements and Bug Fixes (#2099)
#### Overview: This PR introduces several new features and fixes to improve the user experience and functionality of the application. Key updates include enhanced interactivity, export functionalities, UI improvements, and bug fixes. #### New Features: 1. **Play on Hover**: - Added functionality to play media when hovering over the entire node for better interactivity. 2. **Export Handler**: - Implemented an export handler on the main page. - `handleExport` function added as a parameter to the header, along with an export button for easy data export. 3. **Upload Flow**: - Introduced an "Upload Flow" button on the main page for streamlined file uploads. 4. **User Menu**: - Added a user option in the header menu for better user profile management. #### Bug Fixes and UI Improvements: 1. **Naming and Position Fixes**: - Corrected naming issues and positioning of alerts in the export modal for clarity. 2. **Button Styling**: - Updated buttons in the components section header to utilize ShadCN button styles for consistency. 3. **Select All Classes**: - Resolved issues with the "Select All" functionality for improved batch operations. 4. **Import Function**: - Fixed the import function to ensure reliable data import processes. 5. **Error Naming**: - Standardized error naming conventions for better error handling and debugging. #### Commits: 1. **Added play on hover of entire node** - Enhances interactivity by enabling play on hover. 2. **Added Export handler on Main Page** - Implements core export functionality. 3. **Added handleExport as parameter of header and added button to export** - Integrates export control into the header. 4. **Fixed naming of errors** - Improves error handling by fixing naming issues. 5. **Fixed buttons not using ShadCN buttons on header of components section** - Ensures consistent button styling. 6. **Fixed naming and position of alert on Export modal** - Corrects alert display issues in export modal. 7. **Fixed select all classes** - Resolves issues with the "Select All" feature. 8. **Added Upload Flow button to main page** - Introduces an upload button for file handling. 9. **Fixed import function** - Ensures reliable import operations. 10. **Added user on header menu** - Adds user options to the header for profile management. These updates collectively enhance the application's functionality, user interface, and overall user experience.
This commit is contained in:
commit
106ce9e64b
20 changed files with 321 additions and 184 deletions
1
src/frontend/package-lock.json
generated
1
src/frontend/package-lock.json
generated
|
|
@ -480,7 +480,6 @@
|
||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"extraneous": true,
|
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,14 @@ export default function GenericNode({
|
||||||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
||||||
const [inputDescription, setInputDescription] = useState(false);
|
const [inputDescription, setInputDescription] = useState(false);
|
||||||
const [nodeDescription, setNodeDescription] = useState(
|
const [nodeDescription, setNodeDescription] = useState(
|
||||||
data.node?.description!
|
data.node?.description!,
|
||||||
);
|
);
|
||||||
const [isOutdated, setIsOutdated] = useState(false);
|
const [isOutdated, setIsOutdated] = useState(false);
|
||||||
const buildStatus = useFlowStore(
|
const buildStatus = useFlowStore(
|
||||||
(state) => state.flowBuildStatus[data.id]?.status
|
(state) => state.flowBuildStatus[data.id]?.status,
|
||||||
);
|
);
|
||||||
const lastRunTime = useFlowStore(
|
const lastRunTime = useFlowStore(
|
||||||
(state) => state.flowBuildStatus[data.id]?.timestamp
|
(state) => state.flowBuildStatus[data.id]?.timestamp,
|
||||||
);
|
);
|
||||||
const [validationStatus, setValidationStatus] =
|
const [validationStatus, setValidationStatus] =
|
||||||
useState<validationStatusType | null>(null);
|
useState<validationStatusType | null>(null);
|
||||||
|
|
@ -116,7 +116,7 @@ export default function GenericNode({
|
||||||
|
|
||||||
updateNodeInternals(data.id);
|
updateNodeInternals(data.id);
|
||||||
},
|
},
|
||||||
[data.id, data.node, setNode, setIsOutdated]
|
[data.id, data.node, setNode, setIsOutdated],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!data.node!.template) {
|
if (!data.node!.template) {
|
||||||
|
|
@ -255,8 +255,17 @@ export default function GenericNode({
|
||||||
const isDark = useDarkStore((state) => state.dark);
|
const isDark = useDarkStore((state) => state.dark);
|
||||||
const renderIconStatus = (
|
const renderIconStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null,
|
||||||
) => {
|
) => {
|
||||||
|
const conditionSuccess = validationStatus && validationStatus.valid;
|
||||||
|
const conditionInactive =
|
||||||
|
validationStatus &&
|
||||||
|
!validationStatus.valid &&
|
||||||
|
buildStatus === BuildStatus.INACTIVE;
|
||||||
|
const conditionError =
|
||||||
|
buildStatus === BuildStatus.ERROR ||
|
||||||
|
(validationStatus && !validationStatus.valid);
|
||||||
|
|
||||||
if (buildStatus === BuildStatus.BUILDING) {
|
if (buildStatus === BuildStatus.BUILDING) {
|
||||||
return <Loading className="text-medium-indigo" />;
|
return <Loading className="text-medium-indigo" />;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -264,31 +273,30 @@ export default function GenericNode({
|
||||||
<>
|
<>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Play"
|
name="Play"
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
|
className={cn(
|
||||||
|
!conditionSuccess && !conditionInactive && !conditionError
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
|
"absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground transition-all group-hover:text-medium-indigo group-hover/node:opacity-100",
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
{validationStatus && validationStatus.valid ? (
|
{conditionSuccess ? (
|
||||||
<Checkmark
|
<Checkmark
|
||||||
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
|
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover/node:opacity-0"
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
/>
|
/>
|
||||||
) : validationStatus &&
|
) : conditionInactive ? (
|
||||||
!validationStatus.valid &&
|
|
||||||
buildStatus === BuildStatus.INACTIVE ? (
|
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Play"
|
name="Play"
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
|
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-gray opacity-30 transition-all group-hover/node:opacity-0"
|
||||||
/>
|
/>
|
||||||
) : buildStatus === BuildStatus.ERROR ||
|
) : conditionError ? (
|
||||||
(validationStatus && !validationStatus.valid) ? (
|
|
||||||
<Xmark
|
<Xmark
|
||||||
isVisible={true}
|
isVisible={true}
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
|
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover/node:opacity-0"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<IconComponent
|
<></>
|
||||||
name="Play"
|
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
@ -296,7 +304,7 @@ export default function GenericNode({
|
||||||
};
|
};
|
||||||
const getSpecificClassFromBuildStatus = (
|
const getSpecificClassFromBuildStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null,
|
||||||
) => {
|
) => {
|
||||||
let isInvalid = validationStatus && !validationStatus.valid;
|
let isInvalid = validationStatus && !validationStatus.valid;
|
||||||
|
|
||||||
|
|
@ -320,11 +328,11 @@ export default function GenericNode({
|
||||||
selected: boolean,
|
selected: boolean,
|
||||||
showNode: boolean,
|
showNode: boolean,
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null,
|
||||||
) => {
|
) => {
|
||||||
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus
|
validationStatus,
|
||||||
);
|
);
|
||||||
|
|
||||||
const baseBorderClass = getBaseBorderClass(selected);
|
const baseBorderClass = getBaseBorderClass(selected);
|
||||||
|
|
@ -332,8 +340,8 @@ export default function GenericNode({
|
||||||
const names = classNames(
|
const names = classNames(
|
||||||
baseBorderClass,
|
baseBorderClass,
|
||||||
nodeSizeClass,
|
nodeSizeClass,
|
||||||
"generic-node-div",
|
"generic-node-div group/node",
|
||||||
specificClassFromBuildStatus
|
specificClassFromBuildStatus,
|
||||||
);
|
);
|
||||||
return names;
|
return names;
|
||||||
};
|
};
|
||||||
|
|
@ -393,7 +401,7 @@ export default function GenericNode({
|
||||||
selected,
|
selected,
|
||||||
showNode,
|
showNode,
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus
|
validationStatus,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{data.node?.beta && showNode && (
|
{data.node?.beta && showNode && (
|
||||||
|
|
@ -524,7 +532,7 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
templateField
|
templateField,
|
||||||
)}
|
)}
|
||||||
info={data.node?.template[templateField].info}
|
info={data.node?.template[templateField].info}
|
||||||
name={templateField}
|
name={templateField}
|
||||||
|
|
@ -552,7 +560,7 @@ export default function GenericNode({
|
||||||
proxy={data.node?.template[templateField].proxy}
|
proxy={data.node?.template[templateField].proxy}
|
||||||
showNode={showNode}
|
showNode={showNode}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
)}
|
)}
|
||||||
<ParameterComponent
|
<ParameterComponent
|
||||||
key={scapedJSONStringfy({
|
key={scapedJSONStringfy({
|
||||||
|
|
@ -709,7 +717,7 @@ export default function GenericNode({
|
||||||
!data.node?.description) &&
|
!data.node?.description) &&
|
||||||
nameEditable
|
nameEditable
|
||||||
? "font-light italic"
|
? "font-light italic"
|
||||||
: ""
|
: "",
|
||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setInputDescription(true);
|
setInputDescription(true);
|
||||||
|
|
@ -771,13 +779,13 @@ export default function GenericNode({
|
||||||
}
|
}
|
||||||
title={getFieldTitle(
|
title={getFieldTitle(
|
||||||
data.node?.template!,
|
data.node?.template!,
|
||||||
templateField
|
templateField,
|
||||||
)}
|
)}
|
||||||
info={data.node?.template[templateField].info}
|
info={data.node?.template[templateField].info}
|
||||||
name={templateField}
|
name={templateField}
|
||||||
tooltipTitle={
|
tooltipTitle={
|
||||||
data.node?.template[templateField].input_types?.join(
|
data.node?.template[templateField].input_types?.join(
|
||||||
"\n"
|
"\n",
|
||||||
) ?? data.node?.template[templateField].type
|
) ?? data.node?.template[templateField].type
|
||||||
}
|
}
|
||||||
required={data.node!.template[templateField].required}
|
required={data.node!.template[templateField].required}
|
||||||
|
|
@ -804,7 +812,7 @@ export default function GenericNode({
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
||||||
"flex-max-width justify-center"
|
"flex-max-width justify-center",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,15 @@ export default function ErrorAlert({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<h3 className="error-build-foreground">{title}</h3>
|
<h3 className="error-build-foreground line-clamp-2">{title}</h3>
|
||||||
{list?.length !== 0 &&
|
{list?.length !== 0 &&
|
||||||
list?.some((item) => item !== null && item !== undefined) ? (
|
list?.some((item) => item !== null && item !== undefined) ? (
|
||||||
<div className="error-build-message-div">
|
<div className="error-build-message-div">
|
||||||
<ul className="error-build-message-list">
|
<ul className="error-build-message-list">
|
||||||
{list.map((item, index) => (
|
{list.map((item, index) => (
|
||||||
<li key={index}>{item}</li>
|
<li key={index} className="line-clamp-5">
|
||||||
|
{item}
|
||||||
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export default function NoticeAlert({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||||
<p className="text-sm text-info-foreground word-break-break-word">
|
<p className="line-clamp-2 text-sm text-info-foreground word-break-break-word">
|
||||||
{title}
|
{title}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-3 text-sm md:ml-6 md:mt-0">
|
<p className="mt-3 text-sm md:ml-6 md:mt-0">
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export default function SuccessAlert({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3">
|
<div className="ml-3">
|
||||||
<p className="success-alert-message">{title}</p>
|
<p className="success-alert-message line-clamp-2">{title}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ export default function Header(): JSX.Element {
|
||||||
const lastFlowVisitedIndex = routeHistory
|
const lastFlowVisitedIndex = routeHistory
|
||||||
.reverse()
|
.reverse()
|
||||||
.findIndex(
|
.findIndex(
|
||||||
(path) => path.includes("/flow/") && path !== location.pathname
|
(path) => path.includes("/flow/") && path !== location.pathname,
|
||||||
);
|
);
|
||||||
|
|
||||||
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
|
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
|
||||||
|
|
@ -200,6 +200,28 @@ export default function Header(): JSX.Element {
|
||||||
/>
|
/>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
|
{!autoLogin && (
|
||||||
|
<>
|
||||||
|
<DropdownMenuLabel>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"h-5 w-5 rounded-full focus-visible:outline-0 " +
|
||||||
|
(userData?.profile_image ??
|
||||||
|
(userData?.id
|
||||||
|
? gradients[
|
||||||
|
parseInt(userData?.id ?? "", 30) %
|
||||||
|
gradients.length
|
||||||
|
]
|
||||||
|
: "bg-gray-500"))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{userData?.username ?? "User"}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<DropdownMenuLabel>General</DropdownMenuLabel>
|
<DropdownMenuLabel>General</DropdownMenuLabel>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import IconComponent, {
|
||||||
import { Button, buttonVariants } from "../../../ui/button";
|
import { Button, buttonVariants } from "../../../ui/button";
|
||||||
import { Input } from "../../../ui/input";
|
import { Input } from "../../../ui/input";
|
||||||
import useFileDrop from "../../hooks/use-on-file-drop";
|
import useFileDrop from "../../hooks/use-on-file-drop";
|
||||||
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
|
|
||||||
type SideBarFoldersButtonsComponentProps = {
|
type SideBarFoldersButtonsComponentProps = {
|
||||||
folders: FolderType[];
|
folders: FolderType[];
|
||||||
|
|
@ -33,7 +34,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
const [foldersNames, setFoldersNames] = useState({});
|
const [foldersNames, setFoldersNames] = useState({});
|
||||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||||
const [editFolders, setEditFolderName] = useState(
|
const [editFolders, setEditFolderName] = useState(
|
||||||
folders.map((obj) => ({ name: obj.name, edit: false }))
|
folders.map((obj) => ({ name: obj.name, edit: false })),
|
||||||
);
|
);
|
||||||
const uploadFolder = useFolderStore((state) => state.uploadFolder);
|
const uploadFolder = useFolderStore((state) => state.uploadFolder);
|
||||||
const currentFolder = pathname.split("/");
|
const currentFolder = pathname.split("/");
|
||||||
|
|
@ -51,6 +52,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const folderId = location?.state?.folderId ?? myCollectionId;
|
const folderId = location?.state?.folderId ?? myCollectionId;
|
||||||
const getFolderById = useFolderStore((state) => state.getFolderById);
|
const getFolderById = useFolderStore((state) => state.getFolderById);
|
||||||
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
|
|
||||||
const handleFolderChange = (folderId: string) => {
|
const handleFolderChange = (folderId: string) => {
|
||||||
getFolderById(folderId);
|
getFolderById(folderId);
|
||||||
|
|
@ -58,11 +60,21 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
|
|
||||||
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
|
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
|
||||||
folderId,
|
folderId,
|
||||||
handleFolderChange
|
handleFolderChange,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUploadFlowsToFolder = () => {
|
const handleUploadFlowsToFolder = () => {
|
||||||
uploadFolder(folderId);
|
uploadFolder(folderId)
|
||||||
|
.then(() => {
|
||||||
|
getFolderById(folderId);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
setErrorData({
|
||||||
|
title: `Error on upload`,
|
||||||
|
list: [err["response"]["data"]],
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownloadFolder = (id: string) => {
|
const handleDownloadFolder = (id: string) => {
|
||||||
|
|
@ -73,7 +85,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
|
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
|
||||||
(res) => {
|
(res) => {
|
||||||
getFoldersApi(true);
|
getFoldersApi(true);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,7 +131,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
<>
|
<>
|
||||||
{folders.map((item, index) => {
|
{folders.map((item, index) => {
|
||||||
const editFolderName = editFolders?.filter(
|
const editFolderName = editFolders?.filter(
|
||||||
(folder) => folder.name === item.name
|
(folder) => folder.name === item.name,
|
||||||
)[0];
|
)[0];
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -135,7 +147,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
? "border border-border bg-muted hover:bg-muted"
|
? "border border-border bg-muted hover:bg-muted"
|
||||||
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
|
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
|
||||||
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
|
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
|
||||||
folderIdDragging === item.id! ? "bg-border" : ""
|
folderIdDragging === item.id! ? "bg-border" : "",
|
||||||
)}
|
)}
|
||||||
onClick={() => handleChangeFolder!(item.id!)}
|
onClick={() => handleChangeFolder!(item.id!)}
|
||||||
>
|
>
|
||||||
|
|
@ -205,7 +217,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
folders.map((obj) => ({
|
folders.map((obj) => ({
|
||||||
name: obj.name,
|
name: obj.name,
|
||||||
edit: false,
|
edit: false,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
|
|
@ -238,10 +250,10 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
};
|
};
|
||||||
const updatedFolder = await updateFolder(
|
const updatedFolder = await updateFolder(
|
||||||
body,
|
body,
|
||||||
item.id!
|
item.id!,
|
||||||
);
|
);
|
||||||
const updateFolders = folders.filter(
|
const updateFolders = folders.filter(
|
||||||
(f) => f.name !== item.name
|
(f) => f.name !== item.name,
|
||||||
);
|
);
|
||||||
setFolders([...updateFolders, updatedFolder]);
|
setFolders([...updateFolders, updatedFolder]);
|
||||||
setFoldersNames({});
|
setFoldersNames({});
|
||||||
|
|
@ -249,7 +261,7 @@ const SideBarFoldersButtonsComponent = ({
|
||||||
folders.map((obj) => ({
|
folders.map((obj) => ({
|
||||||
name: obj.name,
|
name: obj.name,
|
||||||
edit: false,
|
edit: false,
|
||||||
}))
|
})),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setFoldersNames((old) => ({
|
setFoldersNames((old) => ({
|
||||||
|
|
|
||||||
|
|
@ -668,7 +668,7 @@ export const ZERO_NOTIFICATIONS = "No new notifications";
|
||||||
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
||||||
|
|
||||||
export const ALERT_SAVE_WITH_API =
|
export const ALERT_SAVE_WITH_API =
|
||||||
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
|
"Caution: Unchecking this box only removes API keys from fields specifically designated for API keys.";
|
||||||
|
|
||||||
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
||||||
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ const ExportModal = forwardRef(
|
||||||
is_component: false,
|
is_component: false,
|
||||||
},
|
},
|
||||||
name!,
|
name!,
|
||||||
description
|
description,
|
||||||
);
|
);
|
||||||
setNoticeData({
|
setNoticeData({
|
||||||
title: API_WARNING_NOTICE_ALERT,
|
title: API_WARNING_NOTICE_ALERT,
|
||||||
|
|
@ -61,7 +61,7 @@ const ExportModal = forwardRef(
|
||||||
is_component: false,
|
is_component: false,
|
||||||
}),
|
}),
|
||||||
name!,
|
name!,
|
||||||
description
|
description,
|
||||||
);
|
);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}}
|
}}
|
||||||
|
|
@ -94,7 +94,7 @@ const ExportModal = forwardRef(
|
||||||
{SAVE_WITH_API_CHECKBOX}
|
{SAVE_WITH_API_CHECKBOX}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<span className=" text-xs text-destructive ">
|
<span className="mt-1 text-xs text-destructive ">
|
||||||
{ALERT_SAVE_WITH_API}
|
{ALERT_SAVE_WITH_API}
|
||||||
</span>
|
</span>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
|
|
@ -102,6 +102,6 @@ const ExportModal = forwardRef(
|
||||||
<BaseModal.Footer submit={{ label: "Download Flow" }} />
|
<BaseModal.Footer submit={{ label: "Download Flow" }} />
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
export default ExportModal;
|
export default ExportModal;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import { FormProvider, useForm, useWatch } from "react-hook-form";
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
import CollectionCardComponent from "../../../../components/cardComponent";
|
import CollectionCardComponent from "../../../../components/cardComponent";
|
||||||
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
|
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
|
||||||
import IconComponent from "../../../../components/genericIconComponent";
|
import IconComponent, {
|
||||||
|
ForwardedIconComponent,
|
||||||
|
} from "../../../../components/genericIconComponent";
|
||||||
import PaginatorComponent from "../../../../components/paginatorComponent";
|
import PaginatorComponent from "../../../../components/paginatorComponent";
|
||||||
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
|
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
|
||||||
import { Button } from "../../../../components/ui/button";
|
import { Button } from "../../../../components/ui/button";
|
||||||
|
|
@ -18,6 +20,9 @@ import { getNameByType } from "../../utils/get-name-by-type";
|
||||||
import { sortFlows } from "../../utils/sort-flows";
|
import { sortFlows } from "../../utils/sort-flows";
|
||||||
import EmptyComponent from "../emptyComponent";
|
import EmptyComponent from "../emptyComponent";
|
||||||
import HeaderComponent from "../headerComponent";
|
import HeaderComponent from "../headerComponent";
|
||||||
|
import { downloadFlow, removeApiKeys } from "../../../../utils/reactflowUtils";
|
||||||
|
import { useDarkStore } from "../../../../stores/darkStore";
|
||||||
|
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
|
||||||
|
|
||||||
export default function ComponentsComponent({
|
export default function ComponentsComponent({
|
||||||
type = "all",
|
type = "all",
|
||||||
|
|
@ -31,22 +36,22 @@ export default function ComponentsComponent({
|
||||||
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
||||||
|
|
||||||
const flowsFromFolder = useFolderStore(
|
const flowsFromFolder = useFolderStore(
|
||||||
(state) => state.selectedFolder?.flows
|
(state) => state.selectedFolder?.flows,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const [openDelete, setOpenDelete] = useState(false);
|
const [openDelete, setOpenDelete] = useState(false);
|
||||||
const searchFlowsComponents = useFlowsManagerStore(
|
const searchFlowsComponents = useFlowsManagerStore(
|
||||||
(state) => state.searchFlowsComponents
|
(state) => state.searchFlowsComponents,
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSelectedFlowsComponentsCards = useFlowsManagerStore(
|
const setSelectedFlowsComponentsCards = useFlowsManagerStore(
|
||||||
(state) => state.setSelectedFlowsComponentsCards
|
(state) => state.setSelectedFlowsComponentsCards,
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedFlowsComponentsCards = useFlowsManagerStore(
|
const selectedFlowsComponentsCards = useFlowsManagerStore(
|
||||||
(state) => state.selectedFlowsComponentsCards
|
(state) => state.selectedFlowsComponentsCards,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [handleFileDrop] = useFileDrop(uploadFlow, type)!;
|
const [handleFileDrop] = useFileDrop(uploadFlow, type)!;
|
||||||
|
|
@ -82,7 +87,7 @@ export default function ComponentsComponent({
|
||||||
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
|
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
|
||||||
f.description
|
f.description
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.includes(searchFlowsComponents.toLowerCase())
|
.includes(searchFlowsComponents.toLowerCase()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (searchFlowsComponents === "") {
|
if (searchFlowsComponents === "") {
|
||||||
|
|
@ -129,6 +134,8 @@ export default function ComponentsComponent({
|
||||||
setOpenDelete(true);
|
setOpenDelete(true);
|
||||||
} else if (action === "duplicate") {
|
} else if (action === "duplicate") {
|
||||||
handleDuplicate();
|
handleDuplicate();
|
||||||
|
} else if (action === "export") {
|
||||||
|
handleExport();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -137,9 +144,9 @@ export default function ComponentsComponent({
|
||||||
selectedFlowsComponentsCards.map((selectedFlow) =>
|
selectedFlowsComponentsCards.map((selectedFlow) =>
|
||||||
addFlow(
|
addFlow(
|
||||||
true,
|
true,
|
||||||
allFlows.find((flow) => flow.id === selectedFlow)
|
allFlows.find((flow) => flow.id === selectedFlow),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
).then(() => {
|
).then(() => {
|
||||||
resetFilter();
|
resetFilter();
|
||||||
getFoldersApi(true);
|
getFoldersApi(true);
|
||||||
|
|
@ -152,6 +159,47 @@ export default function ComponentsComponent({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImport = () => {
|
||||||
|
uploadFlow({ newProject: true, isComponent: false })
|
||||||
|
.then(() => {
|
||||||
|
resetFilter();
|
||||||
|
getFoldersApi(true);
|
||||||
|
if (!folderId || folderId === myCollectionId) {
|
||||||
|
getFolderById(folderId ? folderId : myCollectionId);
|
||||||
|
}
|
||||||
|
setSelectedFlowsComponentsCards([]);
|
||||||
|
|
||||||
|
setSuccessData({ title: "Flows imported successfully" });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setErrorData({
|
||||||
|
title: UPLOAD_ERROR_ALERT,
|
||||||
|
list: [error],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const version = useDarkStore((state) => state.version);
|
||||||
|
|
||||||
|
const handleExport = () => {
|
||||||
|
selectedFlowsComponentsCards.map((selectedFlowId) => {
|
||||||
|
const selectedFlow = allFlows.find((flow) => flow.id === selectedFlowId);
|
||||||
|
downloadFlow(
|
||||||
|
removeApiKeys({
|
||||||
|
id: selectedFlow!.id,
|
||||||
|
data: selectedFlow!.data!,
|
||||||
|
description: selectedFlow!.description,
|
||||||
|
name: selectedFlow!.name,
|
||||||
|
last_tested_version: version,
|
||||||
|
is_component: false,
|
||||||
|
}),
|
||||||
|
selectedFlow!.name,
|
||||||
|
selectedFlow!.description,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
setSuccessData({ title: "Flows exported successfully" });
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteMultiple = () => {
|
const handleDeleteMultiple = () => {
|
||||||
removeFlow(selectedFlowsComponentsCards)
|
removeFlow(selectedFlowsComponentsCards)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -161,7 +209,7 @@ export default function ComponentsComponent({
|
||||||
getFolderById(folderId ? folderId : myCollectionId);
|
getFolderById(folderId ? folderId : myCollectionId);
|
||||||
}
|
}
|
||||||
setSuccessData({
|
setSuccessData({
|
||||||
title: "Selected items deleted successfully!",
|
title: "Selected items deleted successfully",
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
|
@ -180,7 +228,7 @@ export default function ComponentsComponent({
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
setSelectedFlowsComponentsCards(selectedFlows);
|
setSelectedFlowsComponentsCards(selectedFlows);
|
||||||
|
|
@ -215,20 +263,23 @@ export default function ComponentsComponent({
|
||||||
if (type === "all") return allFlows?.length;
|
if (type === "all") return allFlows?.length;
|
||||||
|
|
||||||
return allFlows?.filter(
|
return allFlows?.filter(
|
||||||
(f) => (f.is_component ?? false) === (type === "component")
|
(f) => (f.is_component ?? false) === (type === "component"),
|
||||||
)?.length;
|
)?.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{allFlows?.length > 0 && (
|
<div className="flex w-full gap-4 pb-5">
|
||||||
<HeaderComponent
|
{allFlows?.length > 0 && (
|
||||||
handleDelete={() => handleSelectOptionsChange("delete")}
|
<HeaderComponent
|
||||||
handleSelectAll={handleSelectAll}
|
handleDelete={() => handleSelectOptionsChange("delete")}
|
||||||
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
|
handleSelectAll={handleSelectAll}
|
||||||
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
|
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
|
||||||
/>
|
handleExport={() => handleSelectOptionsChange("export")}
|
||||||
)}
|
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<CardsWrapComponent
|
<CardsWrapComponent
|
||||||
onFileDrop={handleFileDrop}
|
onFileDrop={handleFileDrop}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const EmptyComponent = ({}: EmptyComponentProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mt-6 flex w-full items-center justify-center text-center">
|
<div className="mt-2 flex w-full items-center justify-center text-center">
|
||||||
<div className="flex-max-width h-full flex-col">
|
<div className="flex-max-width h-full flex-col">
|
||||||
<div className="align-center flex w-full justify-center gap-1 ">
|
<div className="align-center flex w-full justify-center gap-1 ">
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import IconComponent from "../../../../components/genericIconComponent";
|
import IconComponent, {
|
||||||
|
ForwardedIconComponent,
|
||||||
|
} from "../../../../components/genericIconComponent";
|
||||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||||
import { Checkbox } from "../../../../components/ui/checkbox";
|
import { Checkbox } from "../../../../components/ui/checkbox";
|
||||||
import { cn } from "../../../../utils/utils";
|
import { cn } from "../../../../utils/utils";
|
||||||
|
import { Button } from "../../../../components/ui/button";
|
||||||
|
|
||||||
type HeaderComponentProps = {
|
type HeaderComponentProps = {
|
||||||
handleSelectAll: (select) => void;
|
handleSelectAll: (select) => void;
|
||||||
handleDelete: () => void;
|
handleDelete: () => void;
|
||||||
handleDuplicate: () => void;
|
handleDuplicate: () => void;
|
||||||
|
handleExport: () => void;
|
||||||
disableFunctions: boolean;
|
disableFunctions: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -15,6 +19,7 @@ const HeaderComponent = ({
|
||||||
handleSelectAll,
|
handleSelectAll,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleDuplicate,
|
handleDuplicate,
|
||||||
|
handleExport,
|
||||||
disableFunctions,
|
disableFunctions,
|
||||||
}: HeaderComponentProps) => {
|
}: HeaderComponentProps) => {
|
||||||
const [shouldSelectAll, setShouldSelectAll] = useState(true);
|
const [shouldSelectAll, setShouldSelectAll] = useState(true);
|
||||||
|
|
@ -26,29 +31,46 @@ const HeaderComponent = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-3 pb-5">
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
<div className="col-auto grid-cols-1 self-center justify-self-start">
|
<div className="flex items-center justify-self-start">
|
||||||
<a onClick={handleClick} className="text-sm">
|
<Button
|
||||||
<div className="header-menu-bar-display ">
|
variant="none"
|
||||||
<div
|
size="none"
|
||||||
className="header-menu-flow-name"
|
onClick={handleClick}
|
||||||
data-testid="select_all_collection"
|
className="text-sm"
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Checkbox checked={!shouldSelectAll} id="terms" />
|
<Checkbox checked={!shouldSelectAll} id="terms" />
|
||||||
<label
|
<span className="label text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||||
onClick={handleClick}
|
{shouldSelectAll ? "Select All" : "Unselect All"}
|
||||||
htmlFor="terms"
|
</span>
|
||||||
className="label cursor-pointer text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
||||||
>
|
|
||||||
{shouldSelectAll ? "Select All" : "Unselect All"}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 flex grid-cols-1 gap-2 justify-self-end">
|
<div className="flex items-center gap-2">
|
||||||
|
<div>
|
||||||
|
<ShadTooltip
|
||||||
|
content={
|
||||||
|
disableFunctions ? (
|
||||||
|
<span>Select items to export</span>
|
||||||
|
) : (
|
||||||
|
<span>Export selected items</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
|
onClick={handleExport}
|
||||||
|
disabled={disableFunctions}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="FileDown"
|
||||||
|
className={cn("h-5 w-5 text-primary transition-all")}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
|
|
@ -59,12 +81,17 @@ const HeaderComponent = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button onClick={handleDuplicate} disabled={disableFunctions}>
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
|
onClick={handleDuplicate}
|
||||||
|
disabled={disableFunctions}
|
||||||
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Copy"
|
name="Copy"
|
||||||
className={cn("h-5 w-5 text-primary transition-all")}
|
className={cn("h-5 w-5 text-primary transition-all")}
|
||||||
/>
|
/>
|
||||||
</button>
|
</Button>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -77,15 +104,20 @@ const HeaderComponent = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button onClick={handleDelete} disabled={disableFunctions}>
|
<Button
|
||||||
|
variant="none"
|
||||||
|
size="none"
|
||||||
|
onClick={handleDelete}
|
||||||
|
disabled={disableFunctions}
|
||||||
|
>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Trash2"
|
name="Trash2"
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-5 w-5 text-primary transition-all",
|
"h-5 w-5 text-primary transition-all",
|
||||||
disableFunctions ? "" : "hover:text-destructive"
|
disableFunctions ? "" : "hover:text-destructive",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</Button>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
export default function SettingsPage(): JSX.Element {
|
export default function SettingsPage(): JSX.Element {
|
||||||
const pathname = location.pathname;
|
const pathname = location.pathname;
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId,
|
||||||
);
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentFlowId("");
|
setCurrentFlowId("");
|
||||||
|
|
@ -59,7 +59,10 @@ export default function SettingsPage(): JSX.Element {
|
||||||
title: "Messages",
|
title: "Messages",
|
||||||
href: "/settings/messages",
|
href: "/settings/messages",
|
||||||
icon: (
|
icon: (
|
||||||
<ForwardedIconComponent name="Keyboard" className="w-5 stroke-[1.5]" />
|
<ForwardedIconComponent
|
||||||
|
name="MessagesSquare"
|
||||||
|
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -72,8 +75,10 @@ export default function SettingsPage(): JSX.Element {
|
||||||
<aside className="flex h-full shrink-0 flex-col space-y-6 lg:w-[20vw]">
|
<aside className="flex h-full shrink-0 flex-col space-y-6 lg:w-[20vw]">
|
||||||
<SidebarNav items={sidebarNavItems} />
|
<SidebarNav items={sidebarNavItems} />
|
||||||
</aside>
|
</aside>
|
||||||
<div className="h-full w-full flex-1 pb-8">
|
<div className="flex h-full w-full flex-1 flex-col">
|
||||||
<Outlet />
|
<div className="flex-1 pb-8">
|
||||||
|
<Outlet />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|
|
||||||
|
|
@ -52,23 +52,17 @@ export default function ApiKeysPage() {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col justify-between">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
overlayNoRowsTemplate="No data available"
|
||||||
<TableComponent
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
overlayNoRowsTemplate="No data available"
|
setSelectedRows(event.api.getSelectedRows().map((row) => row.id));
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
}}
|
||||||
setSelectedRows(
|
rowSelection="multiple"
|
||||||
event.api.getSelectedRows().map((row) => row.id),
|
suppressRowClickSelection={true}
|
||||||
);
|
pagination={true}
|
||||||
}}
|
columnDefs={columnDefs}
|
||||||
rowSelection="multiple"
|
rowData={keysList.current}
|
||||||
suppressRowClickSelection={true}
|
/>
|
||||||
pagination={true}
|
|
||||||
columnDefs={columnDefs}
|
|
||||||
rowData={keysList.current}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ import { cn } from "../../../../utils/utils";
|
||||||
|
|
||||||
export default function GlobalVariablesPage() {
|
export default function GlobalVariablesPage() {
|
||||||
const globalVariablesEntries = useGlobalVariablesStore(
|
const globalVariablesEntries = useGlobalVariablesStore(
|
||||||
(state) => state.globalVariablesEntries
|
(state) => state.globalVariablesEntries,
|
||||||
);
|
);
|
||||||
const removeGlobalVariable = useGlobalVariablesStore(
|
const removeGlobalVariable = useGlobalVariablesStore(
|
||||||
(state) => state.removeGlobalVariable
|
(state) => state.removeGlobalVariable,
|
||||||
);
|
);
|
||||||
const globalVariables = useGlobalVariablesStore(
|
const globalVariables = useGlobalVariablesStore(
|
||||||
(state) => state.globalVariables
|
(state) => state.globalVariables,
|
||||||
);
|
);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||||
|
|
@ -154,7 +154,7 @@ export default function GlobalVariablesPage() {
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Trash2"
|
name="Trash2"
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-5 w-5 text-destructive group-disabled:text-primary"
|
"h-5 w-5 text-destructive group-disabled:text-primary",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -168,23 +168,17 @@ export default function GlobalVariablesPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col justify-between">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
overlayNoRowsTemplate="No data available"
|
||||||
<TableComponent
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
overlayNoRowsTemplate="No data available"
|
setSelectedRows(event.api.getSelectedRows().map((row) => row.name));
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
}}
|
||||||
setSelectedRows(
|
rowSelection="multiple"
|
||||||
event.api.getSelectedRows().map((row) => row.name)
|
suppressRowClickSelection={true}
|
||||||
);
|
pagination={true}
|
||||||
}}
|
columnDefs={colDefs}
|
||||||
rowSelection="multiple"
|
rowData={rowData}
|
||||||
suppressRowClickSelection={true}
|
/>
|
||||||
pagination={true}
|
|
||||||
columnDefs={colDefs}
|
|
||||||
rowData={rowData}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -115,15 +115,11 @@ export default function ShortcutsPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full w-full flex-col justify-between">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
pagination={false}
|
||||||
<TableComponent
|
columnDefs={colDefs}
|
||||||
pagination={false}
|
rowData={nodesRowData}
|
||||||
columnDefs={colDefs}
|
/>
|
||||||
rowData={nodesRowData}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function MessagesPage() {
|
||||||
setSelectedRows,
|
setSelectedRows,
|
||||||
setSuccessData,
|
setSuccessData,
|
||||||
setErrorData,
|
setErrorData,
|
||||||
selectedRows
|
selectedRows,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
|
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
|
||||||
|
|
@ -52,29 +52,25 @@ export default function MessagesPage() {
|
||||||
handleRemoveMessages={handleRemoveMessages}
|
handleRemoveMessages={handleRemoveMessages}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex h-full w-full flex-col justify-between pb-8">
|
<div className="flex h-full w-full flex-col justify-between">
|
||||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
<TableComponent
|
||||||
<CardContent className="h-full">
|
readOnlyEdit
|
||||||
<TableComponent
|
onCellEditRequest={(event) => {
|
||||||
readOnlyEdit
|
handleUpdateMessage(event);
|
||||||
onCellEditRequest={(event) => {
|
}}
|
||||||
handleUpdateMessage(event);
|
editable={["Sender Name", "Message"]}
|
||||||
}}
|
overlayNoRowsTemplate="No data available"
|
||||||
editable={["Sender Name", "Message"]}
|
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||||
overlayNoRowsTemplate="No data available"
|
setSelectedRows(
|
||||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
event.api.getSelectedRows().map((row) => row.index),
|
||||||
setSelectedRows(
|
);
|
||||||
event.api.getSelectedRows().map((row) => row.index)
|
}}
|
||||||
);
|
rowSelection="multiple"
|
||||||
}}
|
suppressRowClickSelection={true}
|
||||||
rowSelection="multiple"
|
pagination={true}
|
||||||
suppressRowClickSelection={true}
|
columnDefs={columns}
|
||||||
pagination={true}
|
rowData={messages}
|
||||||
columnDefs={columns}
|
/>
|
||||||
rowData={messages}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
} from "../pages/MainPage/services";
|
} from "../pages/MainPage/services";
|
||||||
import { FoldersStoreType } from "../types/zustand/folders";
|
import { FoldersStoreType } from "../types/zustand/folders";
|
||||||
import useFlowsManagerStore from "./flowsManagerStore";
|
import useFlowsManagerStore from "./flowsManagerStore";
|
||||||
|
import { uploadFlowsToDatabase } from "../controllers/API";
|
||||||
|
|
||||||
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
folders: [],
|
folders: [],
|
||||||
|
|
@ -17,18 +18,18 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
getFolders().then(
|
getFolders().then(
|
||||||
(res) => {
|
(res) => {
|
||||||
const foldersWithoutStarterProjects = res.filter(
|
const foldersWithoutStarterProjects = res.filter(
|
||||||
(folder) => folder.name !== STARTER_FOLDER_NAME
|
(folder) => folder.name !== STARTER_FOLDER_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
const starterProjects = res.find(
|
const starterProjects = res.find(
|
||||||
(folder) => folder.name === STARTER_FOLDER_NAME
|
(folder) => folder.name === STARTER_FOLDER_NAME,
|
||||||
);
|
);
|
||||||
|
|
||||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
set({ starterProjectId: starterProjects!.id ?? "" });
|
||||||
set({ folders: foldersWithoutStarterProjects });
|
set({ folders: foldersWithoutStarterProjects });
|
||||||
|
|
||||||
const myCollectionId = res?.find(
|
const myCollectionId = res?.find(
|
||||||
(f) => f.name === DEFAULT_FOLDER
|
(f) => f.name === DEFAULT_FOLDER,
|
||||||
)?.id;
|
)?.id;
|
||||||
|
|
||||||
set({ myCollectionId });
|
set({ myCollectionId });
|
||||||
|
|
@ -45,7 +46,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
set({ folders: [] });
|
set({ folders: [] });
|
||||||
get().setLoading(false);
|
get().setLoading(false);
|
||||||
reject();
|
reject();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -65,7 +66,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
get().setLoadingById(false);
|
get().setLoadingById(false);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -100,7 +101,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
folderIdDragging: "",
|
folderIdDragging: "",
|
||||||
setFolderIdDragging: (id) => set(() => ({ folderIdDragging: id })),
|
setFolderIdDragging: (id) => set(() => ({ folderIdDragging: id })),
|
||||||
uploadFolder: () => {
|
uploadFolder: () => {
|
||||||
return new Promise<void>(() => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.onchange = (event: Event) => {
|
input.onchange = (event: Event) => {
|
||||||
|
|
@ -111,8 +112,31 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||||
const file = (event.target as HTMLInputElement).files![0];
|
const file = (event.target as HTMLInputElement).files![0];
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
uploadFlowsFromFolders(formData).then(() => {
|
file.text().then((text) => {
|
||||||
get().getFoldersApi(true);
|
const data = JSON.parse(text);
|
||||||
|
if (data.data?.nodes) {
|
||||||
|
useFlowsManagerStore
|
||||||
|
.getState()
|
||||||
|
.addFlow(true, data)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uploadFlowsFromFolders(formData)
|
||||||
|
.then(() => {
|
||||||
|
get()
|
||||||
|
.getFoldersApi(true)
|
||||||
|
.then(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export type FoldersStoreType = {
|
||||||
setFolderUrl: (folderUrl: string) => void;
|
setFolderUrl: (folderUrl: string) => void;
|
||||||
folderDragging: boolean;
|
folderDragging: boolean;
|
||||||
setFolderDragging: (set: boolean) => void;
|
setFolderDragging: (set: boolean) => void;
|
||||||
uploadFolder: (folderId: string) => void;
|
uploadFolder: (folderId: string) => Promise<void>;
|
||||||
folderIdDragging: string;
|
folderIdDragging: string;
|
||||||
setFolderIdDragging: (id: string) => void;
|
setFolderIdDragging: (id: string) => void;
|
||||||
starterProjectId: string;
|
starterProjectId: string;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import {
|
||||||
FolderIcon,
|
FolderIcon,
|
||||||
FolderPlus,
|
FolderPlus,
|
||||||
FolderPlusIcon,
|
FolderPlusIcon,
|
||||||
|
FolderUp,
|
||||||
FormInput,
|
FormInput,
|
||||||
Forward,
|
Forward,
|
||||||
Gift,
|
Gift,
|
||||||
|
|
@ -431,6 +432,7 @@ export const nodeIconsLucide: iconsType = {
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
SlidersHorizontal,
|
SlidersHorizontal,
|
||||||
Palette,
|
Palette,
|
||||||
|
FolderUp,
|
||||||
Blocks,
|
Blocks,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue