From 7f16d4d1a023d63a6790895101edf2034a6dfbec Mon Sep 17 00:00:00 2001 From: heheer <71265218+newfish-cmyk@users.noreply.github.com> Date: Tue, 25 Jul 2023 11:26:33 +0800 Subject: [PATCH] refactor(web): optimize create function & function template component (#1416) * refactor(web): optimize create function component * refactor(web): adjust function template files * fix(web): fix frequent avatar flickering * fix(web): fix billing icon & app list loading --- web/src/components/CommonIcon/index.tsx | 8 +- web/src/constants/index.ts | 8 + web/src/layouts/Header/index.tsx | 234 ++++++------------ .../CreateModal/functionTemplates.ts | 99 -------- .../mods/FunctionPanel/CreateModal/index.tsx | 106 +++----- .../FunctionPanel/FunctionTemplate/index.tsx | 52 ---- .../functions/mods/FunctionPanel/index.tsx | 6 - web/src/pages/app/mods/SideBar/index.tsx | 4 +- .../app/setting/BillingDetails/index.tsx | 194 ++++++++------- .../app/setting/CardRedemption/index.tsx | 4 +- .../app/setting/RechargeHistory/index.tsx | 116 ++++----- web/src/pages/app/setting/Usage/index.tsx | 10 +- web/src/pages/app/setting/UserInfo/index.tsx | 16 +- .../Mods/AddFunctionModal.tsx | 12 +- .../CreateFuncTemplate/index.tsx | 2 - .../FunctionPopover.tsx} | 0 .../Mods/MonacoEditor/index.tsx | 3 +- .../Mods/TemplateCard/TemplateCard.tsx | 38 +-- .../UseTemplate/UseTemplateModal.tsx} | 2 +- .../index.tsx} | 5 +- .../{Mods/SideBar => }/index.module.scss | 0 web/src/pages/functionTemplate/index.tsx | 12 +- web/src/pages/globalStore.ts | 4 + web/src/pages/home/index.tsx | 11 +- web/src/pages/home/mods/List/index.tsx | 6 +- web/src/utils/getAvatarUrl.ts | 4 +- 26 files changed, 340 insertions(+), 616 deletions(-) delete mode 100644 web/src/pages/app/functions/mods/FunctionPanel/CreateModal/functionTemplates.ts delete mode 100644 web/src/pages/app/functions/mods/FunctionPanel/FunctionTemplate/index.tsx rename web/src/pages/functionTemplate/Mods/{FunctionPopover/index.tsx => MonacoEditor/FunctionPopover.tsx} (100%) rename web/src/pages/functionTemplate/Mods/{UseTemplateModal/index.tsx => TemplateInfo/UseTemplate/UseTemplateModal.tsx} (98%) rename web/src/pages/functionTemplate/Mods/TemplateInfo/{UseTemplate.tsx => UseTemplate/index.tsx} (95%) rename web/src/pages/functionTemplate/{Mods/SideBar => }/index.module.scss (100%) diff --git a/web/src/components/CommonIcon/index.tsx b/web/src/components/CommonIcon/index.tsx index ea3b2b0953..f82688971b 100644 --- a/web/src/components/CommonIcon/index.tsx +++ b/web/src/components/CommonIcon/index.tsx @@ -287,10 +287,16 @@ export const DomainIcon = createIcon({ export const FilterIcon = createIcon({ displayName: "FilterIcon", - viewBox: "0 0 10 9", + viewBox: "0 0 10 8", d: "M9.5 0V1H9L6.5 4.75V9H3.5V4.75L1 1H0.5V0H9.5ZM2.202 1L4.5 4.447V8H5.5V4.447L7.798 1H2.202Z", }); +export const CalendarIcon = createIcon({ + displayName: "CalendarIcon", + viewBox: "0 0 12 12", + d: "M2.5 11C2.225 11 1.9895 10.9022 1.7935 10.7065C1.59783 10.5105 1.5 10.275 1.5 10V3C1.5 2.725 1.59783 2.48967 1.7935 2.294C1.9895 2.098 2.225 2 2.5 2H3V1.4875C3 1.34583 3.04783 1.22917 3.1435 1.1375C3.2395 1.04583 3.35833 1 3.5 1C3.64167 1 3.7605 1.04783 3.8565 1.1435C3.95217 1.2395 4 1.35833 4 1.5V2H8V1.4875C8 1.34583 8.048 1.22917 8.144 1.1375C8.23967 1.04583 8.35833 1 8.5 1C8.64167 1 8.76033 1.04783 8.856 1.1435C8.952 1.2395 9 1.35833 9 1.5V2H9.5C9.775 2 10.0105 2.098 10.2065 2.294C10.4022 2.48967 10.5 2.725 10.5 3V10C10.5 10.275 10.4022 10.5105 10.2065 10.7065C10.0105 10.9022 9.775 11 9.5 11H2.5ZM2.5 10H9.5V5H2.5V10ZM2.5 4H9.5V3H2.5V4ZM2.5 4V3V4Z", +}); + export const ExitIcon = createIcon({ displayName: "ExitIcon", viewBox: "0 0 20 20", diff --git a/web/src/constants/index.ts b/web/src/constants/index.ts index a27cc584ee..78369dcfdb 100644 --- a/web/src/constants/index.ts +++ b/web/src/constants/index.ts @@ -75,3 +75,11 @@ export const COLOR_MODE = { }; export const LAF_AI_URL = "https://htr4n1.laf.run/laf-gpt"; + +export const DEFAULT_CODE = `import cloud from '@lafjs/cloud' + +export default async function (ctx: FunctionContext) { + console.log('Hello World') + return { data: 'hi, laf' } +} +`; diff --git a/web/src/layouts/Header/index.tsx b/web/src/layouts/Header/index.tsx index 87372764cf..3e774c402b 100644 --- a/web/src/layouts/Header/index.tsx +++ b/web/src/layouts/Header/index.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { HamburgerIcon, MoonIcon, SunIcon } from "@chakra-ui/icons"; -import { Menu, MenuButton, MenuItem, MenuList, useColorMode } from "@chakra-ui/react"; +import { MoonIcon, SunIcon } from "@chakra-ui/icons"; +import { useColorMode } from "@chakra-ui/react"; import clsx from "clsx"; import { ChatIcon, TextIcon } from "@/components/CommonIcon"; @@ -16,7 +16,7 @@ import Language from "@/pages/homepage/language"; const Header = (props: { width: string }) => { const { width } = props; const { t } = useTranslation(); - const { userInfo } = useGlobalStore((state) => state); + const { userInfo, avatarUpdatedAt } = useGlobalStore((state) => state); const { colorMode } = useColorMode(); const darkMode = colorMode === COLOR_MODE.dark; @@ -39,11 +39,6 @@ const Header = (props: { width: string }) => { ref: "https://forum.laf.run/", icon: , }, - // { - // text: t("HomePage.NavBar.contact"), - // ref: "https://www.wenjuan.com/s/I36ZNbl/", - // icon: , - // }, ]; useEffect(() => { @@ -52,170 +47,79 @@ const Header = (props: { width: string }) => { } }, []); - function isMobileDevice() { - const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i; - return mobileRegex.test(navigator.userAgent); - } - return ( <> - {!isMobileDevice() ? ( -
-
- - {"logo"} - +
+
+ + {"logo"} + - {navList_left.map((item, index) => { - return ( - - {item.text} - - ); - })} -
+ {navList_left.map((item, index) => { + return ( + + {item.text} + + ); + })} +
-
- {navList_right.map((item, index) => { - return ( - - {item.icon} - {item.text} - - ); - })} - -
{ - toggleColorMode(); - window.dispatchEvent(new Event("ColorModeChange")); - }} - > - {darkMode ? : } -
- {userInfo?._id ? ( - <> - - - ) : null} +
+ {navList_right.map((item, index) => { + return ( + + {item.icon} + {item.text} + + ); + })} + +
{ + toggleColorMode(); + window.dispatchEvent(new Event("ColorModeChange")); + }} + > + {darkMode ? : }
-
- ) : ( -
-
- - - - - - {navList_left.map((item, index) => { - return ( - - - {item.text} - - - ); - })} - {navList_right.map((item, index) => { - return ( - - - {item.icon} - {item.text} - - - ); - })} - - - - {"logo"} + - -
{ - toggleColorMode(); - window.dispatchEvent(new Event("ColorModeChange")); - }} - > - {darkMode ? : } -
- {userInfo?._id ? ( - <> - - - ) : null} -
+ + ) : null}
- )} +
); }; diff --git a/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/functionTemplates.ts b/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/functionTemplates.ts deleted file mode 100644 index b42da7f9c5..0000000000 --- a/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/functionTemplates.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { t } from "i18next"; - -const functionTemplates = [ - { - label: "hello laf", - value: `import cloud from '@lafjs/cloud' - -export default async function (ctx: FunctionContext) { - console.log('Hello World') - return { data: 'hi, laf' } -} -`, - }, - { - label: t("database example"), - value: `import cloud from '@lafjs/cloud' - -export default async function (ctx: FunctionContext) { - const db = cloud.database() - - // insert data - await db.collection('test').add({ name: "hello laf" }) - - // get data - const res = await db.collection('test').getOne() - console.log(res) - - return res.data -} -`, - }, - { - label: t("upload example"), - value: `import cloud from '@lafjs/cloud' -import { S3 } from "@aws-sdk/client-s3" - -export default async function (ctx: FunctionContext) { - // Create your bucket first - const BUCKET = "kcqcau-test" - const client = new S3({ - region: cloud.env.OSS_REGION, - endpoint: cloud.env.OSS_EXTERNAL_ENDPOINT, - credentials: { - accessKeyId: cloud.env.OSS_ACCESS_KEY, - secretAccessKey: cloud.env.OSS_ACCESS_SECRET, - }, - forcePathStyle: true, - }) - - const file = ctx.files[0] - console.log(file) - const stream = require('fs').createReadStream(file.path) - - const res = await client.putObject({ - Bucket: BUCKET, - Key: ctx.files[0].filename, - Body: stream, - ContentType: file.mimetype, - }) - console.log(res) - return res -} - `, - }, - { - label: t("ChatGPT example"), - value: `import cloud from '@lafjs/cloud' -const apiKey = cloud.env.API_KEY - -export default async function (ctx: FunctionContext) { - const { ChatGPTAPI } = await import('chatgpt') - const { body, response } = ctx - - // get chatgpt api - let api = cloud.shared.get('api') - if (!api) { - api = new ChatGPTAPI({ apiKey }) - cloud.shared.set('api', api) - } - - // set stream response type - response.setHeader('Content-Type', 'application/octet-stream'); - - // send message - const res = await api.sendMessage(body.message, { - onProgress: (partialResponse) => { - if (partialResponse?.delta != undefined) - response.write(partialResponse.delta) - }, - parentMessageId: body.parentMessageId || '' - }) - - response.end("--!" + res.id) -} - `, - }, -]; - -export default functionTemplates; diff --git a/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/index.tsx b/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/index.tsx index b8578bbd5c..7ff9da242c 100644 --- a/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/index.tsx +++ b/web/src/pages/app/functions/mods/FunctionPanel/CreateModal/index.tsx @@ -18,23 +18,20 @@ import { useDisclosure, VStack, } from "@chakra-ui/react"; -import clsx from "clsx"; import { t } from "i18next"; import { debounce } from "lodash"; import { TextIcon } from "@/components/CommonIcon"; import InputTag from "@/components/InputTag"; -import { SUPPORTED_METHODS } from "@/constants"; +import { DEFAULT_CODE, SUPPORTED_METHODS } from "@/constants"; import { changeURL } from "@/utils/format"; import { useCreateFunctionMutation, useUpdateFunctionMutation } from "../../../service"; import useFunctionStore from "../../../store"; -import FuncTemplate from "../FunctionTemplate"; - -import functionTemplates from "./functionTemplates"; import { TFunctionTemplate, TMethod } from "@/apis/typing"; -import TemplatePopOver from "@/pages/functionTemplate/Mods/TemplatePopover/TemplatePopover"; +import FunctionTemplate from "@/pages/functionTemplate"; +import TemplateCard from "@/pages/functionTemplate/Mods/TemplateCard/TemplateCard"; import { useGetRecommendFunctionTemplatesQuery } from "@/pages/functionTemplate/service"; import useGlobalStore from "@/pages/globalStore"; @@ -52,13 +49,14 @@ const CreateModal = (props: { const isEdit = !!functionItem; const navigate = useNavigate(); const [searchKey, setSearchKey] = useState(""); + const [templateOpen, setTemplateOpen] = useState(false); const defaultValues = { name: functionItem?.name || "", description: functionItem?.desc || "", websocket: !!functionItem?.websocket, methods: functionItem?.methods || ["GET", "POST"], - code: functionItem?.source.code || aiCode || functionTemplates[0].value.trim() || "", + code: functionItem?.source.code || aiCode || DEFAULT_CODE || "", tags: functionItem?.tags || [], }; @@ -222,54 +220,15 @@ const CreateModal = (props: {
{TemplateList.data?.data.list.map((item: TFunctionTemplate) => (
- -
{ - const currentURL = window.location.pathname; - const lastIndex = currentURL.lastIndexOf("/"); - const newURL = currentURL.substring(0, lastIndex) + `/${item._id}`; - navigate(newURL); - }} - > - -
{ - e.currentTarget.style.outlineWidth = "2px"; - e.currentTarget.style.boxShadow = - "0px 2px 4px rgba(0, 0, 0, 0.1), 0px 0px 0px 2px #66CBCA"; - }} - onMouseLeave={(e) => { - e.currentTarget.style.outlineWidth = "1px"; - e.currentTarget.style.boxShadow = - "0px 2px 4px rgba(0, 0, 0, 0.1)"; - }} - > -
-
- {item.name} -
-
-
- {item.description} -
-
- {item.items.map((item: any) => { - return ( -
- {item.name} -
- ); - })} -
-
-
-
-
+ { + navigate(changeURL(`/${item._id}`)); + setTemplateOpen(true); + }} + templateCategory="recommended" + isModal={true} + />
))}
@@ -278,22 +237,15 @@ const CreateModal = (props: { navigate(changeURL(`/recommended`)); }} > - - - +
)} - - {/* */} @@ -315,6 +267,24 @@ const CreateModal = (props: { + + { + setTemplateOpen(!templateOpen); + navigate(changeURL("/")); + }} + > + + + {t("HomePage.NavBar.funcTemplate")} + + + + + + + ); }; diff --git a/web/src/pages/app/functions/mods/FunctionPanel/FunctionTemplate/index.tsx b/web/src/pages/app/functions/mods/FunctionPanel/FunctionTemplate/index.tsx deleted file mode 100644 index 451096a01b..0000000000 --- a/web/src/pages/app/functions/mods/FunctionPanel/FunctionTemplate/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from "react"; -import { useNavigate } from "react-router-dom"; -import { - Modal, - ModalBody, - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, - useDisclosure, -} from "@chakra-ui/react"; -import { t } from "i18next"; - -import { changeURL } from "@/utils/format"; - -import FunctionTemplate from "@/pages/functionTemplate"; - -const FuncTemplate = (props: { children?: React.ReactElement }) => { - const { onOpen, isOpen, onClose } = useDisclosure(); - const { children } = props; - const navigate = useNavigate(); - - const handleModalClose = () => { - navigate(changeURL(`/function`)); - onClose(); - }; - return ( - <> - {children && - React.cloneElement(children, { - onClick: () => { - onOpen(); - }, - })} - - - - - {t("HomePage.NavBar.funcTemplate")} - - - - - - - - - ); -}; - -export default FuncTemplate; diff --git a/web/src/pages/app/functions/mods/FunctionPanel/index.tsx b/web/src/pages/app/functions/mods/FunctionPanel/index.tsx index bc7e702e3f..e620a047db 100644 --- a/web/src/pages/app/functions/mods/FunctionPanel/index.tsx +++ b/web/src/pages/app/functions/mods/FunctionPanel/index.tsx @@ -3,7 +3,6 @@ ***************************/ import { useEffect, useState } from "react"; -// import { TbBrandGithubCopilot } from "react-icons/tb"; 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"; @@ -145,11 +144,6 @@ export default function FunctionList() { , - // - // - // - // - // , diff --git a/web/src/pages/app/mods/SideBar/index.tsx b/web/src/pages/app/mods/SideBar/index.tsx index e5b48a7cf6..5b6e9c053e 100644 --- a/web/src/pages/app/mods/SideBar/index.tsx +++ b/web/src/pages/app/mods/SideBar/index.tsx @@ -27,7 +27,7 @@ type TIcon = { export default function SideBar() { const { pageId } = useParams(); const navigate = useNavigate(); - const { currentApp, setCurrentPage, userInfo, regions = [] } = useGlobalStore(); + const { currentApp, setCurrentPage, userInfo, avatarUpdatedAt, regions = [] } = useGlobalStore(); const currentRegion = regions.find((item: any) => item._id === currentApp?.regionId) || regions[0]; @@ -69,7 +69,7 @@ export default function SideBar() { component: ( ), diff --git a/web/src/pages/app/setting/BillingDetails/index.tsx b/web/src/pages/app/setting/BillingDetails/index.tsx index 287bb21335..8693d3b355 100644 --- a/web/src/pages/app/setting/BillingDetails/index.tsx +++ b/web/src/pages/app/setting/BillingDetails/index.tsx @@ -1,7 +1,6 @@ import { useEffect, useState } from "react"; import { DateRange, DayPicker, SelectRangeEventHandler } from "react-day-picker"; import { useTranslation } from "react-i18next"; -import { CalendarIcon } from "@chakra-ui/icons"; import { Center, Popover, @@ -24,7 +23,7 @@ import { useQuery } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query"; import clsx from "clsx"; -import { BillingIcon, FilterIcon } from "@/components/CommonIcon"; +import { BillingIcon, CalendarIcon, FilterIcon } from "@/components/CommonIcon"; import EmptyBox from "@/components/EmptyBox"; import Pagination from "@/components/Pagination"; import { formatDate } from "@/utils/format"; @@ -93,72 +92,75 @@ export default function BillingDetails() { - AppId - - - - - - - - -
- {appList.map((app: any) => ( -
- { - if (e.target.checked) { - setSelectedAppList([...selectedAppList, app.appid]); - } else { - setSelectedAppList( - selectedAppList.filter((item: any) => item !== app.appid), - ); - } - setQueryData({ - ...queryData, - appid: e.target.checked - ? [...selectedAppList, app.appid] - : selectedAppList.filter((item: any) => item !== app.appid), - }); - }} - /> - {app.appid} -
- ))} -
-
-
-
+ + AppId + + + + + + +
+ {appList.map((app: any) => ( +
+ { + if (e.target.checked) { + setSelectedAppList([...selectedAppList, app.appid]); + } else { + setSelectedAppList( + selectedAppList.filter((item: any) => item !== app.appid), + ); + } + setQueryData({ + ...queryData, + appid: e.target.checked + ? [...selectedAppList, app.appid] + : selectedAppList.filter((item: any) => item !== app.appid), + }); + }} + /> + {app.appid} +
+ ))} +
+
+
+
+
- - {t("Duration")} - - - - - - - - - - + + {t("Duration")} + + + + - - - + + + + + + + + @@ -181,38 +183,38 @@ export default function BillingDetails() { - - {t("State")} + + + {t("State")} + + + + + + + + + {STATE_LIST.map((item) => ( + { + setQueryData({ + ...queryData, + state: e.target.value, + }); + }} + > + {item} + + ))} + + + + - - - - - - - - - - {STATE_LIST.map((item) => ( - { - setQueryData({ - ...queryData, - state: e.target.value, - }); - }} - > - {item} - - ))} - - - - diff --git a/web/src/pages/app/setting/CardRedemption/index.tsx b/web/src/pages/app/setting/CardRedemption/index.tsx index 73d91124eb..0874e75a75 100644 --- a/web/src/pages/app/setting/CardRedemption/index.tsx +++ b/web/src/pages/app/setting/CardRedemption/index.tsx @@ -1,6 +1,6 @@ import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { Button, FormControl, Input } from "@chakra-ui/react"; +import { Button, FormControl, Input, useColorMode } from "@chakra-ui/react"; import { CardIcon } from "@/components/CommonIcon"; @@ -17,6 +17,7 @@ export default function CardRedemption() { handleSubmit, formState: { errors }, } = useForm(); + const darkMode = useColorMode().colorMode === "dark"; const onSubmit = async (data: any) => { const res = await useGiftCode.mutateAsync({ code: data.giftCode }); @@ -40,6 +41,7 @@ export default function CardRedemption() { })} placeholder={String(t("SettingPanel.EnterGiftCode"))} className="mt-2 !border-frostyNightfall-300" + bg={!darkMode ? "#F8FAFB" : "none"} />