From e4d4985c4bdaf58735a7cc8969f7c31689e941a5 Mon Sep 17 00:00:00 2001 From: heheer <1239331448@qq.com> Date: Mon, 19 Jun 2023 17:02:50 +0800 Subject: [PATCH 1/3] feat(web): add function history & add app darkmode --- web/public/locales/en/translation.json | 77 +---- web/public/locales/zh-CN/translation.json | 6 +- web/public/locales/zh/translation.json | 6 +- web/src/apis/v1/api-auto.d.ts | 8 + web/src/apis/v1/apps.ts | 17 + web/src/components/ChargeButton/index.tsx | 1 - web/src/components/CommonIcon/index.tsx | 12 + .../app/functions/mods/DebugPanel/index.tsx | 17 +- .../app/functions/mods/DeployButton/index.tsx | 3 + .../app/functions/mods/EditorPanel/index.tsx | 2 - .../FetchButton/index.tsx | 44 +-- .../mods/VersionHistoryPanel/index.tsx | 66 ++++ web/src/pages/app/functions/service.ts | 13 + .../pages/home/mods/CreateAppModal/index.tsx | 293 ++++++++++-------- 14 files changed, 321 insertions(+), 244 deletions(-) rename web/src/pages/app/functions/mods/{ => VersionHistoryPanel}/FetchButton/index.tsx (61%) create mode 100644 web/src/pages/app/functions/mods/VersionHistoryPanel/index.tsx diff --git a/web/public/locales/en/translation.json b/web/public/locales/en/translation.json index a8d621b9fb..3c521fa8b3 100644 --- a/web/public/locales/en/translation.json +++ b/web/public/locales/en/translation.json @@ -89,7 +89,7 @@ "Deploy": "Deploy", "DeploySuccess": "Deploy successfully", "Fetch": "Fetch", - "FetchSuccess": "Fetch successfully", + "FetchSuccess": "Restore successfully", "Description": "Function Description", "EditFunction": "Edit Function", "Function": "Function", @@ -114,7 +114,9 @@ "CreateWithAITip": "Create functions with AI assistance", "CreateNow": "Create Now", "CreateFromTemplate": "Create from Template", - "getCodeOnline": "Retrieve the latest released online code" + "getCodeOnline": "Retrieve the latest released online code", + "versionHistory": "Version History", + "Restore": "Restore" }, "HomePanel": { "APP": "Android or iOS app", @@ -374,9 +376,7 @@ "forum": "Forum", "contact": "Contact", "start": "Start Now", - "stars": "3.9K", - "funcTemplate": "Function Market", - "appMarket": "Application Market" + "stars": "3.9K" }, "HomePage": { "slogan": "Write Code as effortlessly as Blogging", @@ -438,64 +438,6 @@ "LinkCopied": "Link Copied", "create success": "create success", "ServerStatus": "STATUS", - "market": { - "explore": "Explore", - "category": "Category", - "appTemplate": "Application Template", - "funcTemplate": "Function Template", - "all": "All", - "market": "Function Market" - }, - "Template": { - "useTemplate": "Use Template", - "DeveloperInformation": "Developer Information", - "Sponsor": "Sponsor", - "Function": "Function", - "Dependency": "Dependency", - "EnvironmentVariables": "Environment Variables", - "public": "public", - "private": "private", - "MyTemplate": "MyTemplate", - "SortOrd": "Sort: ", - "CreateTemplate": "Create Template", - "Details": "Details", - "My": "My Template", - "Edit": "Edit", - "Create": "Create", - "CreatedAt": "Created At", - "byTime": "by Time", - "byFavorites": "by Favorites", - "byLikes": "by Likes", - "Description": "Enter function template description", - "updatedAt": "Updated At", - "UsedBy": "Used By", - "Draft": "Draft", - "AddEnvironmentVariables": "Add Environment Variables", - "Total": "Total", - "UsedSuccessfully": "Used Successfully", - "Select the application you want to use": "Select the application you want to use", - "recentUsed": "Recent Used", - "myStar": "My Star", - "myTemplate": "My Template", - "recommend": "Recommend", - "MostRecent": "Most Recent", - "useTemplateSuccess": "Use Template Success", - "cancelStarSuccess": "Cancel Star Success", - "starSuccess": "Star Success", - "FunctionNameExist": "Function Name Exist", - "AllTemplates": "All Templates", - "Community": "Community Template", - "StaredTemplate": "Stared Template", - "Recent": "Recent Template", - "Earliest": "Earliest", - "MostStars": "Most Stars", - "Latest": "Latest", - "Please select template permission": "Please select template permission", - "Please enter template name": "Please enter template name", - "EditTemplate": "Edit Template", - "DeleteTemplate": "Delete Template", - "FunctionListEmpty": "Please add at least one function." - }, "Fee": "Fee", "PleaseCloseApplicationFirst": "Please close your application first.", "custom": "Custom", @@ -507,13 +449,6 @@ "Send": "Send", "Stop": "Stop", "SendMessagePlaceHolder": "Send a message", - "Publish": "Publish", - "MyTemplate": "My Template", - "Create function template success": "Create function template success", - "Update function template success": "Update function template success", - "Templates": { - "My": "My" - }, "application": { "bonus": "bonus", "Recharge": "Recharge", @@ -524,5 +459,5 @@ "targetCPUUtilization": "CPU Utilization", "CPU Threshold": "CPU Threshold" }, - "Storage Threshold": "Memory Threshold" + "Storage Threshold": "Storage Threshold" } diff --git a/web/public/locales/zh-CN/translation.json b/web/public/locales/zh-CN/translation.json index 24fd6a4362..3d832f4025 100644 --- a/web/public/locales/zh-CN/translation.json +++ b/web/public/locales/zh-CN/translation.json @@ -89,7 +89,7 @@ "Deploy": "发布", "DeploySuccess": "发布成功", "Fetch": "拉取", - "FetchSuccess": "拉取成功", + "FetchSuccess": "恢复成功", "Description": "函数描述", "EditFunction": "编辑函数", "Function": "函数", @@ -114,7 +114,9 @@ "CreateWithAITip": "使用 AI 辅助创建函数", "CreateNow": "立即创建", "CreateFromTemplate": "通过模板创建", - "getCodeOnline": "获取线上最新发布的代码" + "getCodeOnline": "获取线上最新发布的代码", + "versionHistory": "历史版本", + "Restore": "恢复" }, "HomePanel": { "APP": "Android or iOS 应用", diff --git a/web/public/locales/zh/translation.json b/web/public/locales/zh/translation.json index a2143c77b7..8b2a6b3a14 100644 --- a/web/public/locales/zh/translation.json +++ b/web/public/locales/zh/translation.json @@ -89,7 +89,7 @@ "Deploy": "发布", "DeploySuccess": "发布成功", "Fetch": "拉取", - "FetchSuccess": "拉取成功", + "FetchSuccess": "恢复成功", "Description": "函数描述", "EditFunction": "编辑函数", "Function": "函数", @@ -114,7 +114,9 @@ "CreateWithAITip": "使用 AI 辅助创建函数", "CreateNow": "立即创建", "CreateFromTemplate": "通过模板创建", - "getCodeOnline": "获取线上最新发布的代码" + "getCodeOnline": "获取线上最新发布的代码", + "versionHistory": "历史版本", + "Restore": "恢复" }, "HomePanel": { "APP": "Android or iOS 应用", diff --git a/web/src/apis/v1/api-auto.d.ts b/web/src/apis/v1/api-auto.d.ts index 77ef262c72..e3e4f78439 100644 --- a/web/src/apis/v1/api-auto.d.ts +++ b/web/src/apis/v1/api-auto.d.ts @@ -428,6 +428,14 @@ declare namespace Paths { export type Responses = any; } + namespace FunctionControllerGetHistory { + export type QueryParameters = any; + + export type BodyParameters = any; + + export type Responses = any; + } + namespace AuthControllerPat2token { export type QueryParameters = any; diff --git a/web/src/apis/v1/apps.ts b/web/src/apis/v1/apps.ts index c4f8b0f312..122e9352a0 100644 --- a/web/src/apis/v1/apps.ts +++ b/web/src/apis/v1/apps.ts @@ -68,6 +68,23 @@ export async function FunctionControllerFindOne( }); } +export async function FunctionControllerGetHistory( + params: Paths.FunctionControllerGetHistory.BodyParameters, +): Promise<{ + error: string; + data: Paths.FunctionControllerGetHistory.Responses; +}> { + // /v1/apps/{appid}/functions/{name}/history + let _params: { [key: string]: any } = { + appid: useGlobalStore.getState().currentApp?.appid || "", + ...params, + }; + return request(`/v1/apps/${_params.appid}/functions/${_params.name}/history`, { + method: "GET", + params: params, + }); +} + /** * Update a function */ diff --git a/web/src/components/ChargeButton/index.tsx b/web/src/components/ChargeButton/index.tsx index 0aaa2db642..74cb919da0 100644 --- a/web/src/components/ChargeButton/index.tsx +++ b/web/src/components/ChargeButton/index.tsx @@ -1,5 +1,4 @@ import React, { useRef } from "react"; -// import { InfoOutlineIcon } from "@chakra-ui/icons"; import { Button, Input, diff --git a/web/src/components/CommonIcon/index.tsx b/web/src/components/CommonIcon/index.tsx index 1524eb924c..73447e5a5f 100644 --- a/web/src/components/CommonIcon/index.tsx +++ b/web/src/components/CommonIcon/index.tsx @@ -176,6 +176,18 @@ export const DraftIcon = () => { ); }; +export const RecommendIcon = createIcon({ + displayName: "RecommendIcon", + viewBox: "0 0 15 15", + d: "M13.6667 6.66667L13.25 5.75L12.3333 5.33333L13.25 4.91667L13.6667 4L14.0833 4.91667L15 5.33333L14.0833 5.75L13.6667 6.66667ZM11.6667 4L11.0333 2.63333L9.66667 2L11.0333 1.36667L11.6667 0L12.3 1.36667L13.6667 2L12.3 2.63333L11.6667 4ZM5 14.6667C4.63333 14.6667 4.31933 14.536 4.058 14.2747C3.79667 14.0133 3.66622 13.6996 3.66667 13.3333H6.33333C6.33333 13.7 6.20267 14.014 5.94133 14.2753C5.68 14.5367 5.36622 14.6671 5 14.6667ZM2.33333 12.6667V11.3333H7.66667V12.6667H2.33333ZM2.5 10.6667C1.73333 10.2111 1.12489 9.6 0.674667 8.83333C0.224445 8.06667 -0.000443787 7.23333 6.57462e-07 6.33333C6.57462e-07 4.94444 0.486223 3.76378 1.45867 2.79133C2.43111 1.81889 3.61156 1.33289 5 1.33333C6.38889 1.33333 7.56956 1.81956 8.542 2.792C9.51444 3.76444 10.0004 4.94489 10 6.33333C10 7.23333 9.77511 8.06667 9.32533 8.83333C8.87556 9.6 8.26711 10.2111 7.5 10.6667H2.5ZM2.9 9.33333H7.1C7.6 8.97778 7.98622 8.53889 8.25867 8.01667C8.53111 7.49444 8.66711 6.93333 8.66667 6.33333C8.66667 5.31111 8.31111 4.44444 7.6 3.73333C6.88889 3.02222 6.02222 2.66667 5 2.66667C3.97778 2.66667 3.11111 3.02222 2.4 3.73333C1.68889 4.44444 1.33333 5.31111 1.33333 6.33333C1.33333 6.93333 1.46933 7.49444 1.74133 8.01667C2.01333 8.53889 2.39956 8.97778 2.9 9.33333Z", +}); + +export const AutoScalingIcon = createIcon({ + displayName: "AutoScalingIcon", + viewBox: "0 0 12 14", + d: "M1.33333 9.6335L5.33333 11.9502V7.3835L1.33333 5.06683V9.6335ZM6.66667 11.9502L10.6667 9.6335V5.06683L6.66667 7.3835V11.9502ZM2.05 3.95016L6 6.2335L9.95 3.95016L6 1.66683L2.05 3.95016ZM0 10.4168V4.35016C0 4.10572 0.0583333 3.8835 0.175 3.6835C0.291667 3.4835 0.455556 3.32238 0.666667 3.20016L5.33333 0.516829C5.54444 0.394607 5.76667 0.333496 6 0.333496C6.23333 0.333496 6.45556 0.394607 6.66667 0.516829L11.3333 3.20016C11.5444 3.32238 11.7083 3.4835 11.825 3.6835C11.9417 3.8835 12 4.10572 12 4.35016V9.65016C12 9.89461 11.9417 10.1168 11.825 10.3168C11.7083 10.5168 11.5444 10.6779 11.3333 10.8002L6.66667 13.4835C6.45556 13.6057 6.23333 13.6668 6 13.6668C5.76667 13.6668 5.54444 13.6057 5.33333 13.4835L0 10.4168Z", +}); + export const ChatIcon = (props: any) => { return ( diff --git a/web/src/pages/app/functions/mods/DebugPanel/index.tsx b/web/src/pages/app/functions/mods/DebugPanel/index.tsx index 7de39d6cfe..d70f059297 100644 --- a/web/src/pages/app/functions/mods/DebugPanel/index.tsx +++ b/web/src/pages/app/functions/mods/DebugPanel/index.tsx @@ -26,6 +26,7 @@ import { COLOR_MODE, Pages, PanelMinHeight } from "@/constants"; import { useCompileMutation, useUpdateFunctionMutation } from "../../service"; import useFunctionStore from "../../store"; import AIChatPanel from "../AIChatPanel"; +import VersionHistoryPanel from "../VersionHistoryPanel"; import BodyParamsTab from "./BodyParamsTab"; import QueryParamsTab from "./QueryParamsTab"; @@ -42,7 +43,7 @@ const HAS_BODY_PARAMS_METHODS: (TMethod | undefined)[] = ["POST", "PUT", "PATCH" export default function DebugPanel(props: { containerRef: any; showOverlay: boolean }) { const { t } = useTranslation(); const { getFunctionUrl, currentFunction, setCurrentFunction, setCurrentRequestId } = - useFunctionStore((state) => state); + useFunctionStore((state: any) => state); const updateFunctionMutation = useUpdateFunctionMutation(); const globalStore = useGlobalStore((state) => state); @@ -171,6 +172,16 @@ export default function DebugPanel(props: { containerRef: any; showOverlay: bool Laf Pilot + + + {t("FunctionPanel.versionHistory")} + + @@ -321,7 +332,6 @@ export default function DebugPanel(props: { containerRef: any; showOverlay: bool - {/* {props.showOverlay && (
+ + + diff --git a/web/src/pages/app/functions/mods/DeployButton/index.tsx b/web/src/pages/app/functions/mods/DeployButton/index.tsx index cf6e81368d..23d376d7d2 100644 --- a/web/src/pages/app/functions/mods/DeployButton/index.tsx +++ b/web/src/pages/app/functions/mods/DeployButton/index.tsx @@ -11,6 +11,7 @@ import { Tooltip, useDisclosure, } from "@chakra-ui/react"; +import { useQueryClient } from "@tanstack/react-query"; import { t } from "i18next"; import { SynchronizeUpIcon } from "@/components/CommonIcon"; @@ -27,6 +28,7 @@ import useGlobalStore from "@/pages/globalStore"; export default function DeployButton() { const { isOpen, onOpen, onClose } = useDisclosure(); const store = useFunctionStore((state) => state); + const queryClient = useQueryClient(); const functionCache = useFunctionCache(); const headerRef = React.useRef(null); @@ -68,6 +70,7 @@ export default function DeployButton() { functionCache.removeCache(store.currentFunction!._id); onClose(); showSuccess(t("FunctionPanel.DeploySuccess")); + queryClient.invalidateQueries(["useFunctionHistoryQuery"]); } }; diff --git a/web/src/pages/app/functions/mods/EditorPanel/index.tsx b/web/src/pages/app/functions/mods/EditorPanel/index.tsx index 9a4fd61771..087635d234 100644 --- a/web/src/pages/app/functions/mods/EditorPanel/index.tsx +++ b/web/src/pages/app/functions/mods/EditorPanel/index.tsx @@ -11,7 +11,6 @@ import { COLOR_MODE } from "@/constants"; import { useFunctionListQuery } from "../../service"; import useFunctionStore from "../../store"; import DeployButton from "../DeployButton"; -import FetchButton from "../FetchButton"; import CreateModal from "../FunctionPanel/CreateModal"; import PromptModal from "../FunctionPanel/CreateModal/PromptModal"; @@ -62,7 +61,6 @@ function EditorPanel() { - diff --git a/web/src/pages/app/functions/mods/FetchButton/index.tsx b/web/src/pages/app/functions/mods/VersionHistoryPanel/FetchButton/index.tsx similarity index 61% rename from web/src/pages/app/functions/mods/FetchButton/index.tsx rename to web/src/pages/app/functions/mods/VersionHistoryPanel/FetchButton/index.tsx index 5906f86ac5..207a761bdd 100644 --- a/web/src/pages/app/functions/mods/FetchButton/index.tsx +++ b/web/src/pages/app/functions/mods/VersionHistoryPanel/FetchButton/index.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { Button, Modal, @@ -7,52 +8,35 @@ import { ModalFooter, ModalHeader, ModalOverlay, - Tooltip, useDisclosure, } from "@chakra-ui/react"; import { t } from "i18next"; -import { SynchronizeDownIcon } from "@/components/CommonIcon"; import CommonDiffEditor from "@/components/Editor/CommonDiffEditor"; -import { useFunctionDetailQuery } from "../../service"; -import useFunctionStore from "../../store"; +import useFunctionStore from "../../../store"; import useFunctionCache from "@/hooks/useFunctionCache"; import useGlobalStore from "@/pages/globalStore"; -export default function FetchButton() { +export default function FetchButton(props: { children: React.ReactElement; functionCode: string }) { + const { children, functionCode } = props; const functionCache = useFunctionCache(); const { isOpen, onOpen, onClose } = useDisclosure(); const store = useFunctionStore((state) => state); const { currentFunction, updateFunctionCode, setIsFetchButtonClicked } = store; - const functionDetailQuery = useFunctionDetailQuery( - encodeURIComponent(store.currentFunction.name), - { - enabled: isOpen, - }, - ); - const data = functionDetailQuery.data?.data?.source?.code; const { showSuccess } = useGlobalStore((state) => state); return ( <> - - - + }, + })} - {isOpen && !functionDetailQuery.isFetching ? ( + {isOpen ? ( @@ -60,7 +44,7 @@ export default function FetchButton() { { - updateFunctionCode(currentFunction, data || ""); - functionCache.setCache(currentFunction!._id, data || ""); + updateFunctionCode(currentFunction, functionCode || ""); + functionCache.setCache(currentFunction!._id, functionCode || ""); setIsFetchButtonClicked(); onClose(); showSuccess(t("FunctionPanel.FetchSuccess")); }} > - {t("FunctionPanel.ConfirmFetch")} + {t("FunctionPanel.Restore")} diff --git a/web/src/pages/app/functions/mods/VersionHistoryPanel/index.tsx b/web/src/pages/app/functions/mods/VersionHistoryPanel/index.tsx new file mode 100644 index 0000000000..f8c676e464 --- /dev/null +++ b/web/src/pages/app/functions/mods/VersionHistoryPanel/index.tsx @@ -0,0 +1,66 @@ +import { useColorMode } from "@chakra-ui/react"; +import clsx from "clsx"; + +import { formatDate } from "@/utils/format"; + +import { useFunctionHistoryQuery } from "../../service"; +import useFunctionStore from "../../store"; + +import FetchButton from "./FetchButton"; + +export default function VersionHistoryPanel() { + const { currentFunction } = useFunctionStore((state) => state); + const { colorMode } = useColorMode(); + const darkMode = colorMode === "dark"; + + const history = useFunctionHistoryQuery(encodeURIComponent(currentFunction.name), { + enabled: currentFunction.name !== undefined, + }); + + return ( +
+ {!history.isFetching && + history.data?.data.map((item: any, index: number) => { + return ( + +
+
+ +
+
+
+ {formatDate(item.createdAt)} +
+ + #{history.data?.data.length - index} + +
+ + ); + })} +
+ ); +} diff --git a/web/src/pages/app/functions/service.ts b/web/src/pages/app/functions/service.ts index caf9111586..7bb8e9e3ba 100644 --- a/web/src/pages/app/functions/service.ts +++ b/web/src/pages/app/functions/service.ts @@ -7,6 +7,7 @@ import { FunctionControllerCreate, FunctionControllerFindAll, FunctionControllerFindOne, + FunctionControllerGetHistory, FunctionControllerRemove, FunctionControllerUpdate, } from "@/apis/v1/apps"; @@ -45,6 +46,18 @@ export const useFunctionDetailQuery = (name: string, config: any) => { ); }; +export const useFunctionHistoryQuery = (name: string, config: any) => { + return useQuery( + ["useFunctionHistoryQuery", name], + () => { + return FunctionControllerGetHistory({ + name, + }); + }, + config, + ); +}; + export const useCreateFunctionMutation = () => { const store = useFunctionStore(); const queryClient = useQueryClient(); diff --git a/web/src/pages/home/mods/CreateAppModal/index.tsx b/web/src/pages/home/mods/CreateAppModal/index.tsx index 4ef76189ca..4f14d7797e 100644 --- a/web/src/pages/home/mods/CreateAppModal/index.tsx +++ b/web/src/pages/home/mods/CreateAppModal/index.tsx @@ -1,12 +1,10 @@ import React, { useEffect } from "react"; import { Controller, useForm } from "react-hook-form"; -// import { CheckIcon } from "@chakra-ui/icons"; import { Button, FormControl, FormErrorMessage, Input, - // HStack, Modal, ModalBody, ModalCloseButton, @@ -37,6 +35,7 @@ import { t } from "i18next"; import { find } from "lodash"; import ChargeButton from "@/components/ChargeButton"; +import { AutoScalingIcon, RecommendIcon, TextIcon } from "@/components/CommonIcon"; import { APP_STATUS } from "@/constants/index"; import { formatPrice } from "@/utils/format"; @@ -106,17 +105,6 @@ const CreateAppModal = (props: { regionId: string; runtimeId: string; bundleId: string; - cpu: number; - memory: number; - databaseCapacity: number; - storageCapacity: number; - autoscaling: { - enable: boolean; - minReplicas: number; - maxReplicas: number; - targetCPUUtilizationPercentage: number | null; - targetMemoryUtilizationPercentage: number | null; - }; }; const currentRegion = @@ -168,20 +156,13 @@ const CreateAppModal = (props: { minReplicas: application?.bundle.autoscaling?.minReplicas || 1, maxReplicas: application?.bundle.autoscaling?.maxReplicas || 5, targetCPUUtilizationPercentage: - application?.bundle.autoscaling?.targetCPUUtilizationPercentage || 50, + application?.bundle.autoscaling?.targetCPUUtilizationPercentage || null, targetMemoryUtilizationPercentage: application?.bundle.autoscaling?.targetMemoryUtilizationPercentage || null, }; const [bundle, setBundle] = React.useState(defaultBundle); - const [customActive, setCustomActive] = React.useState(false); - - const [tooltipValue, setTooltipValue] = React.useState([ - defaultAutoscaling.minReplicas, - defaultAutoscaling.maxReplicas, - ]); - const [autoscaling, setAutoscaling] = React.useState(defaultAutoscaling); const { showSuccess } = useGlobalStore(); @@ -226,7 +207,6 @@ const CreateAppModal = (props: { res = await updateAppMutation.mutateAsync({ name: data.name, appid: application?.appid, - autoscaling, }); break; @@ -239,12 +219,27 @@ const CreateAppModal = (props: { break; case "create": - res = await createAppMutation.mutateAsync({ - ...data, - ...bundle, - autoscaling, - // duration: subscriptionOption.duration, - }); + if ( + !autoscaling.targetCPUUtilizationPercentage && + !autoscaling.targetMemoryUtilizationPercentage && + autoscaling.enable + ) { + res = await createAppMutation.mutateAsync({ + ...data, + ...bundle, + autoscaling: { + ...autoscaling, + targetCPUUtilizationPercentage: 50, + }, + }); + } else { + res = await createAppMutation.mutateAsync({ + ...data, + ...bundle, + autoscaling, + // duration: subscriptionOption.duration, + }); + } break; default: @@ -255,6 +250,8 @@ const CreateAppModal = (props: { onClose(); if (type !== "create") { showSuccess(t("update success")); + } else { + showSuccess(t("create success")); } // Run every 2 seconds, 2 times in total @@ -291,6 +288,8 @@ const CreateAppModal = (props: { onClick: (event?: any) => { event?.preventDefault(); reset(defaultValues); + setBundle(defaultBundle); + setAutoscaling(defaultAutoscaling); onOpen(); setTimeout(() => { setFocus("name"); @@ -303,7 +302,7 @@ const CreateAppModal = (props: { {title} - + */}