Skip to content

Commit

Permalink
feat(web): add functions list folder (#1414)
Browse files Browse the repository at this point in the history
* feat(web): add functions list folder
* fix(web): fix recycle bin can not reopen
  • Loading branch information
newfish-cmyk committed Jul 25, 2023
1 parent 7f16d4d commit 2f49a06
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 71 deletions.
4 changes: 2 additions & 2 deletions web/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"Function": "Function",
"FunctionList": "Functions",
"FunctionName": "Function Name",
"FunctionNameTip": "The unique identifier of the function, such as get-user",
"FunctionNameTip": "The unique identifier of the function, such as get-user, can be used to create folders in the form of 'user/get-user'.",
"InterfaceDebug": "Debug",
"Methods": "Method",
"Name": "KEY",
Expand Down Expand Up @@ -579,4 +579,4 @@
"SelectOne": "At least select one",
"Apply": "apply",
"Developing": "Developing"
}
}
4 changes: 2 additions & 2 deletions web/public/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"Function": "函数",
"FunctionList": "函数列表",
"FunctionName": "函数名",
"FunctionNameTip": "函数唯一标识, 如 get-user",
"FunctionNameTip": "函数唯一标识如 get-user,可以使用 user/get-user 的形式来创建文件夹",
"InterfaceDebug": "接口调试",
"Methods": "请求方法",
"Name": "参数名",
Expand Down Expand Up @@ -579,4 +579,4 @@
"SelectOne": "请至少选择一个",
"Apply": "使用",
"Developing": "开发中"
}
}
2 changes: 1 addition & 1 deletion web/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"Function": "函数",
"FunctionList": "函数列表",
"FunctionName": "函数名",
"FunctionNameTip": "函数唯一标识, 如 get-user",
"FunctionNameTip": "函数唯一标识如 get-user,可以使用 user/get-user 的形式来创建文件夹",
"InterfaceDebug": "接口调试",
"Methods": "请求方法",
"Name": "参数名",
Expand Down
5 changes: 3 additions & 2 deletions web/src/components/FileTypeIcon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ export default function FileTypeIcon(props: {
type: string;
className?: string;
fontSize?: number;
width?: string;
}) {
const { type, className, fontSize } = props;
const { type, className, fontSize, width = "18px" } = props;

switch (type) {
case FileType.app:
Expand Down Expand Up @@ -141,7 +142,7 @@ export default function FileTypeIcon(props: {
);
case FileType.folder:
return (
<Icon viewBox="0 0 20 16" width="18px" className="align-middle">
<Icon viewBox="0 0 20 16" width={width} className="align-middle">
<path
d="M2 16C1.45 16 0.979333 15.8043 0.588 15.413C0.196 15.021 0 14.55 0 14V2C0 1.45 0.196 0.979333 0.588 0.588C0.979333 0.196 1.45 0 2 0H8L10 2H18C18.55 2 19.021 2.196 19.413 2.588C19.8043 2.97933 20 3.45 20 4V14C20 14.55 19.8043 15.021 19.413 15.413C19.021 15.8043 18.55 16 18 16H2Z"
fill="#47C8BF"
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/SectionList/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
li {
cursor: pointer;
display: flex;
height: 24px;
min-height: 24px;
align-items: center;
justify-content: space-between;
padding: 0px 12px;
Expand Down
7 changes: 3 additions & 4 deletions web/src/pages/app/database/PolicyDataList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,9 @@ export default function PolicyDataList() {
>
<Button
disabled={store.currentPolicy === undefined}
colorScheme="primary"
className="mr-2"
style={{ width: "114px" }}
leftIcon={<AddIcon />}
leftIcon={<AddIcon fontSize={10} className="text-grayModern-500" />}
variant="textGhost"
size="xs"
>
{t("CollectionPanel.AddRules")}
</Button>
Expand Down
9 changes: 7 additions & 2 deletions web/src/pages/app/functions/mods/EditorPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@ function EditorPanel() {

<HStack spacing={1}>
<div className={clsx("flex items-center", !darkMode && "bg-[#F6F8F9]")}>
<Input w={"200px"} size="xs" readOnly value={getFunctionUrl()} />
<CopyText text={getFunctionUrl()} className="mr-3 !text-grayModern-300">
<CopyText text={getFunctionUrl()}>
<Input w={"200px"} size="xs" readOnly value={getFunctionUrl()} />
</CopyText>
<CopyText
text={getFunctionUrl()}
className="mr-3 cursor-pointer !text-grayModern-300"
>
<CopyIcon />
</CopyText>
</div>
Expand Down
181 changes: 130 additions & 51 deletions web/src/pages/app/functions/mods/FunctionPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* cloud functions list sidebar
***************************/

import { useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { AddIcon, DeleteIcon, EditIcon, Search2Icon } from "@chakra-ui/icons";
import { Badge, HStack, Input, InputGroup, InputLeftElement, useColorMode } from "@chakra-ui/react";
Expand All @@ -24,7 +24,6 @@ import { useDeleteFunctionMutation, useFunctionListQuery } from "../../service";
import useFunctionStore from "../../store";
import TriggerModal from "../TriggerModal";

// import PromptModal from "./CreateModal/PromptModal";
import CreateModal from "./CreateModal";

import { TFunction } from "@/apis/typing";
Expand All @@ -36,18 +35,27 @@ type TagItem = {
selected: boolean;
};

type TreeNode = {
_id: string;
name: string;
level?: number;
isExpanded?: boolean;
children: (TreeNode | TFunction)[];
};

export default function FunctionList() {
const { setCurrentFunction, currentFunction, setAllFunctionList, allFunctionList } =
useFunctionStore((store) => store);

const functionCache = useFunctionCache();
const [root, setRoot] = useState<TreeNode>({ _id: "", name: "", children: [] });

const [keywords, setKeywords] = useState("");

const { colorMode } = useColorMode();
const darkMode = colorMode === COLOR_MODE.dark;

const { currentApp } = useGlobalStore();
const { currentApp, showSuccess } = useGlobalStore();

const { id: functionName } = useParams();
const navigate = useNavigate();
Expand All @@ -63,9 +71,38 @@ export default function FunctionList() {
return flag;
});

function generateRoot(data: TFunction[]) {
const root = { _id: "", name: "", level: 0, isExpanded: true, children: [] };
data.forEach((item) => {
const nameParts = item.name.split("/");
let currentNode: TreeNode = root;
nameParts.forEach((part, index) => {
if (index === nameParts.length - 1) {
currentNode.children.push(item);
return;
}
let existingNode = currentNode.children.find((node) => node.name === part);
if (!existingNode) {
const newNode = {
_id: item._id,
name: part,
level: index,
isExpanded: false,
children: [],
};
currentNode.children.push(newNode);
existingNode = newNode;
}
currentNode = existingNode as TreeNode;
});
});
return root;
}

useFunctionListQuery({
onSuccess: (data) => {
setAllFunctionList(data.data);
setRoot(generateRoot(data.data));
const tags = data.data.reduce((pre: any, item: any) => {
return pre.concat(item.tags);
}, []);
Expand Down Expand Up @@ -125,6 +162,92 @@ export default function FunctionList() {
) : null;
};

function renderSectionItems(items: TreeNode[], isFuncList = false) {
items.sort((a: TreeNode, b: TreeNode) => {
const isFolderA = a.children && a.children.length > 0;
const isFolderB = b.children && b.children.length > 0;
if (isFolderA && !isFolderB) {
return -1;
} else if (!isFolderA && isFolderB) {
return 1;
}
return 0;
});

return items.map((item, index) => {
let fileType = FileType.ts;
if (item.children?.length) {
fileType = FileType.folder;
}
const level = item.level || item?.name.split("/").length - 1;

return (
<React.Fragment key={index}>
<SectionList.Item
isActive={item?.name === currentFunction?.name}
key={index as any}
className="group"
onClick={() => {
if (!item?.children?.length) {
setCurrentFunction(item);
navigate(`/app/${currentApp?.appid}/${Pages.function}/${item?.name}`);
} else {
item.isExpanded = !item.isExpanded;
setRoot({ ...root });
}
}}
>
<div
className={clsx(
"overflow-hidden text-ellipsis whitespace-nowrap",
!isFuncList ? `ml-${2 * level}` : "",
)}
>
<FileTypeIcon type={fileType} width="12px" />
<span className="ml-2 text-base">
{item.children?.length || isFuncList ? item?.name : item?.name.split("/")[level]}
</span>
</div>
{!item.children?.length && (
<HStack spacing={1}>
{functionCache.getCache(item?._id, (item as any)?.source?.code) !==
(item as any)?.source?.code && (
<span className="mt-[1px] inline-block h-1 w-1 flex-none rounded-full bg-warn-700"></span>
)}
<MoreButton isHidden={item.name !== currentFunction?.name} label={t("Operation")}>
<>
<CreateModal functionItem={item} tagList={tagsList}>
<IconText icon={<EditIcon />} text={t("Edit")} />
</CreateModal>
<ConfirmButton
onSuccessAction={async () => {
const res = await deleteFunctionMutation.mutateAsync(item);
if (!res.error) {
showSuccess(t("DeleteSuccess"));
}
}}
headerText={String(t("Delete"))}
bodyText={String(t("FunctionPanel.DeleteConfirm"))}
>
<IconText
icon={<DeleteIcon />}
text={t("Delete")}
className="hover:!text-error-600"
/>
</ConfirmButton>
</>
</MoreButton>
</HStack>
)}
</SectionList.Item>
{item.isExpanded &&
item?.children?.length &&
renderSectionItems(item.children as TreeNode[])}
</React.Fragment>
);
});
}

return (
<Panel className="min-w-[215px] flex-grow overflow-hidden">
<Panel.Header
Expand Down Expand Up @@ -172,56 +295,12 @@ export default function FunctionList() {
{renderSelectedTags()}

<div className="flex-grow" style={{ overflowY: "auto" }}>
{allFunctionList?.length ? (
{keywords || currentTag ? (
<SectionList>
{filterFunctions.map((func: any) => {
return (
<SectionList.Item
isActive={func?.name === currentFunction?.name}
key={func?.name || ""}
className="group"
onClick={() => {
setCurrentFunction(func);
navigate(`/app/${currentApp?.appid}/${Pages.function}/${func?.name}`);
}}
>
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
<FileTypeIcon type={FileType.ts} />
<span className="ml-2 text-base">{func?.name}</span>
</div>
<HStack spacing={1}>
{functionCache.getCache(func?._id, func?.source?.code) !==
func?.source?.code && (
<span className="mt-[1px] inline-block h-1 w-1 flex-none rounded-full bg-warn-700"></span>
)}
<MoreButton
isHidden={func.name !== currentFunction?.name}
label={t("Operation")}
>
<>
<CreateModal functionItem={func} tagList={tagsList}>
<IconText icon={<EditIcon />} text={t("Edit")} />
</CreateModal>
<ConfirmButton
onSuccessAction={async () => {
await deleteFunctionMutation.mutateAsync(func);
}}
headerText={String(t("Delete"))}
bodyText={String(t("FunctionPanel.DeleteConfirm"))}
>
<IconText
icon={<DeleteIcon />}
text={t("Delete")}
className="hover:!text-error-600"
/>
</ConfirmButton>
</>
</MoreButton>
</HStack>
</SectionList.Item>
);
})}
{renderSectionItems(filterFunctions as unknown as TreeNode[], true)}
</SectionList>
) : root.children?.length ? (
<SectionList>{renderSectionItems(root.children as TreeNode[])}</SectionList>
) : (
<EmptyBox hideIcon>
<p>{t("FunctionPanel.EmptyFunctionTip")}</p>
Expand Down
13 changes: 13 additions & 0 deletions web/src/pages/app/logs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { BiRefresh } from "react-icons/bi";
import {
Button,
Center,
Expand Down Expand Up @@ -120,6 +121,18 @@ export default function LogsPage() {
>
{t("Search")}
</Button>

<Button
size="xs"
variant="textGhost"
leftIcon={<BiRefresh fontSize={22} className="text-grayModern-500" />}
disabled={logListQuery === undefined}
onClick={() => {
logListQuery.refetch();
}}
>
{t("RefreshData")}
</Button>
</HStack>
<Pagination
options={LIMIT_OPTIONS}
Expand Down
8 changes: 5 additions & 3 deletions web/src/pages/app/mods/StatusBar/RecycleBinModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export default function RecycleBinModal(props: { children: React.ReactElement })
setFunctionListData(data.data);
setCurrentFunction(data.data.list[0]);
setEnable(false);
if (data.data.list.length === 0) {
if (data.data.total === 0) {
onClose();
showSuccess(t("RecycleBinEmpty"));
}
Expand All @@ -177,7 +177,7 @@ export default function RecycleBinModal(props: { children: React.ReactElement })
if (functionListData && functionListData?.list.length > 0) {
onOpen();
}
}, [functionListData, onOpen]);
}, [functionListData, onOpen, enable]);

return (
<>
Expand Down Expand Up @@ -314,8 +314,10 @@ export default function RecycleBinModal(props: { children: React.ReactElement })
<Pagination
values={getPageInfo(functionListData)}
onChange={(values: any) => {
setEnable(true);
setQueryData({
...values,
page: values.page,
pageSize: values.pageSize,
});
}}
notShowSelect
Expand Down
Loading

0 comments on commit 2f49a06

Please sign in to comment.