From 413ee2b1f7874fcc4041036f324c8a917d312657 Mon Sep 17 00:00:00 2001 From: jingyang <3161362058@qq.com> Date: Fri, 8 Mar 2024 14:30:00 +0800 Subject: [PATCH 1/4] feat:template support application marketplace category feature Signed-off-by: jingyang <3161362058@qq.com> --- .../deploy/manifests/deploy.yaml.tmpl | 3 + .../template/public/locales/en/common.json | 8 +- .../template/public/locales/zh/common.json | 8 +- .../providers/template/src/api/platform.ts | 7 + .../template/src/components/Banner/index.tsx | 197 ++++++--------- .../src/components/layout/appmenu.tsx | 189 +++++++------- .../template/src/components/layout/index.tsx | 8 +- .../src/components/layout/sidebar.tsx | 112 +++++---- .../providers/template/src/pages/_app.tsx | 21 +- .../template/src/pages/api/listTemplate.ts | 8 +- .../src/pages/api/platform/getSystemConfig.ts | 70 ++++++ .../template/src/pages/api/updateRepo.ts | 4 +- .../src/pages/app/components/list.tsx | 3 +- .../template/src/pages/app/index.tsx | 57 +++-- .../providers/template/src/pages/index.tsx | 230 ++++++++++-------- .../providers/template/src/store/config.ts | 25 ++ .../providers/template/src/store/search.ts | 9 + frontend/providers/template/src/types/app.ts | 25 ++ 18 files changed, 557 insertions(+), 427 deletions(-) create mode 100644 frontend/providers/template/src/pages/api/platform/getSystemConfig.ts create mode 100644 frontend/providers/template/src/store/config.ts diff --git a/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl index 01face4aeb1..e63924c9e08 100644 --- a/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl +++ b/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl @@ -75,6 +75,9 @@ spec: - name: template-frontend-volume mountPath: /config.yaml subPath: config.yaml + - name: template-frontend-volume + mountPath: /app/data/config.json + subPath: config.json - name: template-data mountPath: /app/providers/template/templates volumes: diff --git a/frontend/providers/template/public/locales/en/common.json b/frontend/providers/template/public/locales/en/common.json index 5cd3f377ca4..7aa9899f027 100644 --- a/frontend/providers/template/public/locales/en/common.json +++ b/frontend/providers/template/public/locales/en/common.json @@ -160,7 +160,13 @@ }, "SideBar": { "Applications": "Applications", - "My App": "My Apps" + "My App": "My Apps", + "Backend": "Backend", + "Database": "Database", + "Monitor": "Monitor", + "Frontend": "Frontend", + "Game": "Game", + "AI": "AI" }, "Schedule": "Schedule", "Last Schedule": "Last Schedule", diff --git a/frontend/providers/template/public/locales/zh/common.json b/frontend/providers/template/public/locales/zh/common.json index c8e27200127..8d36a1d1d08 100644 --- a/frontend/providers/template/public/locales/zh/common.json +++ b/frontend/providers/template/public/locales/zh/common.json @@ -166,7 +166,13 @@ }, "SideBar": { "Applications": "所有应用", - "My App": "我的应用" + "My App": "我的应用", + "Backend": "后端", + "Database": "数据库", + "Monitor": "监控", + "Frontend": "前端", + "Game": "游戏", + "AI": "AI" }, "Schedule": "执行周期", "Last Schedule": "上次执行", diff --git a/frontend/providers/template/src/api/platform.ts b/frontend/providers/template/src/api/platform.ts index 43d0163ffe0..bf77dc04859 100644 --- a/frontend/providers/template/src/api/platform.ts +++ b/frontend/providers/template/src/api/platform.ts @@ -1,6 +1,13 @@ import { EnvResponse } from '@/types/index'; import { GET } from '@/services/request'; +import { SystemConfigType, TemplateType } from '@/types/app'; export const updateRepo = () => GET('/api/updateRepo'); +export const getTemplates = () => GET('/api/listTemplate'); + export const getPlatformEnv = () => GET('/api/platform/getEnv'); + +export const getSystemConfig = () => { + return GET('/api/platform/getSystemConfig'); +}; diff --git a/frontend/providers/template/src/components/Banner/index.tsx b/frontend/providers/template/src/components/Banner/index.tsx index d4fd1c898b5..d76c31c03e4 100644 --- a/frontend/providers/template/src/components/Banner/index.tsx +++ b/frontend/providers/template/src/components/Banner/index.tsx @@ -1,51 +1,55 @@ import { Box, Center, Flex, Image, Text } from '@chakra-ui/react'; import { useRef } from 'react'; -import { Autoplay } from 'swiper/modules'; -import { Swiper, SwiperRef, SwiperSlide, useSwiper } from 'swiper/react'; import 'swiper/css'; +import { Autoplay } from 'swiper/modules'; +import { Swiper, SwiperRef, SwiperSlide } from 'swiper/react'; import { ArrowRightIcon } from '../icons/ArrowRight'; +import { useSystemConfigStore } from '@/store/config'; +import { useRouter } from 'next/router'; +import { SlideDataType } from '@/types/app'; +import React from 'react'; -export const SlideData = [ - { - image: - 'https://images.unsplash.com/photo-1546768292-fb12f6c92568?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', - bg: 'linear-gradient(274deg, #824DFF 13.19%, #A97CFF 93.1%)', - title: 'Laf', - desc: 'Laf 是开源的云开发平台,提供云函数、云数据库、云存储等开箱即用的应用资源。让开发者专注于业务开发,无需折腾服务器,快速释放创意。', - borderRadius: '8px', - icon: 'https://laf.run/homepage/logo_icon.svg' - }, - { - image: - 'https://images.unsplash.com/photo-1501446529957-6226bd447c46?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1489&q=80', - bg: 'linear-gradient(274deg, #3770FE 6.31%, #6793FF 93.69%)', - title: 'Umami', - desc: 'Umami is an open source, privacy-focused alternative to Google Analytics.', - borderRadius: '8px', - icon: 'https://jsd.onmicrosoft.cn/gh/umami-software/umami@master/src/assets/logo.svg' - }, - { - image: - 'https://images.unsplash.com/photo-1483729558449-99ef09a8c325?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1350&q=80', - bg: '#824DFF', - title: 'Lobe Chat', - desc: 'LobeChat 是开源的高性能聊天机器人框架,支持语音合成、多模态、可扩展的(Function Call)插件系统。支持一键免费部署私人 ChatGPT/LLM 网页应用程序。', - borderRadius: '8px', - icon: 'https://jsd.onmicrosoft.cn/npm/@lobehub/assets-logo@1.0.0/assets/logo-3d.webp' - }, - { - image: - 'https://images.unsplash.com/photo-1475189778702-5ec9941484ae?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1351&q=80', - bg: '#824DFF', - title: '4444', - desc: 'Build the tools you can’t buy off the shelf', - borderRadius: '8px', - icon: 'https://jsd.onmicrosoft.cn/gh/appsmithorg/appsmith@release/static/logo.png' - } -]; +const Card = ({ item, onClick }: { item: SlideDataType; onClick: () => void }) => { + return ( + + + +
+ logo +
+ + {item.title} + +
+ + {item.desc} + +
+ {`slide-${item.templateName}`} +
+ ); +}; -export default function Banner() { +export default React.memo(function Banner() { const swiperRef = useRef(null); + const { systemConfig } = useSystemConfigStore(); + const router = useRouter(); const handlePrev = () => { if (swiperRef.current) { @@ -59,11 +63,25 @@ export default function Banner() { } }; + const goDeploy = (name: string) => { + if (!name) return; + router.push({ + pathname: '/deploy', + query: { + templateName: name + } + }); + }; + + if (!systemConfig?.showCarousel) { + return null; + } + return ( - {SlideData.map((item, index) => ( + {systemConfig?.slideData.map((item, index) => ( - - - -
- logo -
- - {item.title} - -
- - {item.desc} - -
- {`slide-${index}`} -
- - - -
- logo -
- - {SlideData[(index + 1) % SlideData.length].title} - -
- - {SlideData[(index + 1) % SlideData.length].desc} - -
- {`slide-${index}`} -
+ goDeploy(item.templateName)} /> + + goDeploy( + (systemConfig?.slideData[(index + 1) % systemConfig?.slideData.length]) + .templateName + ) + } + />
))} @@ -199,4 +150,4 @@ export default function Banner() {
); -} +}); diff --git a/frontend/providers/template/src/components/layout/appmenu.tsx b/frontend/providers/template/src/components/layout/appmenu.tsx index 9b3b7848751..454c631ea4b 100644 --- a/frontend/providers/template/src/components/layout/appmenu.tsx +++ b/frontend/providers/template/src/components/layout/appmenu.tsx @@ -1,23 +1,16 @@ import { useCachedStore } from '@/store/cached'; import { useSearchStore } from '@/store/search'; import { getLangStore, setLangStore } from '@/utils/cookieUtils'; -import { Box, Flex, FlexProps, Input, InputGroup, InputLeftElement, Text } from '@chakra-ui/react'; +import { Center, Flex, Icon, Input, InputGroup, InputLeftElement, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; -import { useEffect, useState } from 'react'; -import MyIcon from '../Icon'; import SideBar from './sidebar'; -export default function AppMenu({ isMobile }: { isMobile: boolean }) { +export default function AppMenu() { const { t, i18n } = useTranslation(); const router = useRouter(); - const { searchValue, setSearchValue } = useSearchStore(); + const { setSearchValue } = useSearchStore(); const { insideCloud } = useCachedStore(); - const [isClientRendered, setClientRendered] = useState(false); - - useEffect(() => { - setClientRendered(true); - }, []); const changeI18n = async (newLang: string) => { const lastLang = getLangStore(); @@ -27,101 +20,97 @@ export default function AppMenu({ isMobile }: { isMobile: boolean }) { } }; - const baseStyle: FlexProps = { - position: 'absolute', - justifyContent: 'center', - alignItems: 'center', - bg: 'rgba(150, 153, 180, 0.15)', - userSelect: 'none' - }; - return ( - - {!isMobile && ( - <> - - {t('Templates')} - - - - - - - - { - setSearchValue(e.target.value); - }} - _focus={{ - boxShadow: 'none', - border: '1.5px solid #219BF4', - background: '#FFF' - }} + + + + + - - - )} + + + { + setSearchValue(e.target.value); + }} + _focus={{ + boxShadow: 'none', + border: '1.5px solid #219BF4', + background: '#FFF' + }} + /> + - {isClientRendered && } + - {isMobile ? ( - router.push('/develop')} - > - - - ) : ( - router.push('/develop')} + { + router.push('/app'); + }} + > + - - - {t('develop.Debugging Template')} - - - )} + + + + + {t('SideBar.My App')} + - {isClientRendered && !insideCloud && ( - { - changeI18n(i18n?.language === 'en' ? 'zh' : 'en'); - }} - > - {i18n?.language === 'en' ? 'En' : '中'} - - )} - + {!insideCloud && ( +
{ + changeI18n(i18n?.language === 'en' ? 'zh' : 'en'); + }} + > + {i18n?.language === 'en' ? 'En' : '中'} +
+ )} + + ); } diff --git a/frontend/providers/template/src/components/layout/index.tsx b/frontend/providers/template/src/components/layout/index.tsx index 63ab6ba6bd5..422cb51f6d9 100644 --- a/frontend/providers/template/src/components/layout/index.tsx +++ b/frontend/providers/template/src/components/layout/index.tsx @@ -1,7 +1,5 @@ -import { useGlobalStore } from '@/store/global'; import { Box, Grid } from '@chakra-ui/react'; import { useRouter } from 'next/router'; -import { useMemo } from 'react'; import AppMenu from './appmenu'; const ShowLayoutRoute: Record = { @@ -11,20 +9,18 @@ const ShowLayoutRoute: Record = { }; export default function Layout({ children }: { children: JSX.Element }) { - const { screenWidth } = useGlobalStore(); const router = useRouter(); - const isMobile = useMemo(() => screenWidth < 1024, [screenWidth]); return ( <> {ShowLayoutRoute[router.pathname] ? ( - + <>{children} ) : ( diff --git a/frontend/providers/template/src/components/layout/sidebar.tsx b/frontend/providers/template/src/components/layout/sidebar.tsx index 7b975f64322..8694cb694f8 100644 --- a/frontend/providers/template/src/components/layout/sidebar.tsx +++ b/frontend/providers/template/src/components/layout/sidebar.tsx @@ -1,54 +1,72 @@ -import { useCachedStore } from '@/store/cached'; -import { Flex, Icon, Text } from '@chakra-ui/react'; +import { useSearchStore } from '@/store/search'; +import { ApplicationType } from '@/types/app'; +import { Flex, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; -import { ReactElement, useMemo } from 'react'; +import { useMemo } from 'react'; export type SideBarMenu = { id: string; - url: string; - acriveUrl: string[]; value: string; - icon: ReactElement; + type: ApplicationType; display: boolean; - showLayout: boolean; }; -export default function SideBar({ isMobile }: { isMobile: boolean }) { - const router = useRouter(); +export default function SideBar() { const { t } = useTranslation(); - const { insideCloud } = useCachedStore(); + const { appType, setAppType } = useSearchStore(); + const router = useRouter(); const menus: SideBarMenu[] = useMemo( () => [ { - id: 'Applications', - url: '/', - acriveUrl: ['/', '/deploy'], + id: 'applications', + type: ApplicationType.All, value: 'SideBar.Applications', - icon: ( - - ), - display: true, - showLayout: true + display: true + }, + { + id: 'ai', + type: ApplicationType.AI, + value: 'SideBar.AI', + display: true + }, + { + id: 'game', + type: ApplicationType.Game, + value: 'SideBar.Game', + display: true + }, + { + id: 'monitor', + type: ApplicationType.Monitor, + value: 'SideBar.Monitor', + display: true + }, + { + id: 'database', + type: ApplicationType.Database, + value: 'SideBar.Database', + display: true + }, + { + id: 'frontend', + type: ApplicationType.Frontend, + value: 'SideBar.Frontend', + display: true }, { - id: 'MyApp', - url: '/app', - value: 'SideBar.My App', - acriveUrl: ['/app'], - icon: ( - - ), - display: insideCloud, - showLayout: true + id: 'backend', + type: ApplicationType.Backend, + value: 'SideBar.Backend', + display: true } ], - [insideCloud] + [] ); return ( - + {menus && menus .filter((item) => item.display) @@ -56,42 +74,28 @@ export default function SideBar({ isMobile }: { isMobile: boolean }) { return ( { - router.push(item.url); + router.push('/'); + setAppType(item.type); }} > - - {item.icon} - - {!isMobile && ( - - {t(item.value)} - - )} + {t(item.value)} + ); })} diff --git a/frontend/providers/template/src/pages/_app.tsx b/frontend/providers/template/src/pages/_app.tsx index 06686476104..3e5ee70a88d 100644 --- a/frontend/providers/template/src/pages/_app.tsx +++ b/frontend/providers/template/src/pages/_app.tsx @@ -1,6 +1,5 @@ +import Layout from '@/components/layout'; import { theme } from '@/constants/theme'; -import { useConfirm } from '@/hooks/useConfirm'; -import { useLoading } from '@/hooks/useLoading'; import { useGlobalStore } from '@/store/global'; import { getLangStore, setLangStore } from '@/utils/cookieUtils'; import { ChakraProvider } from '@chakra-ui/react'; @@ -14,11 +13,11 @@ import NProgress from 'nprogress'; //nprogress module import { useEffect, useState } from 'react'; import { EVENT_NAME } from 'sealos-desktop-sdk'; import { createSealosApp, sealosApp } from 'sealos-desktop-sdk/app'; -import Layout from '@/components/layout'; +import { useSystemConfigStore } from '@/store/config'; +import useSessionStore from '@/store/session'; import '@/styles/reset.scss'; import 'nprogress/nprogress.css'; -import useSessionStore from '@/store/session'; //Binding events. Router.events.on('routeChangeStart', () => NProgress.start()); @@ -40,13 +39,13 @@ const App = ({ Component, pageProps, domain }: AppProps & { domain: string }) => const router = useRouter(); const { setSession } = useSessionStore(); const { i18n } = useTranslation(); - const { setScreenWidth, loading, setLastRoute } = useGlobalStore(); - const { Loading } = useLoading(); + const { setScreenWidth, setLastRoute } = useGlobalStore(); + const { initSystemConfig } = useSystemConfigStore(); const [refresh, setRefresh] = useState(false); - const { openConfirm, ConfirmChild } = useConfirm({ - title: 'jump_prompt', - content: 'jump_message' - }); + + useEffect(() => { + initSystemConfig(); + }, []); useEffect(() => { NProgress.start(); @@ -61,7 +60,7 @@ const App = ({ Component, pageProps, domain }: AppProps & { domain: string }) => })(); NProgress.done(); return response; - }, [openConfirm]); + }, []); // add resize event useEffect(() => { diff --git a/frontend/providers/template/src/pages/api/listTemplate.ts b/frontend/providers/template/src/pages/api/listTemplate.ts index 507e1e41229..becca6ddb15 100644 --- a/frontend/providers/template/src/pages/api/listTemplate.ts +++ b/frontend/providers/template/src/pages/api/listTemplate.ts @@ -22,7 +22,7 @@ export const readTemplates = ( jsonPath: string, cdnUrl?: string, blacklistedCategories?: string[] -) => { +): TemplateType[] => { const jsonData = fs.readFileSync(jsonPath, 'utf8'); const _templates: TemplateType[] = JSON.parse(jsonData); @@ -30,7 +30,9 @@ export const readTemplates = ( .filter((item) => { const isBlacklisted = blacklistedCategories && - blacklistedCategories.some((category) => (item?.spec?.categories ?? []).includes(category)); + blacklistedCategories.some((category) => + (item?.spec?.categories ?? []).map((c) => c.toLowerCase()).includes(category) + ); return !item?.spec?.draft && !isBlacklisted; }) .map((item) => { @@ -70,6 +72,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const templates = readTemplates(jsonPath, cdnUrl, blacklistedCategories); jsonRes(res, { data: templates, code: 200 }); } catch (error) { - jsonRes(res, { code: 500, data: 'error' }); + jsonRes(res, { code: 500, data: 'api listTemplate error' }); } } diff --git a/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts b/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts new file mode 100644 index 00000000000..bfc7c1ba0ad --- /dev/null +++ b/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts @@ -0,0 +1,70 @@ +import { jsonRes } from '@/services/backend/response'; +import { SystemConfigType } from '@/types/app'; +import { readFileSync } from 'fs'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const config = await getSystemConfig(); + jsonRes(res, { + data: config, + code: 200 + }); +} + +export const defaultConfig: SystemConfigType = { + showCarousel: true, + slideData: [ + { + image: + 'https://images.unsplash.com/photo-1546768292-fb12f6c92568?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', + bg: 'linear-gradient(274deg, #824DFF 13.19%, #A97CFF 93.1%)', + title: 'Laf', + desc: 'Laf 是开源的云开发平台,提供云函数、云数据库、云存储等开箱即用的应用资源。让开发者专注于业务开发,无需折腾服务器,快速释放创意。', + borderRadius: '8px', + icon: 'https://laf.run/homepage/logo_icon.svg', + templateName: 'laf' + }, + { + image: + 'https://images.unsplash.com/photo-1501446529957-6226bd447c46?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1489&q=80', + bg: 'linear-gradient(274deg, #3770FE 6.31%, #6793FF 93.69%)', + title: 'Umami', + desc: 'Umami is an open source, privacy-focused alternative to Google Analytics.', + borderRadius: '8px', + icon: 'https://jsd.onmicrosoft.cn/gh/umami-software/umami@master/src/assets/logo.svg', + templateName: 'umami' + }, + { + image: + 'https://images.unsplash.com/photo-1483729558449-99ef09a8c325?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1350&q=80', + bg: '#824DFF', + title: 'Lobe Chat', + desc: 'LobeChat 是开源的高性能聊天机器人框架,支持语音合成、多模态、可扩展的(Function Call)插件系统。支持一键免费部署私人 ChatGPT/LLM 网页应用程序。', + borderRadius: '8px', + icon: 'https://jsd.onmicrosoft.cn/npm/@lobehub/assets-logo@1.0.0/assets/logo-3d.webp', + templateName: 'lobe-chat' + }, + { + image: + 'https://images.unsplash.com/photo-1475189778702-5ec9941484ae?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1351&q=80', + bg: '#3770FE', + title: 'FastGPT', + desc: "Improving AI's Ability To Grasp Your Knowledge", + borderRadius: '8px', + icon: 'https://jsd.onmicrosoft.cn/gh/labring/FastGPT@main/.github/imgs/logo.svg', + templateName: 'fastgpt' + } + ] +}; + +export async function getSystemConfig(): Promise { + try { + if (process.env.NODE_ENV === 'development') return defaultConfig; + const filename = '/app/data/config.json'; + const res = JSON.parse(readFileSync(filename, 'utf-8')) as SystemConfigType; + return res; + } catch (error) { + console.log('-getSystemConfig-\n', error); + return defaultConfig; + } +} diff --git a/frontend/providers/template/src/pages/api/updateRepo.ts b/frontend/providers/template/src/pages/api/updateRepo.ts index 290f0fa5b3d..1b0834720bc 100644 --- a/frontend/providers/template/src/pages/api/updateRepo.ts +++ b/frontend/providers/template/src/pages/api/updateRepo.ts @@ -57,8 +57,8 @@ export async function GetTemplateStatic() { } }); return temp; - } catch (error) { - console.log(error, 'error: kubectl get configmap/template-static '); + } catch (error: any) { + console.log('error: kubectl get configmap/template-static \n', error?.body); return {}; } } diff --git a/frontend/providers/template/src/pages/app/components/list.tsx b/frontend/providers/template/src/pages/app/components/list.tsx index e6506a16956..ec44005b378 100644 --- a/frontend/providers/template/src/pages/app/components/list.tsx +++ b/frontend/providers/template/src/pages/app/components/list.tsx @@ -54,11 +54,10 @@ export default function InstanceList() { w={'100%'} gridTemplateColumns="repeat(auto-fill,minmax(344px,1fr))" gridGap={'24px'} - minW={'765px'} pt="24px" - pr="42px" pb="42px" overflow={'auto'} + minW={'712px'} > {data && data?.map((item) => { diff --git a/frontend/providers/template/src/pages/app/index.tsx b/frontend/providers/template/src/pages/app/index.tsx index 0b6b9ead50b..030a21e7a32 100644 --- a/frontend/providers/template/src/pages/app/index.tsx +++ b/frontend/providers/template/src/pages/app/index.tsx @@ -1,12 +1,15 @@ import { serviceSideProps } from '@/utils/i18n'; -import { Box, Flex, Tab, TabIndicator, TabList, Tabs } from '@chakra-ui/react'; +import { Box, Center, Flex, Tab, TabIndicator, TabList, Tabs, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import InstanceList from './components/list'; +import { useRouter } from 'next/router'; +import MyIcon from '@/components/Icon'; export default function MyApp() { const { t } = useTranslation(); const [tabIndex, setTabIndex] = useState(0); + const router = useRouter(); const handleTabsChange = (index: number) => { setTabIndex(index); @@ -15,28 +18,46 @@ export default function MyApp() { return ( - - - - {t('Installed')} - - - - + + + + + {t('Installed')} + + + + +
router.push('/develop')} + > + + + {t('develop.Debugging Template')} + +
+
+
); diff --git a/frontend/providers/template/src/pages/index.tsx b/frontend/providers/template/src/pages/index.tsx index 49f83d36485..33f95526ba1 100644 --- a/frontend/providers/template/src/pages/index.tsx +++ b/frontend/providers/template/src/pages/index.tsx @@ -1,5 +1,5 @@ +import { getTemplates } from '@/api/platform'; import Banner from '@/components/Banner'; -import { GET } from '@/services/request'; import { useCachedStore } from '@/store/cached'; import { useSearchStore } from '@/store/search'; import { TemplateType } from '@/types/app'; @@ -9,11 +9,13 @@ import { Avatar, AvatarGroup, Box, + Center, Flex, Grid, Icon, Image, Spinner, + Tag, Text, Tooltip } from '@chakra-ui/react'; @@ -22,29 +24,33 @@ import { customAlphabet } from 'nanoid'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { MouseEvent, useEffect, useMemo } from 'react'; + const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); export default function AppList() { const { t } = useTranslation(); const router = useRouter(); - const { searchValue } = useSearchStore(); - const { setInsideCloud, insideCloud } = useCachedStore(); + const { searchValue, appType } = useSearchStore(); + const { setInsideCloud } = useCachedStore(); - const { data: FastDeployTemplates, refetch } = useQuery( - ['listTemplte'], - () => GET('/api/listTemplate'), - { - refetchInterval: 5 * 60 * 1000, - staleTime: 5 * 60 * 1000 - } - ); + const { data: FastDeployTemplates, refetch } = useQuery(['listTemplte'], getTemplates, { + refetchInterval: 5 * 60 * 1000, + staleTime: 5 * 60 * 1000 + }); const filterData = useMemo(() => { - const searchResults = FastDeployTemplates?.filter((item: TemplateType) => { + const typeFilteredResults = FastDeployTemplates?.filter((item: TemplateType) => { + if (appType === 'all') return true; + const isMatchType = item?.spec?.categories?.includes(appType); + return isMatchType; + }); + + const searchResults = typeFilteredResults?.filter((item: TemplateType) => { return item?.metadata?.name?.toLowerCase().includes(searchValue.toLowerCase()); }); - return searchValue ? searchResults : FastDeployTemplates; - }, [FastDeployTemplates, searchValue]); + + return searchResults; + }, [FastDeployTemplates, appType, searchValue]); const goDeploy = (name: string) => { if (!name) return; @@ -82,113 +88,125 @@ export default function AppList() { position={'relative'} borderRadius={'12px'} background={'linear-gradient(180deg, #FFF 0%, rgba(255, 255, 255, 0.70) 100%)'} - py={'36px'} + py="24px" px="42px" > - {/* */} + + {!!FastDeployTemplates?.length ? ( - {filterData && - filterData?.map((item: TemplateType) => { - return ( - goDeploy(item?.metadata?.name)} - _hover={{ - borderColor: '#36ADEF', - boxShadow: '0px 4px 5px 0px rgba(185, 196, 205, 0.25)' - }} - key={item?.metadata?.name} - flexDirection={'column'} - minH={'214px'} - h="214px" - p={'24px'} - borderRadius={'8px'} - backgroundColor={'#fff'} - boxShadow={'0px 2px 4px 0px rgba(187, 196, 206, 0.25)'} - border={'1px solid #EAEBF0'} - > - - - - - {item.spec?.deployCount && item.spec?.deployCount > 6 && ( - - - - - - - - +{formatStarNumber(item.spec.deployCount)} - - - )} - - - + {filterData?.map((item: TemplateType) => { + return ( + goDeploy(item?.metadata?.name)} + _hover={{ + borderColor: '#36ADEF', + boxShadow: '0px 4px 5px 0px rgba(185, 196, 205, 0.25)' + }} + key={item?.metadata?.name} + flexDirection={'column'} + h={'184px'} + p={'24px'} + borderRadius={'8px'} + backgroundColor={'#fff'} + boxShadow={'0px 2px 4px 0px rgba(187, 196, 206, 0.25)'} + border={'1px solid #EAEBF0'} + > + + + + + + {item?.spec?.title} + + By {item?.spec?.author} + - - - {item?.spec?.description} - - - - By - {item?.spec?.author} - - goGithub(e, item?.spec?.gitRepo)}> - 6 && ( + + + + + + + + +{formatStarNumber(item.spec.deployCount)} + + + )} + + + {item?.spec?.description} + + + + {item?.spec?.categories?.map((i) => ( + - - - - + {i.toUpperCase()} + + ))} +
goGithub(e, item?.spec?.gitRepo)}> + + + + +
- ); - })} +
+ ); + })}
) : ( diff --git a/frontend/providers/template/src/store/config.ts b/frontend/providers/template/src/store/config.ts new file mode 100644 index 00000000000..9ef5c80adcb --- /dev/null +++ b/frontend/providers/template/src/store/config.ts @@ -0,0 +1,25 @@ +import { getSystemConfig } from '@/api/platform'; +import { SystemConfigType } from '@/types/app'; +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { immer } from 'zustand/middleware/immer'; + +type State = { + systemConfig: SystemConfigType | undefined; + initSystemConfig: () => Promise; +}; + +export const useSystemConfigStore = create()( + devtools( + immer((set, get) => ({ + systemConfig: undefined, + async initSystemConfig() { + const data = await getSystemConfig(); + set((state) => { + state.systemConfig = data; + }); + return data; + } + })) + ) +); diff --git a/frontend/providers/template/src/store/search.ts b/frontend/providers/template/src/store/search.ts index 15ddf01c8b2..398424be34f 100644 --- a/frontend/providers/template/src/store/search.ts +++ b/frontend/providers/template/src/store/search.ts @@ -1,20 +1,29 @@ +import { ApplicationType } from '@/types/app'; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; type State = { searchValue: string; + appType: ApplicationType; setSearchValue: (e: string) => void; + setAppType: (e: ApplicationType) => void; }; export const useSearchStore = create()( devtools( immer((set, get) => ({ searchValue: '', + appType: ApplicationType.All, setSearchValue(e: string) { set((state) => { state.searchValue = e; }); + }, + setAppType(e: ApplicationType) { + set((state) => { + state.appType = e; + }); } })) ) diff --git a/frontend/providers/template/src/types/app.ts b/frontend/providers/template/src/types/app.ts index fe9c1c190d3..d449060a7d1 100644 --- a/frontend/providers/template/src/types/app.ts +++ b/frontend/providers/template/src/types/app.ts @@ -149,3 +149,28 @@ export type InstanceListItemType = { yamlCR: TemplateInstanceType; displayName?: string; }; + +export enum ApplicationType { + All = 'all', + AI = 'ai', + Game = 'game', + Monitor = 'monitor', + Frontend = 'frontend', + Backend = 'backend', + Database = 'database' +} + +export type SlideDataType = { + title: string; + desc: string; + bg: string; + image: string; + borderRadius: string; + icon: string; + templateName: string; +}; + +export type SystemConfigType = { + showCarousel: boolean; + slideData: SlideDataType[]; +}; From 916eada569db3fae964645aed9b7315f766be3bd Mon Sep 17 00:00:00 2001 From: jingyang <3161362058@qq.com> Date: Mon, 11 Mar 2024 10:40:13 +0800 Subject: [PATCH 2/4] fix i18n Signed-off-by: jingyang <3161362058@qq.com> fix swiperjs Signed-off-by: jingyang <3161362058@qq.com> edit github star staletime Signed-off-by: jingyang <3161362058@qq.com> add default app image Signed-off-by: jingyang <3161362058@qq.com> feat categories Signed-off-by: jingyang <3161362058@qq.com> delete image Signed-off-by: jingyang <3161362058@qq.com> --- .../src/components/user_menu/github.tsx | 2 +- frontend/pnpm-lock.yaml | 61 +---- frontend/providers/template/.env.template | 3 +- frontend/providers/template/.gitignore | 1 + .../template/deploy/manifests/appcr.yaml.tmpl | 6 +- .../deploy/manifests/deploy.yaml.tmpl | 5 + frontend/providers/template/package.json | 2 +- .../template/public/locales/en/common.json | 19 +- .../template/public/locales/zh/common.json | 19 +- .../providers/template/src/api/platform.ts | 3 +- .../template/src/components/Banner/index.tsx | 11 +- .../src/components/layout/appmenu.tsx | 19 +- .../src/components/layout/sidebar.tsx | 120 +++----- .../providers/template/src/pages/_app.tsx | 8 +- .../template/src/pages/api/listTemplate.ts | 7 +- .../src/pages/api/platform/getSystemConfig.ts | 51 +--- .../develop/components/BreadCrumbHeader.tsx | 19 +- .../src/pages/develop/components/Form.tsx | 1 + .../providers/template/src/pages/index.tsx | 259 ++++++++++-------- .../src/pages/instance/components/header.tsx | 9 +- .../providers/template/src/store/config.ts | 26 +- frontend/providers/template/src/types/app.ts | 14 +- .../providers/template/src/utils/template.ts | 17 ++ 23 files changed, 328 insertions(+), 354 deletions(-) diff --git a/frontend/desktop/src/components/user_menu/github.tsx b/frontend/desktop/src/components/user_menu/github.tsx index c3e6c8a1254..1bf0610f90b 100644 --- a/frontend/desktop/src/components/user_menu/github.tsx +++ b/frontend/desktop/src/components/user_menu/github.tsx @@ -6,7 +6,7 @@ export default function GithubComponent(props: FlexProps) { ['getGithubStar'], () => fetch('https://api.github.com/repos/labring/sealos').then((res) => res.json()), { - staleTime: 60 * 60 * 1000 + staleTime: 24 * 60 * 60 * 1000 } ); diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 4e65bf35ed8..300e1dff521 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1157,7 +1157,7 @@ importers: version: 4.0.2 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + version: 13.1.6(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -1847,8 +1847,8 @@ importers: specifier: workspace:^ version: link:../../packages/client-sdk swiper: - specifier: ^11.0.5 - version: 11.0.5 + specifier: ^11.0.7 + version: 11.0.7 typescript: specifier: 5.2.2 version: 5.2.2 @@ -10861,7 +10861,6 @@ packages: electron-to-chromium: 1.4.692 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.23.0) - dev: true /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -10982,7 +10981,6 @@ packages: /caniuse-lite@1.0.30001594: resolution: {integrity: sha512-VblSX6nYqyJVs8DKFMldE2IVCJjZ225LW00ydtUWwh5hk9IfkTOffO6r8gJNsH0qqqeAF8KrbMYA2VEwTlGW5g==} - dev: true /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -12151,7 +12149,6 @@ packages: /electron-to-chromium@1.4.692: resolution: {integrity: sha512-d5rZRka9n2Y3MkWRN74IoAsxR0HK3yaAt7T50e3iT9VZmCCQDT3geXUO5ZRMhDToa1pkCeQXuNo+0g+NfDOVPA==} - dev: true /element-resize-detector@1.2.4: resolution: {integrity: sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==} @@ -17319,51 +17316,6 @@ packages: - babel-plugin-macros dev: false - /next@13.1.6(@babel/core@7.23.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): - resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==} - engines: {node: '>=14.6.0'} - hasBin: true - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^6.0.0 || ^7.0.0 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - dependencies: - '@next/env': 13.1.6 - '@swc/helpers': 0.4.14 - caniuse-lite: 1.0.30001565 - postcss: 8.4.14 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - sass: 1.69.5 - styled-jsx: 5.1.1(@babel/core@7.23.5)(react@18.2.0) - optionalDependencies: - '@next/swc-android-arm-eabi': 13.1.6 - '@next/swc-android-arm64': 13.1.6 - '@next/swc-darwin-arm64': 13.1.6 - '@next/swc-darwin-x64': 13.1.6 - '@next/swc-freebsd-x64': 13.1.6 - '@next/swc-linux-arm-gnueabihf': 13.1.6 - '@next/swc-linux-arm64-gnu': 13.1.6 - '@next/swc-linux-arm64-musl': 13.1.6 - '@next/swc-linux-x64-gnu': 13.1.6 - '@next/swc-linux-x64-musl': 13.1.6 - '@next/swc-win32-arm64-msvc': 13.1.6 - '@next/swc-win32-ia32-msvc': 13.1.6 - '@next/swc-win32-x64-msvc': 13.1.6 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: false - /next@13.2.4(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5): resolution: {integrity: sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw==} engines: {node: '>=14.6.0'} @@ -20549,8 +20501,8 @@ packages: stable: 0.1.8 dev: true - /swiper@11.0.5: - resolution: {integrity: sha512-rhCwupqSyRnWrtNzWzemnBLMoyYuoDgGgspAm/8iBD3jCvAWycPLH4Z3TB0O5520DHLzMx94yUMH/B9Efpa48w==} + /swiper@11.0.7: + resolution: {integrity: sha512-cDfglW1B6uSmB6eB6pNmzDTNLmZtu5bWWa1vak0RU7fOI9qHjMzl7gVBvYSl34b0RU2N11HxxETJqQ5LeqI1cA==} engines: {node: '>= 4.7.0'} dev: false @@ -21224,7 +21176,6 @@ packages: browserslist: 4.23.0 escalade: 3.1.1 picocolors: 1.0.0 - dev: true /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -21513,7 +21464,7 @@ packages: '@webassemblyjs/wasm-parser': 1.11.6 acorn: 8.11.2 acorn-import-assertions: 1.9.0(acorn@8.11.2) - browserslist: 4.22.2 + browserslist: 4.23.0 chrome-trace-event: 1.0.3 enhanced-resolve: 5.15.0 es-module-lexer: 1.4.1 diff --git a/frontend/providers/template/.env.template b/frontend/providers/template/.env.template index fe7d1b594ea..7344a111d51 100644 --- a/frontend/providers/template/.env.template +++ b/frontend/providers/template/.env.template @@ -5,4 +5,5 @@ TEMPLATE_REPO_URL="https://github.com/labring-actions/templates" TEMPLATE_REPO_BRANCH="main" # The CDN_URL environment variable is used to specify a CDN address; when set, it replaces raw.githubusercontent.com in the resource loading URL. If not set, the default address is used. CDN_URL= -BLACKLIST_CATEGORIES="" \ No newline at end of file +BLACKLIST_CATEGORIES="" +SIDEBAR_MENU_COUNT="" \ No newline at end of file diff --git a/frontend/providers/template/.gitignore b/frontend/providers/template/.gitignore index 2fcf9ab4d70..46b33cbd5e8 100644 --- a/frontend/providers/template/.gitignore +++ b/frontend/providers/template/.gitignore @@ -42,3 +42,4 @@ yalc.lock templates templates.json +data/config.local.json diff --git a/frontend/providers/template/deploy/manifests/appcr.yaml.tmpl b/frontend/providers/template/deploy/manifests/appcr.yaml.tmpl index 0071b78062f..2a6a49abbf7 100644 --- a/frontend/providers/template/deploy/manifests/appcr.yaml.tmpl +++ b/frontend/providers/template/deploy/manifests/appcr.yaml.tmpl @@ -9,11 +9,11 @@ spec: displayType: normal i18n: zh: - name: 模板市场 + name: 应用商店 zh-Hans: - name: 模板市场 + name: 应用商店 icon: https://template.{{ .cloudDomain }}{{ if .cloudPort }}:{{ .cloudPort }}{{ end }}/logo.svg menuData: nameColor: text-black - name: Templates + name: App Store type: iframe \ No newline at end of file diff --git a/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl b/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl index e63924c9e08..e3f025c2dad 100644 --- a/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl +++ b/frontend/providers/template/deploy/manifests/deploy.yaml.tmpl @@ -13,6 +13,11 @@ metadata: data: config.yaml: |- addr: :3000 + config.json: |- + { + "showCarousel": false, + "slideData": [] + } --- apiVersion: apps/v1 kind: Deployment diff --git a/frontend/providers/template/package.json b/frontend/providers/template/package.json index be1ec5db44a..21fc75ed495 100644 --- a/frontend/providers/template/package.json +++ b/frontend/providers/template/package.json @@ -59,7 +59,7 @@ "remark-unwrap-images": "^3.0.1", "sass": "^1.68.0", "sealos-desktop-sdk": "workspace:^", - "swiper": "^11.0.5", + "swiper": "^11.0.7", "typescript": "5.2.2", "zustand": "^4.4.1" }, diff --git a/frontend/providers/template/public/locales/en/common.json b/frontend/providers/template/public/locales/en/common.json index 7aa9899f027..d0317965c35 100644 --- a/frontend/providers/template/public/locales/en/common.json +++ b/frontend/providers/template/public/locales/en/common.json @@ -139,7 +139,7 @@ "Template Config": "Template Config", "Templates": "Templates", "One Click Deployment": "Pre-build solutions for you and experience one-click deployment of applications", - "Template Name": "Template Name", + "Application Name": "Application Name", "Template List": " Templates", "Configure Project": "Configure Project", "Not need to configure any parameters": "The current application does not need to configure any parameters", @@ -161,12 +161,17 @@ "SideBar": { "Applications": "Applications", "My App": "My Apps", - "Backend": "Backend", - "Database": "Database", - "Monitor": "Monitor", - "Frontend": "Frontend", - "Game": "Game", - "AI": "AI" + "backend": "Backend", + "database": "Database", + "monitor": "Monitor", + "frontend": "Frontend", + "game": "Game", + "ai": "AI", + "tool": "Tool", + "dev-ops": "DevOps", + "blog": "Blog", + "low-code": "LowCode", + "storage": "Storage" }, "Schedule": "Schedule", "Last Schedule": "Last Schedule", diff --git a/frontend/providers/template/public/locales/zh/common.json b/frontend/providers/template/public/locales/zh/common.json index 8d36a1d1d08..93011aa251c 100644 --- a/frontend/providers/template/public/locales/zh/common.json +++ b/frontend/providers/template/public/locales/zh/common.json @@ -146,7 +146,7 @@ "Template Config": "模板配置", "Templates": "模板市场", "One Click Deployment": "为您预先构建解决方案,体验一键部署应用", - "Template Name": "模板名称", + "Application Name": "应用名称", "Template List": " 模板列表", "Not need to configure any parameters": "当前应用不需要配置任何参数", "Do you want to jump to the app details page": "您要跳转到应用详情页吗", @@ -167,12 +167,17 @@ "SideBar": { "Applications": "所有应用", "My App": "我的应用", - "Backend": "后端", - "Database": "数据库", - "Monitor": "监控", - "Frontend": "前端", - "Game": "游戏", - "AI": "AI" + "backend": "后端", + "database": "数据库", + "monitor": "监控", + "frontend": "前端", + "game": "游戏", + "ai": "AI", + "tool": "工具", + "dev-ops": "运维", + "blog": "博客", + "low-code": "低代码", + "storage": "存储" }, "Schedule": "执行周期", "Last Schedule": "上次执行", diff --git a/frontend/providers/template/src/api/platform.ts b/frontend/providers/template/src/api/platform.ts index bf77dc04859..c4eac571190 100644 --- a/frontend/providers/template/src/api/platform.ts +++ b/frontend/providers/template/src/api/platform.ts @@ -4,7 +4,8 @@ import { SystemConfigType, TemplateType } from '@/types/app'; export const updateRepo = () => GET('/api/updateRepo'); -export const getTemplates = () => GET('/api/listTemplate'); +export const getTemplates = () => + GET<{ templates: TemplateType[]; menuKeys: string }>('/api/listTemplate'); export const getPlatformEnv = () => GET('/api/platform/getEnv'); diff --git a/frontend/providers/template/src/components/Banner/index.tsx b/frontend/providers/template/src/components/Banner/index.tsx index d76c31c03e4..106cc3cd385 100644 --- a/frontend/providers/template/src/components/Banner/index.tsx +++ b/frontend/providers/template/src/components/Banner/index.tsx @@ -41,7 +41,14 @@ const Card = ({ item, onClick }: { item: SlideDataType; onClick: () => void }) = {item.desc} - {`slide-${item.templateName}`} + + {`slide-${item.templateName}`}
); }; @@ -99,7 +106,7 @@ export default React.memo(function Banner() { }} modules={[Autoplay]} autoplay={{ - delay: 8000, + delay: 5000, disableOnInteraction: false }} > diff --git a/frontend/providers/template/src/components/layout/appmenu.tsx b/frontend/providers/template/src/components/layout/appmenu.tsx index 454c631ea4b..8018657f046 100644 --- a/frontend/providers/template/src/components/layout/appmenu.tsx +++ b/frontend/providers/template/src/components/layout/appmenu.tsx @@ -5,16 +5,19 @@ import { Center, Flex, Icon, Input, InputGroup, InputLeftElement, Text } from '@ import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import SideBar from './sidebar'; +import { ApplicationType } from '@/types/app'; export default function AppMenu() { const { t, i18n } = useTranslation(); const router = useRouter(); - const { setSearchValue } = useSearchStore(); + const { setSearchValue, setAppType } = useSearchStore(); const { insideCloud } = useCachedStore(); - const changeI18n = async (newLang: string) => { + const changeI18n = () => { const lastLang = getLangStore(); - if (lastLang !== newLang && i18n?.changeLanguage) { + const newLang = lastLang === 'en' ? 'zh' : 'en'; + console.log(lastLang); + if (i18n?.changeLanguage) { i18n.changeLanguage(newLang); setLangStore(newLang); } @@ -40,7 +43,7 @@ export default function AppMenu() { { setSearchValue(e.target.value); }} @@ -66,7 +69,8 @@ export default function AppMenu() { }} alignItems={'center'} onClick={() => { - router.push('/app'); + router.replace('/app'); + setAppType(ApplicationType.MyApp); }} > { - changeI18n(i18n?.language === 'en' ? 'zh' : 'en'); + onClick={(e) => { + e.stopPropagation(); + changeI18n(); }} > {i18n?.language === 'en' ? 'En' : '中'} diff --git a/frontend/providers/template/src/components/layout/sidebar.tsx b/frontend/providers/template/src/components/layout/sidebar.tsx index 8694cb694f8..901fcaf03ff 100644 --- a/frontend/providers/template/src/components/layout/sidebar.tsx +++ b/frontend/providers/template/src/components/layout/sidebar.tsx @@ -1,104 +1,48 @@ +import { useSystemConfigStore } from '@/store/config'; import { useSearchStore } from '@/store/search'; -import { ApplicationType } from '@/types/app'; import { Flex, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; -import { useMemo } from 'react'; - -export type SideBarMenu = { - id: string; - value: string; - type: ApplicationType; - display: boolean; -}; export default function SideBar() { const { t } = useTranslation(); const { appType, setAppType } = useSearchStore(); const router = useRouter(); - - const menus: SideBarMenu[] = useMemo( - () => [ - { - id: 'applications', - type: ApplicationType.All, - value: 'SideBar.Applications', - display: true - }, - { - id: 'ai', - type: ApplicationType.AI, - value: 'SideBar.AI', - display: true - }, - { - id: 'game', - type: ApplicationType.Game, - value: 'SideBar.Game', - display: true - }, - { - id: 'monitor', - type: ApplicationType.Monitor, - value: 'SideBar.Monitor', - display: true - }, - { - id: 'database', - type: ApplicationType.Database, - value: 'SideBar.Database', - display: true - }, - { - id: 'frontend', - type: ApplicationType.Frontend, - value: 'SideBar.Frontend', - display: true - }, - { - id: 'backend', - type: ApplicationType.Backend, - value: 'SideBar.Backend', - display: true - } - ], - [] - ); + const { sideBarMenu } = useSystemConfigStore(); return ( - {menus && - menus - .filter((item) => item.display) - .map((item) => { - return ( - { - router.push('/'); - setAppType(item.type); - }} + {sideBarMenu && + sideBarMenu.map((item) => { + return ( + { + console.log(item.type); + router.replace('/'); + setAppType(item.type); + }} + > + - - {t(item.value)} - - - ); - })} + {t(item.value)} + + + ); + })} ); } diff --git a/frontend/providers/template/src/pages/_app.tsx b/frontend/providers/template/src/pages/_app.tsx index 3e5ee70a88d..16d0123b596 100644 --- a/frontend/providers/template/src/pages/_app.tsx +++ b/frontend/providers/template/src/pages/_app.tsx @@ -35,7 +35,7 @@ const queryClient = new QueryClient({ } }); -const App = ({ Component, pageProps, domain }: AppProps & { domain: string }) => { +const App = ({ Component, pageProps }: AppProps) => { const router = useRouter(); const { setSession } = useSessionStore(); const { i18n } = useTranslation(); @@ -113,7 +113,7 @@ const App = ({ Component, pageProps, domain }: AppProps & { domain: string }) => if (lang) { i18n?.changeLanguage?.(lang); } - }, [refresh, router.pathname]); + }, [i18n, refresh, router.pathname]); return ( <> @@ -134,8 +134,4 @@ const App = ({ Component, pageProps, domain }: AppProps & { domain: string }) => ); }; -App.getInitialProps = async () => { - return { domain: process.env.SEALOS_DOMAIN || 'cloud.sealos.io' }; -}; - export default appWithTranslation(App); diff --git a/frontend/providers/template/src/pages/api/listTemplate.ts b/frontend/providers/template/src/pages/api/listTemplate.ts index becca6ddb15..20a2564a833 100644 --- a/frontend/providers/template/src/pages/api/listTemplate.ts +++ b/frontend/providers/template/src/pages/api/listTemplate.ts @@ -1,6 +1,7 @@ import { jsonRes } from '@/services/backend/response'; import { ApiResp } from '@/services/kubernet'; import { TemplateType } from '@/types/app'; +import { findTopKeyWords } from '@/utils/template'; import { parseGithubUrl } from '@/utils/tools'; import fs from 'fs'; import type { NextApiRequest, NextApiResponse } from 'next'; @@ -54,6 +55,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const blacklistedCategories = process.env.BLACKLIST_CATEGORIES ? process.env.BLACKLIST_CATEGORIES.split(',') : []; + const menuCount = Number(process.env.SIDEBAR_MENU_COUNT) || 10; try { if (!hasAddCron) { @@ -70,7 +72,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } const templates = readTemplates(jsonPath, cdnUrl, blacklistedCategories); - jsonRes(res, { data: templates, code: 200 }); + const categories = templates.map((item) => (item.spec?.categories ? item.spec.categories : [])); + const topKeys = findTopKeyWords(categories, menuCount); + + jsonRes(res, { data: { templates: templates, menuKeys: topKeys.join(',') }, code: 200 }); } catch (error) { jsonRes(res, { code: 500, data: 'api listTemplate error' }); } diff --git a/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts b/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts index bfc7c1ba0ad..56b5b291904 100644 --- a/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts +++ b/frontend/providers/template/src/pages/api/platform/getSystemConfig.ts @@ -1,5 +1,5 @@ import { jsonRes } from '@/services/backend/response'; -import { SystemConfigType } from '@/types/app'; +import { ApplicationType, SystemConfigType } from '@/types/app'; import { readFileSync } from 'fs'; import type { NextApiRequest, NextApiResponse } from 'next'; @@ -12,55 +12,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } export const defaultConfig: SystemConfigType = { - showCarousel: true, - slideData: [ - { - image: - 'https://images.unsplash.com/photo-1546768292-fb12f6c92568?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80', - bg: 'linear-gradient(274deg, #824DFF 13.19%, #A97CFF 93.1%)', - title: 'Laf', - desc: 'Laf 是开源的云开发平台,提供云函数、云数据库、云存储等开箱即用的应用资源。让开发者专注于业务开发,无需折腾服务器,快速释放创意。', - borderRadius: '8px', - icon: 'https://laf.run/homepage/logo_icon.svg', - templateName: 'laf' - }, - { - image: - 'https://images.unsplash.com/photo-1501446529957-6226bd447c46?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1489&q=80', - bg: 'linear-gradient(274deg, #3770FE 6.31%, #6793FF 93.69%)', - title: 'Umami', - desc: 'Umami is an open source, privacy-focused alternative to Google Analytics.', - borderRadius: '8px', - icon: 'https://jsd.onmicrosoft.cn/gh/umami-software/umami@master/src/assets/logo.svg', - templateName: 'umami' - }, - { - image: - 'https://images.unsplash.com/photo-1483729558449-99ef09a8c325?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1350&q=80', - bg: '#824DFF', - title: 'Lobe Chat', - desc: 'LobeChat 是开源的高性能聊天机器人框架,支持语音合成、多模态、可扩展的(Function Call)插件系统。支持一键免费部署私人 ChatGPT/LLM 网页应用程序。', - borderRadius: '8px', - icon: 'https://jsd.onmicrosoft.cn/npm/@lobehub/assets-logo@1.0.0/assets/logo-3d.webp', - templateName: 'lobe-chat' - }, - { - image: - 'https://images.unsplash.com/photo-1475189778702-5ec9941484ae?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=1351&q=80', - bg: '#3770FE', - title: 'FastGPT', - desc: "Improving AI's Ability To Grasp Your Knowledge", - borderRadius: '8px', - icon: 'https://jsd.onmicrosoft.cn/gh/labring/FastGPT@main/.github/imgs/logo.svg', - templateName: 'fastgpt' - } - ] + showCarousel: false, + slideData: [] }; export async function getSystemConfig(): Promise { try { - if (process.env.NODE_ENV === 'development') return defaultConfig; - const filename = '/app/data/config.json'; + const filename = + process.env.NODE_ENV === 'development' ? 'data/config.local.json' : '/app/data/config.json'; const res = JSON.parse(readFileSync(filename, 'utf-8')) as SystemConfigType; return res; } catch (error) { diff --git a/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx b/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx index fbdf651df06..d3ab7981889 100644 --- a/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx +++ b/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx @@ -1,3 +1,5 @@ +import { useSearchStore } from '@/store/search'; +import { ApplicationType } from '@/types/app'; import { Box, Button, Flex, Icon, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; @@ -13,6 +15,7 @@ const BreadCrumbHeader = ({ }) => { const router = useRouter(); const { t } = useTranslation(); + const { setAppType } = useSearchStore(); return ( { + router.push('/'); + setAppType(ApplicationType.All); + }} > - router.push('/')} - > + - router.push('/')}> - {t('Template List')} - + {t('Template List')} / { + if (!formHook) return null; const { t } = useTranslation(); const isShowContent = useMemo( diff --git a/frontend/providers/template/src/pages/index.tsx b/frontend/providers/template/src/pages/index.tsx index 33f95526ba1..6e8436fde2d 100644 --- a/frontend/providers/template/src/pages/index.tsx +++ b/frontend/providers/template/src/pages/index.tsx @@ -1,8 +1,10 @@ import { getTemplates } from '@/api/platform'; import Banner from '@/components/Banner'; +import MyIcon from '@/components/Icon'; import { useCachedStore } from '@/store/cached'; +import { baseSideBarMenu, useSystemConfigStore } from '@/store/config'; import { useSearchStore } from '@/store/search'; -import { TemplateType } from '@/types/app'; +import { ApplicationType, TemplateType } from '@/types/app'; import { serviceSideProps } from '@/utils/i18n'; import { compareFirstLanguages, formatStarNumber } from '@/utils/tools'; import { @@ -32,14 +34,28 @@ export default function AppList() { const router = useRouter(); const { searchValue, appType } = useSearchStore(); const { setInsideCloud } = useCachedStore(); + const { menuKeys, setMenuKeys, setSideBarMenu } = useSystemConfigStore(); - const { data: FastDeployTemplates, refetch } = useQuery(['listTemplte'], getTemplates, { + const { data } = useQuery(['listTemplte'], getTemplates, { refetchInterval: 5 * 60 * 1000, - staleTime: 5 * 60 * 1000 + staleTime: 5 * 60 * 1000, + onSuccess(data) { + if (data.menuKeys && data.menuKeys !== menuKeys) { + const menus = baseSideBarMenu.concat( + data.menuKeys.split(',').map((i) => ({ + id: i, + type: i as ApplicationType, + value: `SideBar.${i}` + })) + ); + setMenuKeys(menuKeys); + setSideBarMenu(menus); + } + } }); const filterData = useMemo(() => { - const typeFilteredResults = FastDeployTemplates?.filter((item: TemplateType) => { + const typeFilteredResults = data?.templates?.filter((item: TemplateType) => { if (appType === 'all') return true; const isMatchType = item?.spec?.categories?.includes(appType); return isMatchType; @@ -50,7 +66,7 @@ export default function AppList() { }); return searchResults; - }, [FastDeployTemplates, appType, searchValue]); + }, [data?.templates, appType, searchValue]); const goDeploy = (name: string) => { if (!name) return; @@ -93,121 +109,136 @@ export default function AppList() { > - {!!FastDeployTemplates?.length ? ( - - {filterData?.map((item: TemplateType) => { - return ( - goDeploy(item?.metadata?.name)} - _hover={{ - borderColor: '#36ADEF', - boxShadow: '0px 4px 5px 0px rgba(185, 196, 205, 0.25)' - }} - key={item?.metadata?.name} - flexDirection={'column'} - h={'184px'} - p={'24px'} - borderRadius={'8px'} - backgroundColor={'#fff'} - boxShadow={'0px 2px 4px 0px rgba(187, 196, 206, 0.25)'} - border={'1px solid #EAEBF0'} - > - - + {filterData?.length && filterData?.length > 0 ? ( + + {filterData?.map((item: TemplateType) => { + return ( + goDeploy(item?.metadata?.name)} + _hover={{ + borderColor: '#36ADEF', + boxShadow: '0px 4px 5px 0px rgba(185, 196, 205, 0.25)' + }} + key={item?.metadata?.name} + flexDirection={'column'} + h={'184px'} + p={'24px'} + borderRadius={'8px'} backgroundColor={'#fff'} - border={' 1px solid rgba(255, 255, 255, 0.50)'} + boxShadow={'0px 2px 4px 0px rgba(187, 196, 206, 0.25)'} + border={'1px solid #EAEBF0'} > - - - - - {item?.spec?.title} - - - By {item?.spec?.author} + + + + + + + {item?.spec?.title} + + + By {item?.spec?.author} + + + {item.spec?.deployCount && item.spec?.deployCount > 6 && ( + + + + + + + + +{formatStarNumber(item.spec.deployCount)} + + + )} + + + {item?.spec?.description} - - {item.spec?.deployCount && item.spec?.deployCount > 6 && ( - - - - - - - - +{formatStarNumber(item.spec.deployCount)} + + {item?.spec?.categories?.map((i) => ( + + {t(`SideBar.${i}`)} + + ))} - - )} - - - {item?.spec?.description} - - - - {item?.spec?.categories?.map((i) => ( - - {i.toUpperCase()} - - ))} +
goGithub(e, item?.spec?.gitRepo)}> + + + + +
+
-
goGithub(e, item?.spec?.gitRepo)}> - - - - -
-
-
- ); - })} - + ); + })} + + ) : ( +
+
+ +
+
+ )} + ) : ( diff --git a/frontend/providers/template/src/pages/instance/components/header.tsx b/frontend/providers/template/src/pages/instance/components/header.tsx index 5974e42b20a..16e8abca994 100644 --- a/frontend/providers/template/src/pages/instance/components/header.tsx +++ b/frontend/providers/template/src/pages/instance/components/header.tsx @@ -3,7 +3,7 @@ import { getInstanceByName } from '@/api/instance'; import { templateDisplayNameKey } from '@/constants/keys'; import { useToast } from '@/hooks/useToast'; import { useResourceStore } from '@/store/resource'; -import { TemplateInstanceType } from '@/types/app'; +import { ApplicationType, TemplateInstanceType } from '@/types/app'; import { Box, Button, @@ -25,6 +25,7 @@ import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; import { useRef, useState } from 'react'; import DelModal from './delDodal'; +import { useSearchStore } from '@/store/search'; export default function Header({ instanceName }: { instanceName: string }) { const router = useRouter(); @@ -39,6 +40,7 @@ export default function Header({ instanceName }: { instanceName: string }) { } = useDisclosure(); const [displayName, setDisplayName] = useState(''); const yamlCR = useRef(); + const { setAppType } = useSearchStore(); const { data, refetch } = useQuery( ['getInstanceByName', instanceName], @@ -93,7 +95,10 @@ export default function Header({ instanceName }: { instanceName: string }) { height="36px" viewBox="0 0 35 36" fill="#5A646E" - onClick={() => router.push('/app')} + onClick={() => { + setAppType(ApplicationType.MyApp); + router.push('/app'); + }} > diff --git a/frontend/providers/template/src/store/config.ts b/frontend/providers/template/src/store/config.ts index 9ef5c80adcb..c7a46eb8881 100644 --- a/frontend/providers/template/src/store/config.ts +++ b/frontend/providers/template/src/store/config.ts @@ -1,24 +1,48 @@ import { getSystemConfig } from '@/api/platform'; -import { SystemConfigType } from '@/types/app'; +import { ApplicationType, SideBarMenu, SystemConfigType } from '@/types/app'; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; type State = { systemConfig: SystemConfigType | undefined; + sideBarMenu: SideBarMenu[]; + menuKeys: string; initSystemConfig: () => Promise; + setSideBarMenu: (newMenu: SideBarMenu[]) => void; + setMenuKeys: (key: string) => void; }; +export const baseSideBarMenu = [ + { + id: 'applications', + type: ApplicationType.All, + value: 'SideBar.Applications' + } +]; + export const useSystemConfigStore = create()( devtools( immer((set, get) => ({ systemConfig: undefined, + menuKeys: '', + sideBarMenu: baseSideBarMenu, async initSystemConfig() { const data = await getSystemConfig(); set((state) => { state.systemConfig = data; }); return data; + }, + setSideBarMenu(newMenu: SideBarMenu[]) { + set((state) => { + state.sideBarMenu = newMenu; + }); + }, + setMenuKeys(key: string) { + set((state) => { + state.menuKeys = key; + }); } })) ) diff --git a/frontend/providers/template/src/types/app.ts b/frontend/providers/template/src/types/app.ts index d449060a7d1..60ae2d33d18 100644 --- a/frontend/providers/template/src/types/app.ts +++ b/frontend/providers/template/src/types/app.ts @@ -157,7 +157,13 @@ export enum ApplicationType { Monitor = 'monitor', Frontend = 'frontend', Backend = 'backend', - Database = 'database' + Database = 'database', + MyApp = 'myapp', + Blog = 'blog', + LowCode = 'low-code', + Storage = 'storage', + Tool = 'tool', + DevOps = 'dev-ops' } export type SlideDataType = { @@ -170,6 +176,12 @@ export type SlideDataType = { templateName: string; }; +export type SideBarMenu = { + id: string; + value: string; + type: ApplicationType; +}; + export type SystemConfigType = { showCarousel: boolean; slideData: SlideDataType[]; diff --git a/frontend/providers/template/src/utils/template.ts b/frontend/providers/template/src/utils/template.ts index be5253b5b3a..9d589a58e7d 100644 --- a/frontend/providers/template/src/utils/template.ts +++ b/frontend/providers/template/src/utils/template.ts @@ -13,3 +13,20 @@ export const getTemplateDefaultValues = (templateSource: TemplateSourceType | un {} ); }; + +export function findTopKeyWords(keywordsList: string[][], topCount: number) { + const flatKeywordsList = keywordsList.filter(Boolean).flat(); + + const keywordCountMap = new Map(); + + flatKeywordsList.forEach((keyword) => { + const count = keywordCountMap.get(keyword) || 0; + keywordCountMap.set(keyword, count + 1); + }); + + const sortedKeywords = Array.from(keywordCountMap.entries()).sort((a, b) => b[1] - a[1]); + + const topKeywords = sortedKeywords.slice(0, topCount).map((entry) => entry[0]); + + return topKeywords; +} From 70f976b576272a13e8d2da46f71bea15013d72f9 Mon Sep 17 00:00:00 2001 From: jingyang <3161362058@qq.com> Date: Tue, 12 Mar 2024 20:44:29 +0800 Subject: [PATCH 3/4] delete log Signed-off-by: jingyang <3161362058@qq.com> --- .../template/public/locales/en/common.json | 4 ++-- .../template/public/locales/zh/common.json | 4 ++-- .../template/src/components/layout/appmenu.tsx | 1 - .../providers/template/src/pages/deploy/index.tsx | 2 +- .../pages/develop/components/BreadCrumbHeader.tsx | 2 +- frontend/providers/template/src/pages/index.tsx | 2 +- frontend/providers/template/src/store/config.ts | 10 +++++----- frontend/providers/template/src/types/app.ts | 15 ++------------- 8 files changed, 14 insertions(+), 26 deletions(-) diff --git a/frontend/providers/template/public/locales/en/common.json b/frontend/providers/template/public/locales/en/common.json index d0317965c35..7f93cf6faa8 100644 --- a/frontend/providers/template/public/locales/en/common.json +++ b/frontend/providers/template/public/locales/en/common.json @@ -140,7 +140,7 @@ "Templates": "Templates", "One Click Deployment": "Pre-build solutions for you and experience one-click deployment of applications", "Application Name": "Application Name", - "Template List": " Templates", + "Application List": " Application List", "Configure Project": "Configure Project", "Not need to configure any parameters": "The current application does not need to configure any parameters", "Do you want to jump to the app details page": "Do you want to jump to the app details page", @@ -203,4 +203,4 @@ "Delete successful": "Delete successful", "Delete Failed": "Delete Failed", "Description": "Description" -} \ No newline at end of file +} diff --git a/frontend/providers/template/public/locales/zh/common.json b/frontend/providers/template/public/locales/zh/common.json index 93011aa251c..cd046f01783 100644 --- a/frontend/providers/template/public/locales/zh/common.json +++ b/frontend/providers/template/public/locales/zh/common.json @@ -147,7 +147,7 @@ "Templates": "模板市场", "One Click Deployment": "为您预先构建解决方案,体验一键部署应用", "Application Name": "应用名称", - "Template List": " 模板列表", + "Application List": " 应用列表", "Not need to configure any parameters": "当前应用不需要配置任何参数", "Do you want to jump to the app details page": "您要跳转到应用详情页吗", "Deploy on sealos": "去sealos部署", @@ -209,4 +209,4 @@ "Delete successful": "删除成功", "Delete Failed": "删除失败", "Description": "描述" -} \ No newline at end of file +} diff --git a/frontend/providers/template/src/components/layout/appmenu.tsx b/frontend/providers/template/src/components/layout/appmenu.tsx index 8018657f046..d6caf95d111 100644 --- a/frontend/providers/template/src/components/layout/appmenu.tsx +++ b/frontend/providers/template/src/components/layout/appmenu.tsx @@ -16,7 +16,6 @@ export default function AppMenu() { const changeI18n = () => { const lastLang = getLangStore(); const newLang = lastLang === 'en' ? 'zh' : 'en'; - console.log(lastLang); if (i18n?.changeLanguage) { i18n.changeLanguage(newLang); setLangStore(newLang); diff --git a/frontend/providers/template/src/pages/deploy/index.tsx b/frontend/providers/template/src/pages/deploy/index.tsx index 19db3fa7857..d08de1c680c 100644 --- a/frontend/providers/template/src/pages/deploy/index.tsx +++ b/frontend/providers/template/src/pages/deploy/index.tsx @@ -301,7 +301,7 @@ export default function EditApp({ appName }: { appName?: string }) { router.push('/')}> - {t('Template List')} + {t('Application List')} / diff --git a/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx b/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx index d3ab7981889..42abc0f577b 100644 --- a/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx +++ b/frontend/providers/template/src/pages/develop/components/BreadCrumbHeader.tsx @@ -52,7 +52,7 @@ const BreadCrumbHeader = ({ - {t('Template List')} + {t('Application List')} / Promise; - setSideBarMenu: (newMenu: SideBarMenu[]) => void; + setSideBarMenu: (newMenu: SideBarMenuType[]) => void; setMenuKeys: (key: string) => void; }; -export const baseSideBarMenu = [ +export const baseSideBarMenu: SideBarMenuType[] = [ { id: 'applications', type: ApplicationType.All, @@ -34,7 +34,7 @@ export const useSystemConfigStore = create()( }); return data; }, - setSideBarMenu(newMenu: SideBarMenu[]) { + setSideBarMenu(newMenu: SideBarMenuType[]) { set((state) => { state.sideBarMenu = newMenu; }); diff --git a/frontend/providers/template/src/types/app.ts b/frontend/providers/template/src/types/app.ts index 60ae2d33d18..fa4f044db17 100644 --- a/frontend/providers/template/src/types/app.ts +++ b/frontend/providers/template/src/types/app.ts @@ -152,18 +152,7 @@ export type InstanceListItemType = { export enum ApplicationType { All = 'all', - AI = 'ai', - Game = 'game', - Monitor = 'monitor', - Frontend = 'frontend', - Backend = 'backend', - Database = 'database', - MyApp = 'myapp', - Blog = 'blog', - LowCode = 'low-code', - Storage = 'storage', - Tool = 'tool', - DevOps = 'dev-ops' + MyApp = 'myapp' } export type SlideDataType = { @@ -176,7 +165,7 @@ export type SlideDataType = { templateName: string; }; -export type SideBarMenu = { +export type SideBarMenuType = { id: string; value: string; type: ApplicationType; From 12a71bb26b5dc1860f0d2feac33a8d0eeb791a99 Mon Sep 17 00:00:00 2001 From: jingyang <3161362058@qq.com> Date: Wed, 13 Mar 2024 00:24:10 +0800 Subject: [PATCH 4/4] done Signed-off-by: jingyang <3161362058@qq.com> --- .../src/components/layout/sidebar.tsx | 8 ++--- .../providers/template/src/pages/index.tsx | 18 ++--------- .../providers/template/src/store/config.ts | 31 ++++++++----------- 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/frontend/providers/template/src/components/layout/sidebar.tsx b/frontend/providers/template/src/components/layout/sidebar.tsx index 901fcaf03ff..8aebcdfa2c4 100644 --- a/frontend/providers/template/src/components/layout/sidebar.tsx +++ b/frontend/providers/template/src/components/layout/sidebar.tsx @@ -1,4 +1,4 @@ -import { useSystemConfigStore } from '@/store/config'; +import { SideBarMenu } from '@/store/config'; import { useSearchStore } from '@/store/search'; import { Flex, Text } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; @@ -8,12 +8,11 @@ export default function SideBar() { const { t } = useTranslation(); const { appType, setAppType } = useSearchStore(); const router = useRouter(); - const { sideBarMenu } = useSystemConfigStore(); return ( - {sideBarMenu && - sideBarMenu.map((item) => { + {SideBarMenu && + SideBarMenu.map((item) => { return ( { - console.log(item.type); router.replace('/'); setAppType(item.type); }} diff --git a/frontend/providers/template/src/pages/index.tsx b/frontend/providers/template/src/pages/index.tsx index ade90208e9b..a36dbaefd59 100644 --- a/frontend/providers/template/src/pages/index.tsx +++ b/frontend/providers/template/src/pages/index.tsx @@ -2,9 +2,8 @@ import { getTemplates } from '@/api/platform'; import Banner from '@/components/Banner'; import MyIcon from '@/components/Icon'; import { useCachedStore } from '@/store/cached'; -import { baseSideBarMenu, useSystemConfigStore } from '@/store/config'; import { useSearchStore } from '@/store/search'; -import { ApplicationType, TemplateType } from '@/types/app'; +import { TemplateType } from '@/types/app'; import { serviceSideProps } from '@/utils/i18n'; import { compareFirstLanguages, formatStarNumber } from '@/utils/tools'; import { @@ -34,24 +33,11 @@ export default function AppList() { const router = useRouter(); const { searchValue, appType } = useSearchStore(); const { setInsideCloud } = useCachedStore(); - const { menuKeys, setMenuKeys, setSideBarMenu } = useSystemConfigStore(); const { data } = useQuery(['listTemplate'], getTemplates, { refetchInterval: 5 * 60 * 1000, staleTime: 5 * 60 * 1000, - onSuccess(data) { - if (data.menuKeys && data.menuKeys !== menuKeys) { - const menus = baseSideBarMenu.concat( - data.menuKeys.split(',').map((i) => ({ - id: i, - type: i as ApplicationType, - value: `SideBar.${i}` - })) - ); - setMenuKeys(menuKeys); - setSideBarMenu(menus); - } - } + retry: 3 }); const filterData = useMemo(() => { diff --git a/frontend/providers/template/src/store/config.ts b/frontend/providers/template/src/store/config.ts index 30cf7a696b1..8db4c817452 100644 --- a/frontend/providers/template/src/store/config.ts +++ b/frontend/providers/template/src/store/config.ts @@ -1,4 +1,4 @@ -import { getSystemConfig } from '@/api/platform'; +import { getSystemConfig, getTemplates } from '@/api/platform'; import { ApplicationType, SideBarMenuType, SystemConfigType } from '@/types/app'; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; @@ -6,14 +6,10 @@ import { immer } from 'zustand/middleware/immer'; type State = { systemConfig: SystemConfigType | undefined; - sideBarMenu: SideBarMenuType[]; - menuKeys: string; initSystemConfig: () => Promise; - setSideBarMenu: (newMenu: SideBarMenuType[]) => void; - setMenuKeys: (key: string) => void; }; -export const baseSideBarMenu: SideBarMenuType[] = [ +export let SideBarMenu = [ { id: 'applications', type: ApplicationType.All, @@ -25,24 +21,23 @@ export const useSystemConfigStore = create()( devtools( immer((set, get) => ({ systemConfig: undefined, - menuKeys: '', - sideBarMenu: baseSideBarMenu, async initSystemConfig() { const data = await getSystemConfig(); + + const { menuKeys } = await getTemplates(); + const menus = SideBarMenu.concat( + menuKeys.split(',').map((i) => ({ + id: i, + type: i as ApplicationType, + value: `SideBar.${i}` + })) + ); + SideBarMenu = menus; + set((state) => { state.systemConfig = data; }); return data; - }, - setSideBarMenu(newMenu: SideBarMenuType[]) { - set((state) => { - state.sideBarMenu = newMenu; - }); - }, - setMenuKeys(key: string) { - set((state) => { - state.menuKeys = key; - }); } })) )