├── .gitignore
├── public
├── demo.png
├── favicon.ico
├── parroticon.png
├── usericon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── sitenodes.json
└── index.html
├── src
├── assets
│ ├── images
│ │ ├── sharing.png
│ │ ├── flowise_logo.png
│ │ ├── open-csg-white.png
│ │ ├── flowise_logo_dark.png
│ │ ├── google-login-white.png
│ │ ├── cURL.svg
│ │ ├── javascript.svg
│ │ ├── python.svg
│ │ └── embed.svg
│ └── scss
│ │ ├── style.scss
│ │ └── _themes-vars.module.scss
├── store
│ ├── context
│ │ ├── MetaContext.js
│ │ ├── ConfirmContext.js
│ │ ├── ConfirmContextProvider.js
│ │ ├── MetaContextProvider.js
│ │ └── ReactFlowContext.js
│ ├── index.js
│ ├── constant.js
│ ├── reducers
│ │ ├── metaRedicer.js
│ │ ├── dialogReducer.js
│ │ ├── canvasReducer.js
│ │ ├── notifierReducer.js
│ │ └── customizationReducer.js
│ ├── reducer.js
│ └── actions.js
├── layout
│ ├── MainLayout
│ │ ├── Header
│ │ │ ├── UserSection
│ │ │ │ └── index.css
│ │ │ └── ProfileSection
│ │ │ │ └── index.css
│ │ ├── LogoSection
│ │ │ └── index.js
│ │ ├── Sidebar
│ │ │ ├── MenuList
│ │ │ │ ├── index.js
│ │ │ │ ├── NavGroup
│ │ │ │ │ └── index.js
│ │ │ │ ├── NavCollapse
│ │ │ │ │ └── index.js
│ │ │ │ └── NavItem
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ └── index.js
│ ├── MinimalLayout
│ │ └── index.js
│ ├── NavigationScroll.js
│ └── NavMotion.js
├── api
│ ├── auth.js
│ ├── config.js
│ ├── workloads.js
│ ├── prediction.js
│ ├── dashboard.js
│ ├── nodes.js
│ ├── jobqueue.js
│ ├── client.js
│ ├── jobs.js
│ └── useraccount.js
├── ui-component
│ ├── dialog
│ │ ├── EditPromptValuesDialog.css
│ │ ├── ConfirmDialog.js
│ │ ├── SourceDocDialog.js
│ │ ├── AdditionalParamsDialog.js
│ │ ├── LoginDialog.js
│ │ ├── AboutDialog.js
│ │ └── SaveChatflowDialog.js
│ ├── markdown
│ │ ├── MemoizedReactMarkdown.js
│ │ └── CodeBlock.js
│ ├── alert
│ │ └── Alert.js
│ ├── button
│ │ ├── StyledFab.js
│ │ ├── StyledButton.js
│ │ └── AnimateButton.js
│ ├── loading
│ │ ├── Loadable.js
│ │ ├── BackdropLoader.js
│ │ └── Loader.js
│ ├── extended
│ │ ├── Logo.js
│ │ ├── Avatar.js
│ │ └── Transitions.js
│ ├── switch
│ │ └── Switch.js
│ ├── tooltip
│ │ └── TooltipWithParser.js
│ ├── checkbox
│ │ └── Checkbox.js
│ ├── cards
│ │ ├── Skeleton
│ │ │ └── ChatflowCard.js
│ │ └── MainCard.js
│ ├── grid
│ │ └── Grid.js
│ ├── table
│ │ └── Table.js
│ ├── editor
│ │ ├── DarkCodeEditor.js
│ │ ├── LightCodeEditor.js
│ │ └── prism-light.css
│ ├── menu
│ │ └── StyledMenu.js
│ ├── json
│ │ └── JsonEditor.js
│ ├── input
│ │ └── Input.js
│ ├── dropdown
│ │ └── Dropdown.js
│ └── file
│ │ └── File.js
├── views
│ ├── dashboard
│ │ └── Loading.js
│ ├── qosmanage
│ │ └── TableToolbar.js
│ ├── callback
│ │ └── index.js
│ ├── usermanage
│ │ └── TableToolbar.js
│ ├── orgmanage
│ │ ├── enhancedtabletoolbar.js
│ │ └── enhancedtablehead.js
│ ├── k8sjobs
│ │ ├── enhanceddeploytablehead.js
│ │ ├── enhancedpodtablehead.js
│ │ ├── enhancedservicetablehead.js
│ │ ├── deploytablebody.js
│ │ ├── podtablebody.js
│ │ ├── enhancedtabletoolbar.js
│ │ └── servicetablebody.js
│ ├── hpcjobs
│ │ ├── JobDetails.js
│ │ └── enhancedtablehead.js
│ └── resources
│ │ ├── enhancedtabletoolbar.js
│ │ └── enhancedtablehead.js
├── menu-items
│ ├── index.js
│ └── dashboard.js
├── auth
│ └── AuthProvider.js
├── hooks
│ ├── useScriptRef.js
│ ├── useApi.js
│ ├── useConfirm.js
│ └── useAuth.js
├── routes
│ ├── index.js
│ ├── AuthRoutes.js
│ └── MainRoutes.js
├── config.js
├── App.js
├── utils
│ ├── usePrompt.js
│ └── useNotifier.js
├── index.js
└── themes
│ ├── index.js
│ ├── typography.js
│ └── palette.js
├── .env
├── .env.example
├── jsconfig.json
├── craco.config.js
├── README.md
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/public/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/public/demo.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/parroticon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/public/parroticon.png
--------------------------------------------------------------------------------
/public/usericon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/public/usericon.png
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/public/favicon-32x32.png
--------------------------------------------------------------------------------
/src/assets/images/sharing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/src/assets/images/sharing.png
--------------------------------------------------------------------------------
/src/assets/images/flowise_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/src/assets/images/flowise_logo.png
--------------------------------------------------------------------------------
/src/assets/images/open-csg-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/src/assets/images/open-csg-white.png
--------------------------------------------------------------------------------
/src/assets/images/flowise_logo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/src/assets/images/flowise_logo_dark.png
--------------------------------------------------------------------------------
/src/assets/images/google-login-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenCSGs/llm-scheduler-ui/HEAD/src/assets/images/google-login-white.png
--------------------------------------------------------------------------------
/src/store/context/MetaContext.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const MetaContext = React.createContext()
4 |
5 | export default MetaContext
6 |
--------------------------------------------------------------------------------
/src/store/context/ConfirmContext.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const ConfirmContext = React.createContext()
4 |
5 | export default ConfirmContext
6 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Header/UserSection/index.css:
--------------------------------------------------------------------------------
1 | .ps__rail-x {
2 | display: none !important;
3 | }
4 | .ps__thumb-x {
5 | display: none !important;
6 | }
7 |
--------------------------------------------------------------------------------
/src/api/auth.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 |
3 | const signIn = (user) => client.post('/api/v1/auth/signin', user)
4 |
5 | export default {
6 | signIn
7 | }
8 |
--------------------------------------------------------------------------------
/src/api/config.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 |
3 | const getConfig = (id) => client.get(`/flow-config/${id}`)
4 |
5 | export default {
6 | getConfig
7 | }
8 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Header/ProfileSection/index.css:
--------------------------------------------------------------------------------
1 | .ps__rail-x {
2 | display: none !important;
3 | }
4 | .ps__thumb-x {
5 | display: none !important;
6 | }
7 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | PORT=8080
2 | REACT_APP_SCHEDULER_API=http://localhost:3000
3 | REACT_APP_ORGNAME=OpenCSG
4 | REACT_APP_APPNAME=StarCloud
5 | REACT_APP_OUTPUT_LOCATION=/home/users
6 |
7 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | PORT=8080
2 | REACT_APP_SCHEDULER_API=http://localhost:3000
3 | REACT_APP_ORGNAME=OpenCSG
4 | REACT_APP_APPNAME=StarCloud
5 | REACT_APP_OUTPUT_LOCATION=/home/users
--------------------------------------------------------------------------------
/src/ui-component/dialog/EditPromptValuesDialog.css:
--------------------------------------------------------------------------------
1 | .editor__textarea {
2 | outline: 0;
3 | }
4 | .editor__textarea::placeholder {
5 | color: rgba(120, 120, 120, 0.5);
6 | }
7 |
--------------------------------------------------------------------------------
/src/api/workloads.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 |
3 | const applyWorkloads = (data) => client.post('/api/v1/k8s/workload/workloads/', data)
4 |
5 | export default {
6 | applyWorkloads,
7 | }
--------------------------------------------------------------------------------
/src/api/prediction.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 |
3 | const sendMessageAndGetPrediction = (id, input) => client.post(`/internal-prediction/${id}`, input)
4 |
5 | export default {
6 | sendMessageAndGetPrediction
7 | }
8 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "commonjs",
5 | "baseUrl": "src"
6 | },
7 | "include": ["src/**/*"],
8 | "exclude": ["node_modules"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/views/dashboard/Loading.js:
--------------------------------------------------------------------------------
1 | import { Box, CircularProgress } from "@mui/material";
2 |
3 | export default () => (
4 |
5 |
6 |
7 | )
--------------------------------------------------------------------------------
/src/menu-items/index.js:
--------------------------------------------------------------------------------
1 | import dashboard from './dashboard'
2 |
3 | // ==============================|| MENU ITEMS ||============================== //
4 |
5 | const menuItems = {
6 | items: [dashboard()]
7 | }
8 |
9 | export default menuItems
10 |
--------------------------------------------------------------------------------
/src/ui-component/markdown/MemoizedReactMarkdown.js:
--------------------------------------------------------------------------------
1 | import { memo } from 'react'
2 | import ReactMarkdown from 'react-markdown'
3 |
4 | export const MemoizedReactMarkdown = memo(ReactMarkdown, (prevProps, nextProps) => prevProps.children === nextProps.children)
5 |
--------------------------------------------------------------------------------
/src/api/dashboard.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 |
3 | const getHPCDashboard = () => client.get('/api/v1/dashboard/hpc')
4 | const getK8SDashboard = () => client.get('/api/v1/dashboard/k8s')
5 |
6 | export default {
7 | getHPCDashboard,
8 | getK8SDashboard
9 | }
--------------------------------------------------------------------------------
/src/ui-component/alert/Alert.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import MuiAlert from '@mui/material/Alert'
3 |
4 | export const Alert = React.forwardRef(function Alert(props, ref) {
5 | return
6 | })
7 |
--------------------------------------------------------------------------------
/src/layout/MinimalLayout/index.js:
--------------------------------------------------------------------------------
1 | import { Outlet } from 'react-router-dom'
2 |
3 | // ==============================|| MINIMAL LAYOUT ||============================== //
4 |
5 | const MinimalLayout = () => (
6 | <>
7 |
8 | >
9 | )
10 |
11 | export default MinimalLayout
12 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux'
2 | import reducer from './reducer'
3 |
4 | // ==============================|| REDUX - MAIN STORE ||============================== //
5 |
6 | const store = createStore(reducer)
7 | const persister = 'Free'
8 |
9 | export { store, persister }
10 |
--------------------------------------------------------------------------------
/public/sitenodes.json:
--------------------------------------------------------------------------------
1 | {
2 | "node": {
3 | "openAI": -1,
4 | "azureOpenAI": -1,
5 | "openAIFunctionAgent": -1,
6 | "azureChatOpenAI": -1,
7 | "chatOpenAI": -1,
8 | "openAIEmbeddings": -1,
9 | "azureOpenAIEmbeddings": -1
10 | },
11 | "tip": "因为政策原因OpenAI相关服务暂时无法使用,敬请谅解"
12 | }
13 |
--------------------------------------------------------------------------------
/src/store/constant.js:
--------------------------------------------------------------------------------
1 | // constant
2 | export const gridSpacing = 3
3 | export const drawerWidth = 260
4 | export const appDrawerWidth = 320
5 | export const maxScroll = 100000
6 | export const baseURL = process.env.REACT_APP_SCHEDULER_API
7 | export const uiBaseURL = window.location.origin
8 | export const slurmContext = '/slurm/v0.0.41'
9 | export const slurmDBContext = '/slurmdb/v0.0.41'
10 |
--------------------------------------------------------------------------------
/src/auth/AuthProvider.js:
--------------------------------------------------------------------------------
1 | import useAuth from 'hooks/useAuth'
2 |
3 | const AuthContext = createContext()
4 |
5 | const useAuthContext = () => useContext(AuthContext)
6 |
7 | const AuthProvider = ({ children }) => {
8 | const auth = useAuth()
9 |
10 | return {children}
11 | }
12 |
13 | export { useAuthContext, AuthProvider }
14 |
--------------------------------------------------------------------------------
/src/ui-component/button/StyledFab.js:
--------------------------------------------------------------------------------
1 | import { styled } from '@mui/material/styles'
2 | import { Fab } from '@mui/material'
3 |
4 | export const StyledFab = styled(Fab)(({ theme, color = 'primary' }) => ({
5 | color: 'white',
6 | backgroundColor: theme.palette[color].main,
7 | '&:hover': {
8 | backgroundColor: theme.palette[color].main,
9 | backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`
10 | }
11 | }))
12 |
--------------------------------------------------------------------------------
/src/ui-component/button/StyledButton.js:
--------------------------------------------------------------------------------
1 | import { styled } from '@mui/material/styles'
2 | import { Button } from '@mui/material'
3 |
4 | export const StyledButton = styled(Button)(({ theme, color = 'primary' }) => ({
5 | color: 'white',
6 | backgroundColor: theme.palette[color].main,
7 | '&:hover': {
8 | backgroundColor: theme.palette[color].main,
9 | backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`
10 | }
11 | }))
12 |
--------------------------------------------------------------------------------
/src/hooks/useScriptRef.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | // ==============================|| ELEMENT REFERENCE HOOKS ||============================== //
4 |
5 | const useScriptRef = () => {
6 | const scripted = useRef(true)
7 |
8 | useEffect(
9 | () => () => {
10 | scripted.current = false
11 | },
12 | []
13 | )
14 |
15 | return scripted
16 | }
17 |
18 | export default useScriptRef
19 |
--------------------------------------------------------------------------------
/src/store/reducers/metaRedicer.js:
--------------------------------------------------------------------------------
1 | import { SET_TAGS } from '../actions'
2 |
3 | export const initialState = {
4 | tags: [],
5 | }
6 |
7 | const metaReducer = (state = initialState, action) => {
8 | switch (action.type) {
9 | case SET_TAGS:
10 | return {
11 | ...state,
12 | tags: [...action.tags]
13 | }
14 | default:
15 | return state
16 | }
17 | }
18 |
19 | export default metaReducer
20 |
--------------------------------------------------------------------------------
/src/ui-component/loading/Loadable.js:
--------------------------------------------------------------------------------
1 | import { Suspense } from 'react'
2 |
3 | // project imports
4 | import Loader from './Loader'
5 |
6 | // ==============================|| LOADABLE - LAZY LOADING ||============================== //
7 |
8 | const Loadable = (Component) =>
9 | function WithLoader(props) {
10 | return (
11 | }>
12 |
13 |
14 | )
15 | }
16 |
17 | export default Loadable
18 |
--------------------------------------------------------------------------------
/src/ui-component/loading/BackdropLoader.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { Backdrop, CircularProgress } from '@mui/material'
3 |
4 | export const BackdropLoader = ({ open }) => {
5 | return (
6 |
7 | theme.zIndex.drawer + 1 }} open={open}>
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | BackdropLoader.propTypes = {
15 | open: PropTypes.bool
16 | }
17 |
--------------------------------------------------------------------------------
/src/api/nodes.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 | import { slurmContext } from 'store/constant'
3 |
4 | const getHPCNodes = () => client.get(slurmContext + '/nodes')
5 |
6 | const getK8sNodes = () => client.get('/api/v1/k8s/nodes')
7 |
8 | const getSpecificNode = (name) => client.get(slurmContext + `/nodes/${name}`)
9 |
10 | const updateNode = (name, body) => client.post(slurmContext + `/nodes/${name}`, body)
11 |
12 | export default {
13 | getHPCNodes,
14 | getK8sNodes,
15 | getSpecificNode,
16 | updateNode
17 | }
18 |
--------------------------------------------------------------------------------
/src/api/jobqueue.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 |
3 | import { slurmContext, slurmDBContext } from 'store/constant'
4 |
5 | const getMyQueue = () => client.get('/api/v1/jobs/queues')
6 |
7 | const getAllQueue = () => client.get(slurmContext + '/partitions')
8 |
9 | const getQosInfo = (name) => client.get(slurmDBContext + `/qos/${name}`)
10 |
11 | const getSpecificQueue = (name) => client.get(`/api/v1/jobs/queue/${name}`)
12 |
13 | export default {
14 | getAllQueue,
15 | getMyQueue,
16 | getQosInfo,
17 | getSpecificQueue
18 | }
19 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/LogoSection/index.js:
--------------------------------------------------------------------------------
1 | import { Link } from 'react-router-dom'
2 |
3 | // material-ui
4 | import { ButtonBase } from '@mui/material'
5 |
6 | // project imports
7 | import config from 'config'
8 | import Logo from 'ui-component/extended/Logo'
9 |
10 | // ==============================|| MAIN LOGO ||============================== //
11 |
12 | const LogoSection = () => (
13 |
14 |
15 |
16 | )
17 |
18 | export default LogoSection
19 |
--------------------------------------------------------------------------------
/src/ui-component/loading/Loader.js:
--------------------------------------------------------------------------------
1 | // material-ui
2 | import LinearProgress from '@mui/material/LinearProgress'
3 | import { styled } from '@mui/material/styles'
4 |
5 | // styles
6 | const LoaderWrapper = styled('div')({
7 | position: 'fixed',
8 | top: 0,
9 | left: 0,
10 | zIndex: 1301,
11 | width: '100%'
12 | })
13 |
14 | // ==============================|| LOADER ||============================== //
15 | const Loader = () => (
16 |
17 |
18 |
19 | )
20 |
21 | export default Loader
22 |
--------------------------------------------------------------------------------
/src/routes/index.js:
--------------------------------------------------------------------------------
1 | import { useRoutes } from 'react-router-dom'
2 |
3 | // routes
4 | import MainRoutes from './MainRoutes'
5 | import AuthRoutes from './AuthRoutes'
6 | import config from 'config'
7 |
8 | // Hooks
9 | import useAuth from 'hooks/useAuth'
10 |
11 | // ==============================|| ROUTING RENDER ||============================== //
12 |
13 | export default function ThemeRoutes() {
14 | const { getIDToken } = useAuth()
15 | const idToken = getIDToken()
16 | const isLoggedIn = idToken ? true : false
17 | return useRoutes([MainRoutes(isLoggedIn), AuthRoutes], config.basename)
18 | }
19 |
--------------------------------------------------------------------------------
/src/store/context/ConfirmContextProvider.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react'
2 | import PropTypes from 'prop-types'
3 | import alertReducer, { initialState } from '../reducers/dialogReducer'
4 | import ConfirmContext from './ConfirmContext'
5 |
6 | const ConfirmContextProvider = ({ children }) => {
7 | const [state, dispatch] = useReducer(alertReducer, initialState)
8 |
9 | return {children}
10 | }
11 |
12 | ConfirmContextProvider.propTypes = {
13 | children: PropTypes.any
14 | }
15 |
16 | export default ConfirmContextProvider
17 |
--------------------------------------------------------------------------------
/craco.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | webpack: {
3 | configure: {
4 | module: {
5 | rules: [
6 | {
7 | test: /\.m?js$/,
8 | resolve: {
9 | fullySpecified: false
10 | }
11 | }
12 | ]
13 | }
14 | },
15 | devServer: {
16 | host: '0.0.0.0',
17 | disableHostCheck: true
18 | },
19 | headers: {
20 | 'X-Frame-Options': 'ALLOWALL'
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/store/reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 |
3 | // reducer import
4 | import customizationReducer from './reducers/customizationReducer'
5 | import canvasReducer from './reducers/canvasReducer'
6 | import notifierReducer from './reducers/notifierReducer'
7 | import dialogReducer from './reducers/dialogReducer'
8 |
9 | // ==============================|| COMBINE REDUCER ||============================== //
10 |
11 | const reducer = combineReducers({
12 | customization: customizationReducer,
13 | canvas: canvasReducer,
14 | notifier: notifierReducer,
15 | dialog: dialogReducer
16 | })
17 |
18 | export default reducer
19 |
--------------------------------------------------------------------------------
/src/assets/images/cURL.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/hooks/useApi.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 |
3 | export default (apiFunc) => {
4 | const [data, setData] = useState(null)
5 | const [error, setError] = useState(null)
6 | const [loading, setLoading] = useState(false)
7 |
8 | const request = async (...args) => {
9 | setLoading(true)
10 | try {
11 | const result = await apiFunc(...args)
12 | setData(result.data)
13 | } catch (err) {
14 | setError(err || 'Unexpected Error!')
15 | } finally {
16 | setLoading(false)
17 | }
18 | }
19 |
20 | return {
21 | data,
22 | error,
23 | loading,
24 | request
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/store/context/MetaContextProvider.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react'
2 | import PropTypes from 'prop-types'
3 | import metaReducer, { initialState } from '../reducers/metaRedicer'
4 | import MetaContext from './MetaContext'
5 | import Cookies from 'js-cookie'
6 | import axios from 'axios'
7 | import { baseURL } from 'store/constant'
8 | import { SET_TAGS } from 'store/actions'
9 |
10 | const MetaContextProvider = ({ children }) => {
11 | const [state, dispatch] = useReducer(metaReducer, initialState)
12 | return {children}
13 | }
14 |
15 | MetaContextProvider.propTypes = {
16 | children: PropTypes.any
17 | }
18 |
19 | export default MetaContextProvider
20 |
--------------------------------------------------------------------------------
/src/layout/NavigationScroll.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { useEffect } from 'react'
3 | import { useLocation } from 'react-router-dom'
4 |
5 | // ==============================|| NAVIGATION SCROLL TO TOP ||============================== //
6 |
7 | const NavigationScroll = ({ children }) => {
8 | const location = useLocation()
9 | const { pathname } = location
10 |
11 | useEffect(() => {
12 | window.scrollTo({
13 | top: 0,
14 | left: 0,
15 | behavior: 'smooth'
16 | })
17 | }, [pathname])
18 |
19 | return children || null
20 | }
21 |
22 | NavigationScroll.propTypes = {
23 | children: PropTypes.node
24 | }
25 |
26 | export default NavigationScroll
27 |
--------------------------------------------------------------------------------
/src/assets/images/javascript.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LLM scheduler UI
2 |
3 | ## Overview
4 |
5 | LLM scheduler UI is an open source web interface to LLM jobs. the scheduler engine is using [Slurm](https://slurm.schedmd.com/) as default engine
6 | It performs large model jobs scheduling based on GPU resources.
7 |
8 | [
](https://www.bilibili.com/video/BV1hH4y1G73D/?vd_source=998127e911ff03443d75c8df2b3d33e8)
9 |
10 | ## Prerequisite
11 |
12 | please setup scheduler API fisrtly [llm-scheduler-api](https://github.com/OpenCSGs/llm-scheduler-api)
13 | then configure the environment variable in .env
14 |
15 | ## Quick Start
16 | 1. install dependency
17 | ```bash
18 | npm install yarn -g
19 | yarn install
20 | ```
21 | 2. install dependency
22 | ```bash
23 | yarn start
24 | ```
25 |
--------------------------------------------------------------------------------
/src/ui-component/extended/Logo.js:
--------------------------------------------------------------------------------
1 | import logo from 'assets/images/flowise_logo.png'
2 | import logoDark from 'assets/images/flowise_logo_dark.png'
3 |
4 | import { useSelector } from 'react-redux'
5 |
6 | // ==============================|| LOGO ||============================== //
7 |
8 | const Logo = () => {
9 | const customization = useSelector((state) => state.customization)
10 |
11 | return (
12 |
13 |

18 |
19 | )
20 | }
21 |
22 | export default Logo
23 |
--------------------------------------------------------------------------------
/src/routes/AuthRoutes.js:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react'
2 |
3 | // project imports
4 | import Loadable from 'ui-component/loading/Loadable'
5 | import MinimalLayout from 'layout/MinimalLayout'
6 |
7 | // canvas routing
8 | const Login = Loadable(lazy(() => import('views/login')))
9 | const Callback = Loadable(lazy(() => import('views/callback')))
10 |
11 | // ==============================|| CANVAS ROUTING ||============================== //
12 |
13 | const AuthRoutes = {
14 | path: '/',
15 | element: ,
16 | children: [
17 | {
18 | path: '/login',
19 | element:
20 | },
21 | {
22 | path: '/callback',
23 | element:
24 | }
25 | ]
26 | }
27 |
28 | export default AuthRoutes
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | export const authConfig = {
2 | serverUrl: process.env.REACT_APP_ENDPOINT,
3 | clientId: process.env.REACT_APP_CLIENTID,
4 | organizationName: process.env.REACT_APP_ORGNAME,
5 | appName: process.env.REACT_APP_APPNAME,
6 | redirectPath: '/callback', // in accordance with casdoor configuration
7 | signinPath: '/api/v1/auth/signin'
8 | }
9 |
10 | export const config = {
11 | // basename: only at build time to set, and Don't add '/' at end off BASENAME for breadcrumbs, also Don't put only '/' use blank('') instead,
12 | basename: '',
13 | defaultPath: '/dashboard',
14 | fontFamily: `'Roboto', sans-serif`,
15 | borderRadius: 12,
16 | ON_PREMISE: JSON.parse(process.env.REACT_APP_ON_PREMISE | true),
17 | FEATURE_TOGGLE_K8S: JSON.parse(process.env.REACT_APP_FEATURE_TOGGLE_K8S | false)
18 | }
19 |
20 | export default config
21 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Sidebar/MenuList/index.js:
--------------------------------------------------------------------------------
1 | // material-ui
2 | import { Typography } from '@mui/material'
3 |
4 | // project imports
5 | import NavGroup from './NavGroup'
6 | import menuItem from 'menu-items'
7 |
8 | // ==============================|| SIDEBAR MENU LIST ||============================== //
9 |
10 | const MenuList = () => {
11 | const navItems = menuItem.items.map((item) => {
12 | switch (item.type) {
13 | case 'group':
14 | return
15 | default:
16 | return (
17 |
18 | Menu Items Error
19 |
20 | )
21 | }
22 | })
23 |
24 | return <>{navItems}>
25 | }
26 |
27 | export default MenuList
28 |
--------------------------------------------------------------------------------
/src/store/reducers/dialogReducer.js:
--------------------------------------------------------------------------------
1 | import { SHOW_CONFIRM, HIDE_CONFIRM } from '../actions'
2 |
3 | export const initialState = {
4 | show: false,
5 | title: '',
6 | description: '',
7 | confirmButtonName: 'OK',
8 | cancelButtonName: 'Cancel'
9 | }
10 |
11 | const alertReducer = (state = initialState, action) => {
12 | switch (action.type) {
13 | case SHOW_CONFIRM:
14 | return {
15 | show: true,
16 | title: action.payload.title,
17 | description: action.payload.description,
18 | confirmButtonName: action.payload.confirmButtonName,
19 | cancelButtonName: action.payload.cancelButtonName
20 | }
21 | case HIDE_CONFIRM:
22 | return initialState
23 | default:
24 | return state
25 | }
26 | }
27 |
28 | export default alertReducer
29 |
--------------------------------------------------------------------------------
/src/ui-component/switch/Switch.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { FormControl, Switch } from '@mui/material'
4 |
5 | export const SwitchInput = ({ value, onChange, disabled = false }) => {
6 | const [myValue, setMyValue] = useState(!!value ?? false)
7 |
8 | return (
9 | <>
10 |
11 | {
15 | setMyValue(event.target.checked)
16 | onChange(event.target.checked)
17 | }}
18 | />
19 |
20 | >
21 | )
22 | }
23 |
24 | SwitchInput.propTypes = {
25 | value: PropTypes.string,
26 | onChange: PropTypes.func,
27 | disabled: PropTypes.bool
28 | }
29 |
--------------------------------------------------------------------------------
/src/store/reducers/canvasReducer.js:
--------------------------------------------------------------------------------
1 | // action - state management
2 | import * as actionTypes from '../actions'
3 |
4 | export const initialState = {
5 | isDirty: false,
6 | chatflow: null
7 | }
8 |
9 | // ==============================|| CANVAS REDUCER ||============================== //
10 |
11 | const canvasReducer = (state = initialState, action) => {
12 | switch (action.type) {
13 | case actionTypes.SET_DIRTY:
14 | return {
15 | ...state,
16 | isDirty: true
17 | }
18 | case actionTypes.REMOVE_DIRTY:
19 | return {
20 | ...state,
21 | isDirty: false
22 | }
23 | case actionTypes.SET_CHATFLOW:
24 | return {
25 | ...state,
26 | chatflow: action.chatflow
27 | }
28 | default:
29 | return state
30 | }
31 | }
32 |
33 | export default canvasReducer
34 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { useSelector } from 'react-redux'
2 |
3 | import { ThemeProvider } from '@mui/material/styles'
4 | import { CssBaseline, StyledEngineProvider } from '@mui/material'
5 |
6 | // routing
7 | import Routes from 'routes'
8 |
9 | // defaultTheme
10 | import themes from 'themes'
11 |
12 | // project imports
13 | import NavigationScroll from 'layout/NavigationScroll'
14 |
15 | // ==============================|| APP ||============================== //
16 |
17 | const App = () => {
18 | const customization = useSelector((state) => state.customization)
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | export default App
33 |
--------------------------------------------------------------------------------
/src/layout/NavMotion.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { motion } from 'framer-motion'
3 |
4 | // ==============================|| ANIMATION FOR CONTENT ||============================== //
5 |
6 | const NavMotion = ({ children }) => {
7 | const motionVariants = {
8 | initial: {
9 | opacity: 0,
10 | scale: 0.99
11 | },
12 | in: {
13 | opacity: 1,
14 | scale: 1
15 | },
16 | out: {
17 | opacity: 0,
18 | scale: 1.01
19 | }
20 | }
21 |
22 | const motionTransition = {
23 | type: 'tween',
24 | ease: 'anticipate',
25 | duration: 0.4
26 | }
27 |
28 | return (
29 |
30 | {children}
31 |
32 | )
33 | }
34 |
35 | NavMotion.propTypes = {
36 | children: PropTypes.node
37 | }
38 |
39 | export default NavMotion
40 |
--------------------------------------------------------------------------------
/src/hooks/useConfirm.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import ConfirmContext from 'store/context/ConfirmContext'
3 | import { HIDE_CONFIRM, SHOW_CONFIRM } from 'store/actions'
4 |
5 | let resolveCallback
6 | const useConfirm = () => {
7 | const [confirmState, dispatch] = useContext(ConfirmContext)
8 |
9 | const closeConfirm = () => {
10 | dispatch({
11 | type: HIDE_CONFIRM
12 | })
13 | }
14 |
15 | const onConfirm = () => {
16 | closeConfirm()
17 | resolveCallback(true)
18 | }
19 |
20 | const onCancel = () => {
21 | closeConfirm()
22 | resolveCallback(false)
23 | }
24 | const confirm = (confirmPayload) => {
25 | dispatch({
26 | type: SHOW_CONFIRM,
27 | payload: confirmPayload
28 | })
29 | return new Promise((res) => {
30 | resolveCallback = res
31 | })
32 | }
33 |
34 | return { confirm, onConfirm, onCancel, confirmState }
35 | }
36 |
37 | export default useConfirm
38 |
--------------------------------------------------------------------------------
/src/assets/images/python.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/ui-component/tooltip/TooltipWithParser.js:
--------------------------------------------------------------------------------
1 | import { Info } from '@mui/icons-material'
2 | import { IconButton, Tooltip } from '@mui/material'
3 | import parser from 'html-react-parser'
4 | import PropTypes from 'prop-types'
5 | import { useSelector } from 'react-redux'
6 |
7 | export const TooltipWithParser = ({ title, style }) => {
8 | const customization = useSelector((state) => state.customization)
9 |
10 | return (
11 |
12 |
13 |
22 |
23 |
24 | )
25 | }
26 |
27 | TooltipWithParser.propTypes = {
28 | title: PropTypes.node,
29 | style: PropTypes.any
30 | }
31 |
--------------------------------------------------------------------------------
/src/utils/usePrompt.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useContext, useEffect } from 'react'
2 | import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'
3 |
4 | // https://stackoverflow.com/questions/71572678/react-router-v-6-useprompt-typescript
5 |
6 | export function useBlocker(blocker, when = true) {
7 | const { navigator } = useContext(NavigationContext)
8 |
9 | useEffect(() => {
10 | if (!when) return
11 |
12 | const unblock = navigator.block((tx) => {
13 | const autoUnblockingTx = {
14 | ...tx,
15 | retry() {
16 | unblock()
17 | tx.retry()
18 | }
19 | }
20 |
21 | blocker(autoUnblockingTx)
22 | })
23 |
24 | return unblock
25 | }, [navigator, blocker, when])
26 | }
27 |
28 | export function usePrompt(message, when = true) {
29 | const blocker = useCallback(
30 | (tx) => {
31 | if (window.confirm(message)) tx.retry()
32 | },
33 | [message]
34 | )
35 |
36 | useBlocker(blocker, when)
37 | }
38 |
--------------------------------------------------------------------------------
/src/ui-component/checkbox/Checkbox.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { FormControlLabel, Checkbox } from '@mui/material'
4 |
5 | export const CheckboxInput = ({ value, label, onChange, disabled = false }) => {
6 | const [myValue, setMyValue] = useState(value)
7 |
8 | return (
9 | <>
10 | {
18 | setMyValue(event.target.checked)
19 | onChange(event.target.checked)
20 | }}
21 | />
22 | }
23 | label={label}
24 | />
25 | >
26 | )
27 | }
28 |
29 | CheckboxInput.propTypes = {
30 | value: PropTypes.bool,
31 | label: PropTypes.string,
32 | onChange: PropTypes.func,
33 | disabled: PropTypes.bool
34 | }
35 |
--------------------------------------------------------------------------------
/src/api/client.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import Cookies from 'js-cookie'
3 | import { baseURL } from 'store/constant'
4 |
5 | const apiClient = axios.create({
6 | baseURL: `${baseURL}`,
7 | headers: {
8 | 'Content-type': 'application/json'
9 | // 'Connection': 'keep-alive'
10 | }
11 | })
12 |
13 | const logout = () => {
14 | localStorage.removeItem('idToken')
15 | window.location.reload()
16 | }
17 |
18 | apiClient.interceptors.request.use(function (config) {
19 | const IDToken = localStorage.getItem('idToken')
20 | if (IDToken) {
21 | const userInfo = JSON.parse(localStorage.getItem('userinfos'))
22 | config.headers['X-SLURM-USER-NAME'] = userInfo.name
23 | config.headers['X-SLURM-USER-TOKEN'] = IDToken
24 | config.headers['isAdmin'] = userInfo.isAdmin
25 | }
26 | return config
27 | })
28 |
29 | apiClient.interceptors.response.use(
30 | (response) => {
31 | return response
32 | },
33 | (error) => {
34 | if (error.response.status === 401) {
35 | logout()
36 | }
37 | return error
38 | }
39 | )
40 |
41 | export default apiClient
42 |
--------------------------------------------------------------------------------
/src/views/qosmanage/TableToolbar.js:
--------------------------------------------------------------------------------
1 | import { alpha } from '@mui/material/styles'
2 | import Toolbar from '@mui/material/Toolbar'
3 | import Typography from '@mui/material/Typography'
4 | import PropTypes from 'prop-types'
5 | import Button from '@mui/material/Button'
6 | import { Remove, Add } from '@mui/icons-material'
7 |
8 | const TableToolbar = (props) => {
9 | const { title, operation } = props
10 |
11 | return (
12 | <>
13 |
20 |
21 | {title}
22 |
23 |
24 |
27 |
28 | >
29 | )
30 | }
31 |
32 | TableToolbar.propTypes = {
33 | title: PropTypes.string
34 | }
35 |
36 | export default TableToolbar
37 |
--------------------------------------------------------------------------------
/src/ui-component/cards/Skeleton/ChatflowCard.js:
--------------------------------------------------------------------------------
1 | // material-ui
2 | import { Card, CardContent, Grid } from '@mui/material'
3 | import Skeleton from '@mui/material/Skeleton'
4 |
5 | // ==============================|| SKELETON - BRIDGE CARD ||============================== //
6 |
7 | const ChatflowCard = () => (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | )
31 |
32 | export default ChatflowCard
33 |
--------------------------------------------------------------------------------
/src/ui-component/grid/Grid.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { DataGrid } from '@mui/x-data-grid'
3 | import { IconPlus } from '@tabler/icons'
4 | import { Button } from '@mui/material'
5 |
6 | export const Grid = ({ columns, rows, style, onRowUpdate, addNewRow }) => {
7 | const handleProcessRowUpdate = (newRow) => {
8 | onRowUpdate(newRow)
9 | return newRow
10 | }
11 |
12 | return (
13 | <>
14 | }>
15 | 增加
16 |
17 | {rows && columns && (
18 |
19 | console.error(error)}
22 | rows={rows}
23 | columns={columns}
24 | />
25 |
26 | )}
27 | >
28 | )
29 | }
30 |
31 | Grid.propTypes = {
32 | rows: PropTypes.array,
33 | columns: PropTypes.array,
34 | style: PropTypes.any,
35 | addNewRow: PropTypes.func,
36 | onRowUpdate: PropTypes.func
37 | }
38 |
--------------------------------------------------------------------------------
/src/views/callback/index.js:
--------------------------------------------------------------------------------
1 | import { AuthCallback } from 'casdoor-react-sdk'
2 |
3 | import React, { useEffect } from 'react'
4 | import { useNavigate } from 'react-router-dom'
5 | import { authConfig, config } from '../../config'
6 | import SDK from 'casdoor-js-sdk'
7 | import { baseURL } from 'store/constant'
8 |
9 | // Hooks
10 | import useAuth from 'hooks/useAuth'
11 |
12 | export default function Callback() {
13 | const navigate = useNavigate()
14 | const { userInfo, setUserInfo, setIDToken } = useAuth()
15 |
16 | return (
17 | {
21 | // @ts-ignore
22 | // save token
23 | setUserInfo(res.userinfos)
24 | setIDToken(res.token)
25 | navigate(config.defaultPath, { replace: true })
26 | }}
27 | isGetTokenSuccessful={(res) => {
28 | // @ts-ignore
29 | // according to the data returned by the server,
30 | // determine whether the `token` is successfully obtained through `code` and `state`.
31 | return res.success === true
32 | }}
33 | />
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import App from './App'
3 | import { store } from 'store'
4 | import { createRoot } from 'react-dom/client'
5 |
6 | // style + assets
7 | import 'assets/scss/style.scss'
8 |
9 | // third party
10 | import { BrowserRouter } from 'react-router-dom'
11 | import { Provider } from 'react-redux'
12 | import { SnackbarProvider } from 'notistack'
13 | import ConfirmContextProvider from 'store/context/ConfirmContextProvider'
14 | import MetaContextProvider from 'store/context/MetaContextProvider'
15 | import { ReactFlowContext } from 'store/context/ReactFlowContext'
16 |
17 | const container = document.getElementById('root')
18 | const root = createRoot(container)
19 |
20 | root.render(
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | )
37 |
--------------------------------------------------------------------------------
/src/store/reducers/notifierReducer.js:
--------------------------------------------------------------------------------
1 | import { ENQUEUE_SNACKBAR, CLOSE_SNACKBAR, REMOVE_SNACKBAR } from '../actions'
2 |
3 | export const initialState = {
4 | notifications: []
5 | }
6 |
7 | const notifierReducer = (state = initialState, action) => {
8 | switch (action.type) {
9 | case ENQUEUE_SNACKBAR:
10 | return {
11 | ...state,
12 | notifications: [
13 | ...state.notifications,
14 | {
15 | key: action.key,
16 | ...action.notification
17 | }
18 | ]
19 | }
20 |
21 | case CLOSE_SNACKBAR:
22 | return {
23 | ...state,
24 | notifications: state.notifications.map((notification) =>
25 | action.dismissAll || notification.key === action.key ? { ...notification, dismissed: true } : { ...notification }
26 | )
27 | }
28 |
29 | case REMOVE_SNACKBAR:
30 | return {
31 | ...state,
32 | notifications: state.notifications.filter((notification) => notification.key !== action.key)
33 | }
34 |
35 | default:
36 | return state
37 | }
38 | }
39 |
40 | export default notifierReducer
41 |
--------------------------------------------------------------------------------
/src/assets/images/embed.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/jobs.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 | import { slurmContext, slurmDBContext } from 'store/constant'
3 |
4 | const getHPCJobs = (params) => client.get(`/api/v1/jobs/list?${params}`)
5 |
6 | const getSpecificJob = (jobid) => client.get(slurmContext + `/job/${jobid}`)
7 |
8 | const getHPCUser = (username) => client.get(slurmDBContext + `/user/${username}?with_assocs`)
9 |
10 | const submitJob = (dataBody) => client.post(slurmContext + `/job/submit`, dataBody)
11 |
12 | const stopJob = (jobid) => client.delete(slurmContext + `/job/${jobid}`)
13 |
14 | const getJobOutput = (jobid, filepath) => client.get(`/api/v1/hpc/job_output/${jobid}?filepath=${filepath}`)
15 |
16 | const getHPCJobFileList = (outputDIR) => client.get(`/api/v1/hpc/filelist?output_dir=${outputDIR}`)
17 |
18 | const getK8SPods = (namespace) => client.get(`/api/v1/k8s/workload/pods/?namespace=${namespace}`)
19 |
20 | const getK8SDeploy = (namespace) => client.get(`/api/v1/k8s/workload/deployments/?namespace=${namespace}`)
21 |
22 | const getK8SSvs = (namespace) => client.get(`/api/v1/k8s/workload/services/?namespace=${namespace}`)
23 |
24 | const getK8SNs = () => client.get(`/api/v1/k8s/workload/namespaces`)
25 |
26 | export default {
27 | getHPCJobs,
28 | getK8SPods,
29 | getK8SDeploy,
30 | getK8SSvs,
31 | getK8SNs,
32 | getSpecificJob,
33 | submitJob,
34 | stopJob,
35 | getJobOutput,
36 | getHPCUser,
37 | getHPCJobFileList
38 | }
39 |
--------------------------------------------------------------------------------
/src/api/useraccount.js:
--------------------------------------------------------------------------------
1 | import client from './client'
2 | import { slurmDBContext, slurmContext } from 'store/constant'
3 |
4 | const getAccounts = (params) => client.get(slurmDBContext + `/accounts?${params}`)
5 | const addAccounts = (data) => client.post(slurmDBContext + '/accounts', data)
6 | const deleteAccount = (account) => client.delete(slurmDBContext + `/account/${account}`)
7 | const setAdmin = (data) => client.post('/api/v1/users/set_admins', data)
8 | const getUsers = () => client.get(slurmDBContext + '/users')
9 | const updateUsers = (data) => client.post(slurmDBContext + '/users', data)
10 | const getQOS = () => client.get(slurmDBContext + '/qos')
11 | const addQOS = (data) => client.post(slurmDBContext + '/qos', data)
12 | const removeQOS = (name) => client.delete(slurmDBContext + `/qos/${name}`)
13 | const getPartitions = () => client.get(slurmContext + '/partitions')
14 | const getAssociation = (params) => client.get(slurmDBContext + '/associations?' + params)
15 | const addAssociations = (data) => client.post(slurmDBContext + '/associations', data)
16 | const removeAssociations = (params) => client.delete(slurmDBContext + '/associations?' + params)
17 |
18 | export default {
19 | getAccounts,
20 | addAccounts,
21 | deleteAccount,
22 | setAdmin,
23 | getUsers,
24 | updateUsers,
25 | getQOS,
26 | addQOS,
27 | removeQOS,
28 | getPartitions,
29 | getAssociation,
30 | addAssociations,
31 | removeAssociations
32 | }
33 |
--------------------------------------------------------------------------------
/src/ui-component/table/Table.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { TableContainer, Table, TableHead, TableCell, TableRow, TableBody, Paper } from '@mui/material'
3 |
4 | export const TableViewOnly = ({ columns, rows }) => {
5 | return (
6 | <>
7 |
8 |
9 |
10 |
11 | {columns.map((col, index) => (
12 | {col.charAt(0).toUpperCase() + col.slice(1)}
13 | ))}
14 |
15 |
16 |
17 | {rows.map((row, index) => (
18 |
19 | {Object.keys(row).map((key, index) => (
20 | {row[key]}
21 | ))}
22 |
23 | ))}
24 |
25 |
26 |
27 | >
28 | )
29 | }
30 |
31 | TableViewOnly.propTypes = {
32 | rows: PropTypes.array,
33 | columns: PropTypes.array
34 | }
35 |
--------------------------------------------------------------------------------
/src/ui-component/dialog/ConfirmDialog.js:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom'
2 | import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material'
3 | import useConfirm from 'hooks/useConfirm'
4 | import { StyledButton } from 'ui-component/button/StyledButton'
5 |
6 | const ConfirmDialog = () => {
7 | const { onConfirm, onCancel, confirmState } = useConfirm()
8 | const portalElement = document.getElementById('portal')
9 |
10 | const component = confirmState.show ? (
11 |
32 | ) : null
33 |
34 | return createPortal(component, portalElement)
35 | }
36 |
37 | export default ConfirmDialog
38 |
--------------------------------------------------------------------------------
/src/ui-component/editor/DarkCodeEditor.js:
--------------------------------------------------------------------------------
1 | import Editor from 'react-simple-code-editor'
2 | import { highlight, languages } from 'prismjs/components/prism-core'
3 | import 'prismjs/components/prism-clike'
4 | import 'prismjs/components/prism-javascript'
5 | import 'prismjs/components/prism-json'
6 | import 'prismjs/components/prism-markup'
7 | import './prism-dark.css'
8 | import PropTypes from 'prop-types'
9 | import { useTheme } from '@mui/material/styles'
10 |
11 | export const DarkCodeEditor = ({ value, placeholder, disabled = false, type, style, onValueChange, onMouseUp, onBlur }) => {
12 | const theme = useTheme()
13 |
14 | return (
15 | highlight(code, type === 'json' ? languages.json : languages.js)}
20 | padding={10}
21 | onValueChange={onValueChange}
22 | onMouseUp={onMouseUp}
23 | onBlur={onBlur}
24 | tabSize={4}
25 | style={{
26 | ...style,
27 | background: theme.palette.codeEditor.main
28 | }}
29 | textareaClassName='editor__textarea'
30 | />
31 | )
32 | }
33 |
34 | DarkCodeEditor.propTypes = {
35 | value: PropTypes.string,
36 | placeholder: PropTypes.string,
37 | disabled: PropTypes.bool,
38 | type: PropTypes.string,
39 | style: PropTypes.object,
40 | onValueChange: PropTypes.func,
41 | onMouseUp: PropTypes.func,
42 | onBlur: PropTypes.func
43 | }
44 |
--------------------------------------------------------------------------------
/src/ui-component/editor/LightCodeEditor.js:
--------------------------------------------------------------------------------
1 | import Editor from 'react-simple-code-editor'
2 | import { highlight, languages } from 'prismjs/components/prism-core'
3 | import 'prismjs/components/prism-clike'
4 | import 'prismjs/components/prism-javascript'
5 | import 'prismjs/components/prism-json'
6 | import 'prismjs/components/prism-markup'
7 | import './prism-light.css'
8 | import PropTypes from 'prop-types'
9 | import { useTheme } from '@mui/material/styles'
10 |
11 | export const LightCodeEditor = ({ value, placeholder, disabled = false, type, style, onValueChange, onMouseUp, onBlur }) => {
12 | const theme = useTheme()
13 |
14 | return (
15 | highlight(code, type === 'json' ? languages.json : languages.js)}
20 | padding={10}
21 | onValueChange={onValueChange}
22 | onMouseUp={onMouseUp}
23 | onBlur={onBlur}
24 | tabSize={4}
25 | style={{
26 | ...style,
27 | background: theme.palette.card.main
28 | }}
29 | textareaClassName='editor__textarea'
30 | />
31 | )
32 | }
33 |
34 | LightCodeEditor.propTypes = {
35 | value: PropTypes.string,
36 | placeholder: PropTypes.string,
37 | disabled: PropTypes.bool,
38 | type: PropTypes.string,
39 | style: PropTypes.object,
40 | onValueChange: PropTypes.func,
41 | onMouseUp: PropTypes.func,
42 | onBlur: PropTypes.func
43 | }
44 |
--------------------------------------------------------------------------------
/src/hooks/useAuth.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import Cookies from 'js-cookie'
3 |
4 | export default function useAuth() {
5 | let parentDomain = window.location.hostname.substring(window.location.hostname.indexOf('.'))
6 |
7 | const getUserInfo = () => {
8 | const userInfoString = localStorage.getItem('userinfos')
9 | if (userInfoString) {
10 | return JSON.parse(userInfoString)
11 | }
12 |
13 | return {}
14 | }
15 |
16 | const [userInfo, setUserInfo] = useState(getUserInfo())
17 |
18 | const saveUserInfo = (userInfo) => {
19 | delete userInfo['token']
20 | localStorage.setItem('userinfos', JSON.stringify(userInfo))
21 | setUserInfo(userInfo)
22 | }
23 |
24 | const getIDToken = () => {
25 | const idToken = localStorage.getItem('idToken')
26 | return idToken
27 | }
28 |
29 | const [IDToken, setIDToken] = useState(getIDToken())
30 |
31 | const saveIDToken = (idToken) => {
32 | localStorage.setItem('idToken', idToken)
33 | setIDToken(idToken)
34 | }
35 |
36 | const removeDToken = () => {
37 | localStorage.removeItem('idToken')
38 | setIDToken(null)
39 | }
40 |
41 | const saveAuthToken = (authToken) => {
42 | setIDToken(authToken)
43 | }
44 |
45 | return {
46 | setUserInfo: saveUserInfo,
47 | setIDToken: saveIDToken,
48 | removeDToken: removeDToken,
49 | setAuthToken: saveAuthToken,
50 | getIDToken: getIDToken,
51 | userInfo,
52 | IDToken
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/store/actions.js:
--------------------------------------------------------------------------------
1 | // action - customization reducer
2 | export const SET_MENU = '@customization/SET_MENU'
3 | export const MENU_TOGGLE = '@customization/MENU_TOGGLE'
4 | export const MENU_OPEN = '@customization/MENU_OPEN'
5 | export const SET_FONT_FAMILY = '@customization/SET_FONT_FAMILY'
6 | export const SET_BORDER_RADIUS = '@customization/SET_BORDER_RADIUS'
7 | export const SET_LAYOUT = '@customization/SET_LAYOUT '
8 | export const SET_DARKMODE = '@customization/SET_DARKMODE'
9 |
10 | // action - canvas reducer
11 | export const SET_DIRTY = '@canvas/SET_DIRTY'
12 | export const REMOVE_DIRTY = '@canvas/REMOVE_DIRTY'
13 | export const SET_CHATFLOW = '@canvas/SET_CHATFLOW'
14 |
15 | // action - notifier reducer
16 | export const ENQUEUE_SNACKBAR = 'ENQUEUE_SNACKBAR'
17 | export const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR'
18 | export const REMOVE_SNACKBAR = 'REMOVE_SNACKBAR'
19 |
20 | // action - dialog reducer
21 | export const SHOW_CONFIRM = 'SHOW_CONFIRM'
22 | export const HIDE_CONFIRM = 'HIDE_CONFIRM'
23 |
24 | // action - meta reducer
25 | export const SET_TAGS = 'SET_TAGS'
26 |
27 | export const enqueueSnackbar = (notification) => {
28 | const key = notification.options && notification.options.key
29 |
30 | return {
31 | type: ENQUEUE_SNACKBAR,
32 | notification: {
33 | ...notification,
34 | key: key || new Date().getTime() + Math.random()
35 | }
36 | }
37 | }
38 |
39 | export const closeSnackbar = (key) => ({
40 | type: CLOSE_SNACKBAR,
41 | dismissAll: !key, // dismiss all if no key has been defined
42 | key
43 | })
44 |
45 | export const removeSnackbar = (key) => ({
46 | type: REMOVE_SNACKBAR,
47 | key
48 | })
49 |
--------------------------------------------------------------------------------
/src/store/reducers/customizationReducer.js:
--------------------------------------------------------------------------------
1 | // project imports
2 | import config from 'config'
3 |
4 | // action - state management
5 | import * as actionTypes from '../actions'
6 |
7 | export const initialState = {
8 | isOpen: [], // for active default menu
9 | fontFamily: config.fontFamily,
10 | borderRadius: config.borderRadius,
11 | opened: true,
12 | isHorizontal: localStorage.getItem('isHorizontal') === 'true' ? true : false,
13 | isDarkMode: localStorage.getItem('isDarkMode') === 'true' ? true : false
14 | }
15 |
16 | // ==============================|| CUSTOMIZATION REDUCER ||============================== //
17 |
18 | const customizationReducer = (state = initialState, action) => {
19 | let id
20 | switch (action.type) {
21 | case actionTypes.MENU_OPEN:
22 | id = action.id
23 | return {
24 | ...state,
25 | isOpen: [id]
26 | }
27 | case actionTypes.SET_MENU:
28 | return {
29 | ...state,
30 | opened: action.opened
31 | }
32 | case actionTypes.SET_FONT_FAMILY:
33 | return {
34 | ...state,
35 | fontFamily: action.fontFamily
36 | }
37 | case actionTypes.SET_BORDER_RADIUS:
38 | return {
39 | ...state,
40 | borderRadius: action.borderRadius
41 | }
42 | case actionTypes.SET_LAYOUT:
43 | return {
44 | ...state,
45 | isHorizontal: action.isHorizontal
46 | }
47 | case actionTypes.SET_DARKMODE:
48 | return {
49 | ...state,
50 | isDarkMode: action.isDarkMode
51 | }
52 | default:
53 | return state
54 | }
55 | }
56 |
57 | export default customizationReducer
58 |
--------------------------------------------------------------------------------
/src/utils/useNotifier.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { useSnackbar } from 'notistack'
4 | import { removeSnackbar } from 'store/actions'
5 |
6 | let displayed = []
7 |
8 | const useNotifier = () => {
9 | const dispatch = useDispatch()
10 | const notifier = useSelector((state) => state.notifier)
11 | const { notifications } = notifier
12 |
13 | const { enqueueSnackbar, closeSnackbar } = useSnackbar()
14 |
15 | const storeDisplayed = (id) => {
16 | displayed = [...displayed, id]
17 | }
18 |
19 | const removeDisplayed = (id) => {
20 | displayed = [...displayed.filter((key) => id !== key)]
21 | }
22 |
23 | React.useEffect(() => {
24 | notifications.forEach(({ key, message, options = {}, dismissed = false }) => {
25 | if (dismissed) {
26 | // dismiss snackbar using notistack
27 | closeSnackbar(key)
28 | return
29 | }
30 |
31 | // do nothing if snackbar is already displayed
32 | if (displayed.includes(key)) return
33 |
34 | // display snackbar using notistack
35 | enqueueSnackbar(message, {
36 | key,
37 | ...options,
38 | onClose: (event, reason, myKey) => {
39 | if (options.onClose) {
40 | options.onClose(event, reason, myKey)
41 | }
42 | },
43 | onExited: (event, myKey) => {
44 | // remove this snackbar from redux store
45 | dispatch(removeSnackbar(myKey))
46 | removeDisplayed(myKey)
47 | }
48 | })
49 |
50 | // keep track of snackbars that we've displayed
51 | storeDisplayed(key)
52 | })
53 | }, [notifications, closeSnackbar, enqueueSnackbar, dispatch])
54 | }
55 |
56 | export default useNotifier
57 |
--------------------------------------------------------------------------------
/src/ui-component/dialog/SourceDocDialog.js:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom'
2 | import { useState, useEffect } from 'react'
3 | import { useSelector } from 'react-redux'
4 | import PropTypes from 'prop-types'
5 | import { Dialog, DialogContent, DialogTitle } from '@mui/material'
6 | import ReactJson from 'react-json-view'
7 |
8 | const SourceDocDialog = ({ show, dialogProps, onCancel }) => {
9 | const portalElement = document.getElementById('portal')
10 | const customization = useSelector((state) => state.customization)
11 |
12 | const [data, setData] = useState({})
13 |
14 | useEffect(() => {
15 | if (dialogProps.data) setData(dialogProps.data)
16 |
17 | return () => {
18 | setData({})
19 | }
20 | }, [dialogProps])
21 |
22 | const component = show ? (
23 |
46 | ) : null
47 |
48 | return createPortal(component, portalElement)
49 | }
50 |
51 | SourceDocDialog.propTypes = {
52 | show: PropTypes.bool,
53 | dialogProps: PropTypes.object,
54 | onCancel: PropTypes.func
55 | }
56 |
57 | export default SourceDocDialog
58 |
--------------------------------------------------------------------------------
/src/ui-component/menu/StyledMenu.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import * as React from 'react'
3 | import PropTypes from 'prop-types'
4 | import { IconButton, Menu, MenuItem } from '@mui/material'
5 | import MoreVertIcon from '@mui/icons-material/MoreVert'
6 |
7 | export const StyledMenu = ({ operation, menuAction, row }) => {
8 | const [anchorEl, setAnchorEl] = React.useState(null)
9 | const open = Boolean(anchorEl)
10 | const handleClick = (event) => {
11 | event.stopPropagation()
12 | setAnchorEl(event.currentTarget)
13 | }
14 | const handleClose = (event, actionId) => {
15 | event.stopPropagation()
16 | setAnchorEl(null)
17 | operation(actionId, row)
18 | }
19 |
20 | return (
21 | <>
22 |
30 |
31 |
32 |
55 | >
56 | )
57 | }
58 |
59 | StyledMenu.propTypes = {
60 | operation: PropTypes.func,
61 | menuAction: PropTypes.array
62 | }
63 |
--------------------------------------------------------------------------------
/src/views/usermanage/TableToolbar.js:
--------------------------------------------------------------------------------
1 | import { alpha } from '@mui/material/styles'
2 | import Toolbar from '@mui/material/Toolbar'
3 | import Typography from '@mui/material/Typography'
4 | import PropTypes from 'prop-types'
5 | import Button from '@mui/material/Button'
6 | import SearchIcon from '@mui/icons-material/Search'
7 | import { IconButton, InputBase, Paper } from '@mui/material'
8 |
9 | const TableToolbar = (props) => {
10 | const { title, setSearchKeyword } = props
11 |
12 | return (
13 | <>
14 |
21 |
22 | {title}
23 |
24 |
25 |
36 | {
41 | setSearchKeyword(event.target.value)
42 | }}
43 | />
44 |
45 |
46 |
47 |
48 |
49 | >
50 | )
51 | }
52 |
53 | TableToolbar.propTypes = {
54 | title: PropTypes.string,
55 | setSearchKeyword: PropTypes.func
56 | }
57 |
58 | export default TableToolbar
59 |
--------------------------------------------------------------------------------
/src/routes/MainRoutes.js:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react'
2 | import { Navigate } from 'react-router-dom'
3 |
4 | // project imports
5 | import Loadable from 'ui-component/loading/Loadable'
6 |
7 | const MainLayout = Loadable(lazy(() => import('layout/MainLayout')))
8 | const Dashboard = Loadable(lazy(() => import('views/dashboard')))
9 | const Resources = Loadable(lazy(() => import('views/resources')))
10 |
11 | const HPCjobs = Loadable(lazy(() => import('views/hpcjobs')))
12 |
13 | const K8sjobs = Loadable(lazy(() => import('views/k8sjobs')))
14 |
15 | // apikey routing
16 | const JobQueue = Loadable(lazy(() => import('views/jobqueue')))
17 |
18 | const OrgManage = Loadable(lazy(() => import('views/orgmanage')))
19 |
20 | const UserManage = Loadable(lazy(() => import('views/usermanage')))
21 |
22 | const QOSManage = Loadable(lazy(() => import('views/qosmanage')))
23 |
24 | // ==============================|| MAIN ROUTING ||============================== //
25 |
26 | const MainRoutes = (isLoggedIn) => {
27 | return {
28 | path: '/',
29 | element: isLoggedIn ? : ,
30 | children: [
31 | {
32 | path: '/',
33 | element:
34 | },
35 | {
36 | path: '/dashboard',
37 | element:
38 | },
39 | {
40 | path: '/resources',
41 | element:
42 | },
43 | {
44 | path: '/hpcjobs',
45 | element:
46 | },
47 | {
48 | path: '/k8sjobs',
49 | element:
50 | },
51 | {
52 | path: '/jobqueue',
53 | element:
54 | },
55 | {
56 | path: '/orgmanage',
57 | element:
58 | },
59 | {
60 | path: '/usermanage',
61 | element:
62 | },
63 | {
64 | path: '/qosmanage',
65 | element:
66 | }
67 | ]
68 | }
69 | }
70 |
71 | export default MainRoutes
72 |
--------------------------------------------------------------------------------
/src/ui-component/extended/Avatar.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | // material-ui
4 | import { useTheme } from '@mui/material/styles'
5 | import MuiAvatar from '@mui/material/Avatar'
6 |
7 | // ==============================|| AVATAR ||============================== //
8 |
9 | const Avatar = ({ color, outline, size, sx, ...others }) => {
10 | const theme = useTheme()
11 |
12 | const colorSX = color && !outline && { color: theme.palette.background.paper, bgcolor: `${color}.main` }
13 | const outlineSX = outline && {
14 | color: color ? `${color}.main` : `primary.main`,
15 | bgcolor: theme.palette.background.paper,
16 | border: '2px solid',
17 | borderColor: color ? `${color}.main` : `primary.main`
18 | }
19 | let sizeSX = {}
20 | switch (size) {
21 | case 'badge':
22 | sizeSX = {
23 | width: theme.spacing(3.5),
24 | height: theme.spacing(3.5)
25 | }
26 | break
27 | case 'xs':
28 | sizeSX = {
29 | width: theme.spacing(4.25),
30 | height: theme.spacing(4.25)
31 | }
32 | break
33 | case 'sm':
34 | sizeSX = {
35 | width: theme.spacing(5),
36 | height: theme.spacing(5)
37 | }
38 | break
39 | case 'lg':
40 | sizeSX = {
41 | width: theme.spacing(9),
42 | height: theme.spacing(9)
43 | }
44 | break
45 | case 'xl':
46 | sizeSX = {
47 | width: theme.spacing(10.25),
48 | height: theme.spacing(10.25)
49 | }
50 | break
51 | case 'md':
52 | sizeSX = {
53 | width: theme.spacing(7.5),
54 | height: theme.spacing(7.5)
55 | }
56 | break
57 | default:
58 | sizeSX = {}
59 | }
60 |
61 | return
62 | }
63 |
64 | Avatar.propTypes = {
65 | className: PropTypes.string,
66 | color: PropTypes.string,
67 | outline: PropTypes.bool,
68 | size: PropTypes.string,
69 | sx: PropTypes.object
70 | }
71 |
72 | export default Avatar
73 |
--------------------------------------------------------------------------------
/src/views/orgmanage/enhancedtabletoolbar.js:
--------------------------------------------------------------------------------
1 | import { alpha } from '@mui/material/styles'
2 | import Toolbar from '@mui/material/Toolbar'
3 | import Typography from '@mui/material/Typography'
4 | import PropTypes from 'prop-types'
5 | import Button from '@mui/material/Button'
6 | import { Remove, Add } from '@mui/icons-material'
7 |
8 | const EnhancedTableToolbar = (props) => {
9 | const { numSelected, rowsNumber, currentLimit, operation, title, isOrgFocused } = props
10 |
11 | return (
12 | <>
13 | 0 && { bgcolor: (theme) => alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity) })
19 | }}
20 | >
21 |
22 | {title}
23 |
24 |
25 | {!isOrgFocused && (
26 | <>
27 |
32 |
35 |
38 | >
39 | )}
40 |
41 | >
42 | )
43 | }
44 |
45 | EnhancedTableToolbar.propTypes = {
46 | numSelected: PropTypes.number,
47 | rowsNumber: PropTypes.number,
48 | rows_operation: PropTypes.func,
49 | title: PropTypes.string,
50 | isOrgFocused: PropTypes.bool
51 | }
52 |
53 | export default EnhancedTableToolbar
54 |
--------------------------------------------------------------------------------
/src/ui-component/dialog/AdditionalParamsDialog.js:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom'
2 | import { useState, useEffect } from 'react'
3 | import PropTypes from 'prop-types'
4 | import { Dialog, DialogContent } from '@mui/material'
5 | import PerfectScrollbar from 'react-perfect-scrollbar'
6 | import NodeInputHandler from 'views/canvas/NodeInputHandler'
7 |
8 | const AdditionalParamsDialog = ({ show, dialogProps, onCancel }) => {
9 | const portalElement = document.getElementById('portal')
10 |
11 | const [inputParams, setInputParams] = useState([])
12 | const [data, setData] = useState({})
13 |
14 | useEffect(() => {
15 | if (dialogProps.inputParams) setInputParams(dialogProps.inputParams)
16 | if (dialogProps.data) setData(dialogProps.data)
17 |
18 | return () => {
19 | setInputParams([])
20 | setData({})
21 | }
22 | }, [dialogProps])
23 |
24 | const component = show ? (
25 |
53 | ) : null
54 |
55 | return createPortal(component, portalElement)
56 | }
57 |
58 | AdditionalParamsDialog.propTypes = {
59 | show: PropTypes.bool,
60 | dialogProps: PropTypes.object,
61 | onCancel: PropTypes.func
62 | }
63 |
64 | export default AdditionalParamsDialog
65 |
--------------------------------------------------------------------------------
/src/themes/index.js:
--------------------------------------------------------------------------------
1 | import { createTheme } from '@mui/material/styles'
2 |
3 | // assets
4 | import colors from 'assets/scss/_themes-vars.module.scss'
5 |
6 | // project imports
7 | import componentStyleOverrides from './compStyleOverride'
8 | import themePalette from './palette'
9 | import themeTypography from './typography'
10 |
11 | /**
12 | * Represent theme style and structure as per Material-UI
13 | * @param {JsonObject} customization customization parameter object
14 | */
15 |
16 | export const theme = (customization) => {
17 | const color = colors
18 |
19 | const themeOption = customization.isDarkMode
20 | ? {
21 | colors: color,
22 | heading: color.paper,
23 | paper: color.darkPrimaryLight,
24 | backgroundDefault: color.darkPaper,
25 | background: color.darkPrimaryLight,
26 | darkTextPrimary: color.paper,
27 | darkTextSecondary: color.paper,
28 | textDark: color.paper,
29 | menuSelected: color.darkSecondaryDark,
30 | menuSelectedBack: color.darkSecondaryLight,
31 | divider: color.darkPaper,
32 | customization
33 | }
34 | : {
35 | colors: color,
36 | heading: color.grey900,
37 | paper: color.paper,
38 | backgroundDefault: color.paper,
39 | background: color.primaryLight,
40 | darkTextPrimary: color.grey700,
41 | darkTextSecondary: color.grey500,
42 | textDark: color.grey900,
43 | menuSelected: color.secondaryIconText,
44 | menuSelectedBack: color.secondaryIcon,
45 | divider: color.grey200,
46 | customization
47 | }
48 |
49 | const themeOptions = {
50 | direction: 'ltr',
51 | palette: themePalette(themeOption),
52 | mixins: {
53 | toolbar: {
54 | minHeight: '48px',
55 | padding: '16px',
56 | '@media (min-width: 600px)': {
57 | minHeight: '48px'
58 | }
59 | }
60 | },
61 | typography: themeTypography(themeOption)
62 | }
63 |
64 | const themes = createTheme(themeOptions)
65 | themes.components = componentStyleOverrides(themeOption)
66 |
67 | return themes
68 | }
69 |
70 | export default theme
71 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Sidebar/MenuList/NavGroup/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | // material-ui
4 | import { useTheme } from '@mui/material/styles'
5 | import { Divider, List, Typography } from '@mui/material'
6 |
7 | // project imports
8 | import NavItem from '../NavItem'
9 | import NavCollapse from '../NavCollapse'
10 |
11 | // ==============================|| SIDEBAR MENU LIST GROUP ||============================== //
12 |
13 | const NavGroup = ({ item }) => {
14 | const theme = useTheme()
15 |
16 | // menu list collapse & items
17 | const items = item.children
18 | ?.filter((item) => {
19 | if (Object.hasOwn(item, 'hide') && item.hide) {
20 | return false
21 | }
22 | return true
23 | })
24 | .map((menu) => {
25 | switch (menu.type) {
26 | case 'collapse':
27 | return
28 | case 'item':
29 | return
30 | default:
31 | return (
32 |
33 | Menu Items Error
34 |
35 | )
36 | }
37 | })
38 |
39 | return (
40 | <>
41 |
45 | {item.title}
46 | {item.caption && (
47 |
48 | {item.caption}
49 |
50 | )}
51 |
52 | )
53 | }
54 | >
55 | {items}
56 |
57 |
58 | {/* group divider */}
59 |
60 | >
61 | )
62 | }
63 |
64 | NavGroup.propTypes = {
65 | item: PropTypes.object
66 | }
67 |
68 | export default NavGroup
69 |
--------------------------------------------------------------------------------
/src/ui-component/cards/MainCard.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { forwardRef } from 'react'
3 |
4 | // material-ui
5 | import { useTheme } from '@mui/material/styles'
6 | import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material'
7 |
8 | // constant
9 | const headerSX = {
10 | '& .MuiCardHeader-action': { mr: 0 }
11 | }
12 |
13 | // ==============================|| CUSTOM MAIN CARD ||============================== //
14 |
15 | const MainCard = forwardRef(function MainCard(
16 | {
17 | border = true,
18 | boxShadow,
19 | children,
20 | content = true,
21 | contentClass = '',
22 | contentSX = {},
23 | darkTitle,
24 | secondary,
25 | shadow,
26 | sx = {},
27 | title,
28 | ...others
29 | },
30 | ref
31 | ) {
32 | const theme = useTheme()
33 |
34 | return (
35 |
47 | {/* card header and action */}
48 | {!darkTitle && title && }
49 | {darkTitle && title && {title}} action={secondary} />}
50 |
51 | {/* content & header divider */}
52 | {title && }
53 |
54 | {/* card content */}
55 | {content && (
56 |
57 | {children}
58 |
59 | )}
60 | {!content && children}
61 |
62 | )
63 | })
64 |
65 | MainCard.propTypes = {
66 | border: PropTypes.bool,
67 | boxShadow: PropTypes.bool,
68 | children: PropTypes.node,
69 | content: PropTypes.bool,
70 | contentClass: PropTypes.string,
71 | contentSX: PropTypes.object,
72 | darkTitle: PropTypes.bool,
73 | secondary: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object]),
74 | shadow: PropTypes.string,
75 | sx: PropTypes.object,
76 | title: PropTypes.oneOfType([PropTypes.node, PropTypes.string, PropTypes.object])
77 | }
78 |
79 | export default MainCard
80 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "llmscheduler-ui",
3 | "version": "1.2.12",
4 | "license": "SEE LICENSE IN LICENSE.md",
5 | "homepage": "https://www.opencsg.com",
6 | "dependencies": {
7 | "@ant-design/plots": "^1.0.9",
8 | "@emotion/cache": "^11.4.0",
9 | "@emotion/react": "^11.10.6",
10 | "@emotion/styled": "^11.10.6",
11 | "@mui/icons-material": "^5.0.3",
12 | "@mui/lab": "^5.0.0-alpha.152",
13 | "@mui/material": "^5.11.12",
14 | "@mui/x-data-grid": "^6.8.0",
15 | "@mui/x-tree-view": "^6.17.0",
16 | "@tabler/icons": "^1.39.1",
17 | "axios": "^1.6.0",
18 | "casdoor-js-sdk": "^0.11.0",
19 | "casdoor-react-sdk": "^1.2.0",
20 | "history": "^5.0.0",
21 | "html-react-parser": "^3.0.4",
22 | "js-cookie": "^3.0.5",
23 | "jwt-decode": "^3.1.2",
24 | "lodash": "^4.17.21",
25 | "moment": "^2.29.3",
26 | "notistack": "^2.0.4",
27 | "prismjs": "^1.28.0",
28 | "prop-types": "^15.7.2",
29 | "react": "^18.2.0",
30 | "react-code-blocks": "^0.0.9-0",
31 | "react-device-detect": "^1.17.0",
32 | "react-dom": "^18.2.0",
33 | "react-json-view": "^1.21.3",
34 | "react-markdown": "^9.0.0",
35 | "react-perfect-scrollbar": "^1.5.8",
36 | "react-redux": "^8.0.5",
37 | "react-router": "~6.3.0",
38 | "react-router-dom": "~6.3.0",
39 | "react-simple-code-editor": "^0.11.2",
40 | "react-syntax-highlighter": "^15.5.0",
41 | "redux": "^4.0.5",
42 | "socket.io-client": "^4.6.1",
43 | "yup": "^0.32.9"
44 | },
45 | "peerDependencies": {
46 | "react": ">=16.8.4",
47 | "react-dom": ">=16.8.4"
48 | },
49 | "scripts": {
50 | "start": "craco start",
51 | "dev": "craco start",
52 | "build": "craco build",
53 | "test": "craco test",
54 | "eject": "craco eject"
55 | },
56 | "babel": {
57 | "presets": [
58 | "@babel/preset-react"
59 | ]
60 | },
61 | "browserslist": {
62 | "production": [
63 | ">0.2%",
64 | "not dead",
65 | "not op_mini all"
66 | ],
67 | "development": [
68 | "last 1 chrome version",
69 | "last 1 firefox version",
70 | "last 1 safari version"
71 | ]
72 | },
73 | "devDependencies": {
74 | "@babel/eslint-parser": "^7.15.8",
75 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
76 | "@craco/craco": "^7.1.0",
77 | "@testing-library/jest-dom": "^5.11.10",
78 | "@testing-library/react": "^14.0.0",
79 | "@testing-library/user-event": "^12.8.3",
80 | "pretty-quick": "^3.1.3",
81 | "react-scripts": "^5.0.1",
82 | "sass": "^1.42.1",
83 | "typescript": "^4.8.4"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/ui-component/json/JsonEditor.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { FormControl } from '@mui/material'
4 | import ReactJson from 'react-json-view'
5 |
6 | export const JsonEditorInput = ({ value, onChange, disabled = false, isDarkMode = false }) => {
7 | const [myValue, setMyValue] = useState(value ? JSON.parse(value) : {})
8 |
9 | const onClipboardCopy = (e) => {
10 | const src = e.src
11 | if (Array.isArray(src) || typeof src === 'object') {
12 | navigator.clipboard.writeText(JSON.stringify(src, null, ' '))
13 | } else {
14 | navigator.clipboard.writeText(src)
15 | }
16 | }
17 |
18 | return (
19 | <>
20 |
21 | {disabled && (
22 | onClipboardCopy(e)}
28 | quotesOnKeys={false}
29 | displayDataTypes={false}
30 | />
31 | )}
32 | {!disabled && (
33 | onClipboardCopy(e)}
41 | onEdit={(edit) => {
42 | setMyValue(edit.updated_src)
43 | onChange(JSON.stringify(edit.updated_src))
44 | }}
45 | onAdd={() => {
46 | //console.log(add)
47 | }}
48 | onDelete={(deleteobj) => {
49 | setMyValue(deleteobj.updated_src)
50 | onChange(JSON.stringify(deleteobj.updated_src))
51 | }}
52 | />
53 | )}
54 |
55 | >
56 | )
57 | }
58 |
59 | JsonEditorInput.propTypes = {
60 | value: PropTypes.string,
61 | onChange: PropTypes.func,
62 | disabled: PropTypes.bool,
63 | isDarkMode: PropTypes.bool
64 | }
65 |
--------------------------------------------------------------------------------
/src/ui-component/dialog/LoginDialog.js:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom'
2 | import { useState } from 'react'
3 | import PropTypes from 'prop-types'
4 |
5 | import { Dialog, DialogActions, DialogContent, Typography, DialogTitle } from '@mui/material'
6 | import { StyledButton } from 'ui-component/button/StyledButton'
7 | import { Input } from 'ui-component/input/Input'
8 |
9 | const LoginDialog = ({ show, dialogProps, onConfirm }) => {
10 | const portalElement = document.getElementById('portal')
11 | const usernameInput = {
12 | label: 'Username',
13 | name: 'username',
14 | type: 'string',
15 | placeholder: 'john doe'
16 | }
17 | const passwordInput = {
18 | label: 'Password',
19 | name: 'password',
20 | type: 'password'
21 | }
22 | const [usernameVal, setUsernameVal] = useState('')
23 | const [passwordVal, setPasswordVal] = useState('')
24 |
25 | const component = show ? (
26 |
59 | ) : null
60 |
61 | return createPortal(component, portalElement)
62 | }
63 |
64 | LoginDialog.propTypes = {
65 | show: PropTypes.bool,
66 | dialogProps: PropTypes.object,
67 | onConfirm: PropTypes.func
68 | }
69 |
70 | export default LoginDialog
71 |
--------------------------------------------------------------------------------
/src/ui-component/dialog/AboutDialog.js:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom'
2 | import { useState, useEffect } from 'react'
3 | import PropTypes from 'prop-types'
4 | import { Dialog, DialogContent, DialogTitle, TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Paper } from '@mui/material'
5 | import moment from 'moment'
6 |
7 | const AboutDialog = ({ show, onCancel }) => {
8 | const portalElement = document.getElementById('portal')
9 |
10 | const [data, setData] = useState({})
11 |
12 | const component = show ? (
13 |
52 | ) : null
53 |
54 | return createPortal(component, portalElement)
55 | }
56 |
57 | AboutDialog.propTypes = {
58 | show: PropTypes.bool,
59 | onCancel: PropTypes.func
60 | }
61 |
62 | export default AboutDialog
63 |
--------------------------------------------------------------------------------
/src/ui-component/input/Input.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { FormControl, OutlinedInput } from '@mui/material'
4 | import EditPromptValuesDialog from 'ui-component/dialog/EditPromptValuesDialog'
5 |
6 | export const Input = ({ inputParam, value, onChange, disabled = false, showDialog, dialogProps, onDialogCancel, onDialogConfirm }) => {
7 | const [myValue, setMyValue] = useState(value ?? '')
8 |
9 | const getInputType = (type) => {
10 | switch (type) {
11 | case 'string':
12 | return 'text'
13 | case 'password':
14 | return 'password'
15 | case 'number':
16 | return 'number'
17 | default:
18 | return 'text'
19 | }
20 | }
21 |
22 | return (
23 | <>
24 |
25 | {
36 | setMyValue(e.target.value)
37 | onChange(e.target.value)
38 | }}
39 | inputProps={{
40 | step: 0.1,
41 | style: {
42 | height: inputParam.rows ? '90px' : 'inherit'
43 | }
44 | }}
45 | />
46 |
47 | {showDialog && (
48 | {
53 | setMyValue(newValue)
54 | onDialogConfirm(newValue, inputParamName)
55 | }}
56 | >
57 | )}
58 | >
59 | )
60 | }
61 |
62 | Input.propTypes = {
63 | inputParam: PropTypes.object,
64 | value: PropTypes.string,
65 | onChange: PropTypes.func,
66 | disabled: PropTypes.bool,
67 | showDialog: PropTypes.bool,
68 | dialogProps: PropTypes.object,
69 | onDialogCancel: PropTypes.func,
70 | onDialogConfirm: PropTypes.func
71 | }
72 |
--------------------------------------------------------------------------------
/src/ui-component/dropdown/Dropdown.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import { useSelector } from 'react-redux'
3 |
4 | import { Popper, FormControl, TextField, Box, Typography } from '@mui/material'
5 | import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'
6 | import { styled } from '@mui/material/styles'
7 | import PropTypes from 'prop-types'
8 |
9 | const StyledPopper = styled(Popper)({
10 | boxShadow: '0px 8px 10px -5px rgb(0 0 0 / 20%), 0px 16px 24px 2px rgb(0 0 0 / 14%), 0px 6px 30px 5px rgb(0 0 0 / 12%)',
11 | borderRadius: '10px',
12 | [`& .${autocompleteClasses.listbox}`]: {
13 | boxSizing: 'border-box',
14 | '& ul': {
15 | padding: 10,
16 | margin: 10
17 | }
18 | }
19 | })
20 |
21 | export const Dropdown = ({ name, value, options, onSelect, disabled = false, disableClearable = false }) => {
22 | const customization = useSelector((state) => state.customization)
23 | const findMatchingOptions = (options = [], value) => options.find((option) => option.name === value)
24 | const getDefaultOptionValue = () => ''
25 | let [internalValue, setInternalValue] = useState(value ?? 'choose an option')
26 |
27 | return (
28 |
29 | {
37 | const value = selection ? selection.name : ''
38 | setInternalValue(value)
39 | onSelect(value)
40 | }}
41 | PopperComponent={StyledPopper}
42 | renderInput={(params) => }
43 | renderOption={(props, option) => (
44 |
45 |
46 | {option.label}
47 | {option.description && (
48 | {option.description}
49 | )}
50 |
51 |
52 | )}
53 | />
54 |
55 | )
56 | }
57 |
58 | Dropdown.propTypes = {
59 | name: PropTypes.string,
60 | value: PropTypes.string,
61 | options: PropTypes.array,
62 | onSelect: PropTypes.func,
63 | disabled: PropTypes.bool,
64 | disableClearable: PropTypes.bool
65 | }
66 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Sidebar/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 |
3 | // material-ui
4 | import { useTheme } from '@mui/material/styles'
5 | import { Box, Drawer, useMediaQuery } from '@mui/material'
6 |
7 | // third-party
8 | import PerfectScrollbar from 'react-perfect-scrollbar'
9 | import { BrowserView, MobileView } from 'react-device-detect'
10 |
11 | // project imports
12 | import MenuList from './MenuList'
13 | // import LogoSection from '../LogoSection'
14 | import { drawerWidth } from 'store/constant'
15 |
16 | // ==============================|| SIDEBAR DRAWER ||============================== //
17 |
18 | const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
19 | const theme = useTheme()
20 | const matchUpMd = useMediaQuery(theme.breakpoints.up('md'))
21 |
22 | const drawer = (
23 | <>
24 |
25 |
26 | {/* */}
27 | LLM scheduler
28 |
29 |
30 |
31 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | >
48 | )
49 |
50 | const container = window !== undefined ? () => window.document.body : undefined
51 |
52 | return (
53 |
54 |
74 | {drawer}
75 |
76 |
77 | )
78 | }
79 |
80 | Sidebar.propTypes = {
81 | drawerOpen: PropTypes.bool,
82 | drawerToggle: PropTypes.func,
83 | window: PropTypes.object
84 | }
85 |
86 | export default Sidebar
87 |
--------------------------------------------------------------------------------
/src/assets/scss/style.scss:
--------------------------------------------------------------------------------
1 | // color variants
2 | @import 'themes-vars.module.scss';
3 |
4 | // third-party
5 | @import '~react-perfect-scrollbar/dist/css/styles.css';
6 |
7 | // ==============================|| LIGHT BOX ||============================== //
8 | .fullscreen .react-images__blanket {
9 | z-index: 1200;
10 | }
11 |
12 | // ==============================|| PERFECT SCROLLBAR ||============================== //
13 |
14 | .scrollbar-container {
15 | .ps__rail-y {
16 | &:hover > .ps__thumb-y,
17 | &:focus > .ps__thumb-y,
18 | &.ps--clicking .ps__thumb-y {
19 | background-color: $grey500;
20 | width: 5px;
21 | }
22 | }
23 | .ps__thumb-y {
24 | background-color: $grey500;
25 | border-radius: 6px;
26 | width: 5px;
27 | right: 0;
28 | }
29 | }
30 |
31 | .scrollbar-container.ps,
32 | .scrollbar-container > .ps {
33 | &.ps--active-y > .ps__rail-y {
34 | width: 5px;
35 | background-color: transparent !important;
36 | z-index: 999;
37 | &:hover,
38 | &.ps--clicking {
39 | width: 5px;
40 | background-color: transparent;
41 | }
42 | }
43 | &.ps--scrolling-y > .ps__rail-y,
44 | &.ps--scrolling-x > .ps__rail-x {
45 | opacity: 0.4;
46 | background-color: transparent;
47 | }
48 | }
49 |
50 | // ==============================|| ANIMATION KEYFRAMES ||============================== //
51 |
52 | @keyframes wings {
53 | 50% {
54 | transform: translateY(-40px);
55 | }
56 | 100% {
57 | transform: translateY(0px);
58 | }
59 | }
60 |
61 | @keyframes blink {
62 | 50% {
63 | opacity: 0;
64 | }
65 | 100% {
66 | opacity: 1;
67 | }
68 | }
69 |
70 | @keyframes bounce {
71 | 0%,
72 | 20%,
73 | 53%,
74 | to {
75 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
76 | transform: translateZ(0);
77 | }
78 | 40%,
79 | 43% {
80 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
81 | transform: translate3d(0, -5px, 0);
82 | }
83 | 70% {
84 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
85 | transform: translate3d(0, -7px, 0);
86 | }
87 | 80% {
88 | transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
89 | transform: translateZ(0);
90 | }
91 | 90% {
92 | transform: translate3d(0, -2px, 0);
93 | }
94 | }
95 |
96 | @keyframes slideY {
97 | 0%,
98 | 50%,
99 | 100% {
100 | transform: translateY(0px);
101 | }
102 | 25% {
103 | transform: translateY(-10px);
104 | }
105 | 75% {
106 | transform: translateY(10px);
107 | }
108 | }
109 |
110 | @keyframes slideX {
111 | 0%,
112 | 50%,
113 | 100% {
114 | transform: translateX(0px);
115 | }
116 | 25% {
117 | transform: translateX(-10px);
118 | }
119 | 75% {
120 | transform: translateX(10px);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/ui-component/file/File.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { useTheme } from '@mui/material/styles'
4 | import { FormControl, Button } from '@mui/material'
5 | import { IconUpload } from '@tabler/icons'
6 | import { getFileName } from 'utils/genericHelper'
7 |
8 | export const File = ({ value, fileType, onChange, disabled = false }) => {
9 | const theme = useTheme()
10 |
11 | const [myValue, setMyValue] = useState(value ?? '')
12 |
13 | const handleFileUpload = async (e) => {
14 | if (!e.target.files) return
15 |
16 | if (e.target.files.length === 1) {
17 | const file = e.target.files[0]
18 | const { name } = file
19 |
20 | const reader = new FileReader()
21 | reader.onload = (evt) => {
22 | if (!evt?.target?.result) {
23 | return
24 | }
25 | const { result } = evt.target
26 |
27 | const value = result + `,filename:${name}`
28 |
29 | setMyValue(value)
30 | onChange(value)
31 | }
32 | reader.readAsDataURL(file)
33 | } else if (e.target.files.length > 0) {
34 | let files = Array.from(e.target.files).map((file) => {
35 | const reader = new FileReader()
36 | const { name } = file
37 |
38 | return new Promise((resolve) => {
39 | reader.onload = (evt) => {
40 | if (!evt?.target?.result) {
41 | return
42 | }
43 | const { result } = evt.target
44 | const value = result + `,filename:${name}`
45 | resolve(value)
46 | }
47 | reader.readAsDataURL(file)
48 | })
49 | })
50 |
51 | const res = await Promise.all(files)
52 | setMyValue(JSON.stringify(res))
53 | onChange(JSON.stringify(res))
54 | }
55 | }
56 |
57 | return (
58 |
59 |
66 | {myValue ? getFileName(myValue) : 'Choose a file to upload'}
67 |
68 | }
74 | sx={{ marginRight: '1rem' }}
75 | >
76 | {'Upload File'}
77 | handleFileUpload(e)} />
78 |
79 |
80 | )
81 | }
82 |
83 | File.propTypes = {
84 | value: PropTypes.string,
85 | fileType: PropTypes.string,
86 | onChange: PropTypes.func,
87 | disabled: PropTypes.bool
88 | }
89 |
--------------------------------------------------------------------------------
/src/ui-component/button/AnimateButton.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { forwardRef } from 'react'
3 | // third-party
4 | import { motion, useCycle } from 'framer-motion'
5 |
6 | // ==============================|| ANIMATION BUTTON ||============================== //
7 |
8 | const AnimateButton = forwardRef(function AnimateButton({ children, type, direction, offset, scale }, ref) {
9 | let offset1
10 | let offset2
11 | switch (direction) {
12 | case 'up':
13 | case 'left':
14 | offset1 = offset
15 | offset2 = 0
16 | break
17 | case 'right':
18 | case 'down':
19 | default:
20 | offset1 = 0
21 | offset2 = offset
22 | break
23 | }
24 |
25 | const [x, cycleX] = useCycle(offset1, offset2)
26 | const [y, cycleY] = useCycle(offset1, offset2)
27 |
28 | switch (type) {
29 | case 'rotate':
30 | return (
31 |
41 | {children}
42 |
43 | )
44 | case 'slide':
45 | if (direction === 'up' || direction === 'down') {
46 | return (
47 | cycleY()}
51 | onHoverStart={() => cycleY()}
52 | >
53 | {children}
54 |
55 | )
56 | }
57 | return (
58 | cycleX()} onHoverStart={() => cycleX()}>
59 | {children}
60 |
61 | )
62 |
63 | case 'scale':
64 | default:
65 | if (typeof scale === 'number') {
66 | scale = {
67 | hover: scale,
68 | tap: scale
69 | }
70 | }
71 | return (
72 |
73 | {children}
74 |
75 | )
76 | }
77 | })
78 |
79 | AnimateButton.propTypes = {
80 | children: PropTypes.node,
81 | offset: PropTypes.number,
82 | type: PropTypes.oneOf(['slide', 'scale', 'rotate']),
83 | direction: PropTypes.oneOf(['up', 'down', 'left', 'right']),
84 | scale: PropTypes.oneOfType([PropTypes.number, PropTypes.object])
85 | }
86 |
87 | AnimateButton.defaultProps = {
88 | type: 'scale',
89 | offset: 10,
90 | direction: 'right',
91 | scale: {
92 | hover: 1,
93 | tap: 0.9
94 | }
95 | }
96 |
97 | export default AnimateButton
98 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/enhanceddeploytablehead.js:
--------------------------------------------------------------------------------
1 | import TableHead from '@mui/material/TableHead'
2 | import TableRow from '@mui/material/TableRow'
3 | import TableCell from '@mui/material/TableCell'
4 | import Checkbox from '@mui/material/Checkbox'
5 | import TableSortLabel from '@mui/material/TableSortLabel'
6 | import Box from '@mui/material/Box'
7 | import { visuallyHidden } from '@mui/utils'
8 |
9 | export const DeployHeadCells = [
10 | {
11 | id: 'name',
12 | align: 'left',
13 | disablePadding: true,
14 | label: '名称',
15 | sortable: true
16 | },
17 | {
18 | id: 'namespace',
19 | align: 'left',
20 | disablePadding: false,
21 | label: '命名空间',
22 | sortable: true
23 | },
24 | // {
25 | // id: 'images',
26 | // align: 'left',
27 | // disablePadding: false,
28 | // label: '镜像',
29 | // sortable: true
30 | // },
31 | {
32 | id: 'lable',
33 | align: 'left',
34 | disablePadding: false,
35 | label: '标签',
36 | sortable: false
37 | },
38 | {
39 | id: 'status',
40 | align: 'left',
41 | disablePadding: false,
42 | label: '状态',
43 | sortable: false
44 | }
45 | ]
46 |
47 | const EnhancedDeployTableHead = (props) => {
48 | const { order, orderBy, onRequestSort } = props
49 | const createSortHandler = (property) => (event) => {
50 | onRequestSort(event, property)
51 | }
52 |
53 | return (
54 |
55 |
56 |
57 |
58 | {DeployHeadCells.map((headCell) => (
59 |
65 | {headCell.sortable ? (
66 |
71 | {headCell.label}
72 | {orderBy === headCell.id ? (
73 |
74 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
75 |
76 | ) : null}
77 |
78 | ) : (
79 | {headCell.label}
80 | )}
81 |
82 | ))}
83 |
84 |
85 | )
86 | }
87 |
88 | export default EnhancedDeployTableHead
89 |
--------------------------------------------------------------------------------
/src/views/hpcjobs/JobDetails.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { IconButton, Box, Tab, Dialog, DialogTitle, Slide } from '@mui/material'
3 | import { TabContext, TabList, TabPanel } from '@mui/lab'
4 | import CloseIcon from '@mui/icons-material/Close'
5 | import JobTab from './JobTab'
6 | import JobDataTab from './JobDataTab'
7 | // Hooks
8 | import useApi from 'hooks/useApi'
9 | import jobAPI from 'api/jobs'
10 |
11 | const Transition = React.forwardRef(function Transition(props, ref) {
12 | return
13 | })
14 |
15 | const JobDetails = (props) => {
16 | const getSpecificJob = useApi(jobAPI.getSpecificJob)
17 | const { showDetails, onCancel, jobData } = props
18 | const [summaryTable, setSummaryTable] = useState('summary')
19 | const [jobDetails, setjobDetails] = useState({})
20 | const handleTabChange = (event, newValue) => {
21 | setSummaryTable(newValue)
22 | }
23 |
24 | useEffect(() => {
25 | if (showDetails) {
26 | setSummaryTable('summary')
27 | getSpecificJob.request(jobData.job_id)
28 | }
29 | }, [showDetails])
30 |
31 | useEffect(() => {
32 | if (getSpecificJob.data) {
33 | setjobDetails(getSpecificJob.data.jobs[0])
34 | }
35 | }, [getSpecificJob.data])
36 |
37 | return (
38 |
86 | )
87 | }
88 |
89 | export default JobDetails
90 |
--------------------------------------------------------------------------------
/src/ui-component/extended/Transitions.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { forwardRef } from 'react'
3 |
4 | // material-ui
5 | import { Collapse, Fade, Box, Grow, Slide, Zoom } from '@mui/material'
6 |
7 | // ==============================|| TRANSITIONS ||============================== //
8 |
9 | const Transitions = forwardRef(function Transitions({ children, position, type, direction, ...others }, ref) {
10 | let positionSX = {
11 | transformOrigin: '0 0 0'
12 | }
13 |
14 | switch (position) {
15 | case 'top-right':
16 | positionSX = {
17 | transformOrigin: 'top right'
18 | }
19 | break
20 | case 'top':
21 | positionSX = {
22 | transformOrigin: 'top'
23 | }
24 | break
25 | case 'bottom-left':
26 | positionSX = {
27 | transformOrigin: 'bottom left'
28 | }
29 | break
30 | case 'bottom-right':
31 | positionSX = {
32 | transformOrigin: 'bottom right'
33 | }
34 | break
35 | case 'bottom':
36 | positionSX = {
37 | transformOrigin: 'bottom'
38 | }
39 | break
40 | case 'top-left':
41 | default:
42 | positionSX = {
43 | transformOrigin: '0 0 0'
44 | }
45 | break
46 | }
47 |
48 | return (
49 |
50 | {type === 'grow' && (
51 |
52 | {children}
53 |
54 | )}
55 | {type === 'collapse' && (
56 |
57 | {children}
58 |
59 | )}
60 | {type === 'fade' && (
61 |
69 | {children}
70 |
71 | )}
72 | {type === 'slide' && (
73 |
82 | {children}
83 |
84 | )}
85 | {type === 'zoom' && (
86 |
87 | {children}
88 |
89 | )}
90 |
91 | )
92 | })
93 |
94 | Transitions.propTypes = {
95 | children: PropTypes.node,
96 | type: PropTypes.oneOf(['grow', 'fade', 'collapse', 'slide', 'zoom']),
97 | position: PropTypes.oneOf(['top-left', 'top-right', 'top', 'bottom-left', 'bottom-right', 'bottom']),
98 | direction: PropTypes.oneOf(['up', 'down', 'left', 'right'])
99 | }
100 |
101 | Transitions.defaultProps = {
102 | type: 'grow',
103 | position: 'top-left',
104 | direction: 'up'
105 | }
106 |
107 | export default Transitions
108 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | LLM scheduler
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
39 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/views/orgmanage/enhancedtablehead.js:
--------------------------------------------------------------------------------
1 | import TableHead from '@mui/material/TableHead'
2 | import TableRow from '@mui/material/TableRow'
3 | import TableCell from '@mui/material/TableCell'
4 | import Checkbox from '@mui/material/Checkbox'
5 | import TableSortLabel from '@mui/material/TableSortLabel'
6 | import Box from '@mui/material/Box'
7 | import { visuallyHidden } from '@mui/utils'
8 | import { useEffect, useState, useMemo } from 'react'
9 |
10 | export const HeadCells = [
11 | {
12 | id: 'name',
13 | align: 'left',
14 | disablePadding: true,
15 | label: 'User Name',
16 | sortable: true
17 | },
18 |
19 | {
20 | id: 'accounts',
21 | align: 'left',
22 | disablePadding: false,
23 | label: 'User accounts',
24 | sortable: true
25 | },
26 | {
27 | id: 'partitions',
28 | align: 'left',
29 | disablePadding: false,
30 | label: 'Available partitions',
31 | sortable: false
32 | },
33 | {
34 | id: 'cluster',
35 | align: 'left',
36 | disablePadding: false,
37 | label: 'Cluster',
38 | sortable: true
39 | },
40 | {
41 | id: 'operation',
42 | align: 'right',
43 | disablePadding: false,
44 | label: 'Operation',
45 | sortable: false
46 | }
47 | ]
48 |
49 | const EnhancedTableHead = (props) => {
50 | const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props
51 | const createSortHandler = (property) => (event) => {
52 | onRequestSort(event, property)
53 | }
54 |
55 | return (
56 |
57 |
58 |
59 | 0 && numSelected < rowCount}
62 | onChange={onSelectAllClick}
63 | inputProps={{ 'aria-label': 'select all desserts' }}
64 | />
65 |
66 | {HeadCells.map((headCell) => (
67 |
73 | {headCell.sortable ? (
74 |
79 | {headCell.label}
80 | {orderBy === headCell.id ? (
81 |
82 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
83 |
84 | ) : null}
85 |
86 | ) : (
87 | {headCell.label}
88 | )}
89 |
90 | ))}
91 |
92 |
93 | )
94 | }
95 |
96 | export default EnhancedTableHead
97 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/enhancedpodtablehead.js:
--------------------------------------------------------------------------------
1 | import TableHead from '@mui/material/TableHead'
2 | import TableRow from '@mui/material/TableRow'
3 | import TableCell from '@mui/material/TableCell'
4 | import Checkbox from '@mui/material/Checkbox'
5 | import TableSortLabel from '@mui/material/TableSortLabel'
6 | import Box from '@mui/material/Box'
7 | import { visuallyHidden } from '@mui/utils'
8 |
9 | export const PodHeadCells = [
10 | {
11 | id: 'podname',
12 | align: 'left',
13 | disablePadding: true,
14 | label: '名称',
15 | sortable: true
16 | },
17 | {
18 | id: 'namespace',
19 | align: 'left',
20 | disablePadding: false,
21 | label: '命名空间',
22 | sortable: true
23 | },
24 | {
25 | id: 'state',
26 | align: 'center',
27 | disablePadding: false,
28 | label: '状态',
29 | sortable: false
30 | },
31 | {
32 | id: 'pod_ip',
33 | align: 'left',
34 | disablePadding: false,
35 | label: 'Pod IP',
36 | sortable: true
37 | },
38 | {
39 | id: 'lable',
40 | align: 'left',
41 | disablePadding: false,
42 | label: 'Lable',
43 | sortable: false
44 | },
45 | {
46 | id: 'node',
47 | align: 'left',
48 | disablePadding: false,
49 | label: 'Node',
50 | sortable: false
51 | },
52 | {
53 | id: 'node_ip',
54 | align: 'left',
55 | disablePadding: false,
56 | label: 'Node IP',
57 | sortable: false
58 | },
59 | {
60 | id: 'start_time',
61 | align: 'center',
62 | disablePadding: false,
63 | label: 'Creation Time',
64 | sortable: true
65 | }
66 | ]
67 |
68 | const EnhancedPodTableHead = (props) => {
69 | const { order, orderBy, onRequestSort } = props
70 | const createSortHandler = (property) => (event) => {
71 | onRequestSort(event, property)
72 | }
73 |
74 | return (
75 |
76 |
77 |
78 |
79 | {PodHeadCells.map((headCell) => (
80 |
86 | {headCell.sortable ? (
87 |
92 | {headCell.label}
93 | {orderBy === headCell.id ? (
94 |
95 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
96 |
97 | ) : null}
98 |
99 | ) : (
100 | {headCell.label}
101 | )}
102 |
103 | ))}
104 |
105 |
106 | )
107 | }
108 |
109 | export default EnhancedPodTableHead
110 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/enhancedservicetablehead.js:
--------------------------------------------------------------------------------
1 | import TableHead from '@mui/material/TableHead'
2 | import TableRow from '@mui/material/TableRow'
3 | import TableCell from '@mui/material/TableCell'
4 | import Checkbox from '@mui/material/Checkbox'
5 | import TableSortLabel from '@mui/material/TableSortLabel'
6 | import Box from '@mui/material/Box'
7 | import { visuallyHidden } from '@mui/utils'
8 |
9 | export const ServiceHeadCells = [
10 | {
11 | id: 'name',
12 | align: 'left',
13 | disablePadding: true,
14 | label: '名称',
15 | sortable: true
16 | },
17 | {
18 | id: 'namespace',
19 | align: 'left',
20 | disablePadding: false,
21 | label: '命名空间',
22 | sortable: true
23 | },
24 | {
25 | id: 'type',
26 | align: 'left',
27 | disablePadding: false,
28 | label: '类型',
29 | sortable: true
30 | },
31 | {
32 | id: 'cluster_ip',
33 | align: 'left',
34 | disablePadding: false,
35 | label: 'Cluster IP',
36 | sortable: true
37 | },
38 | {
39 | id: 'external_ip',
40 | align: 'left',
41 | disablePadding: false,
42 | label: 'External IP',
43 | sortable: true
44 | },
45 | {
46 | id: 'lable',
47 | align: 'left',
48 | disablePadding: false,
49 | label: '标签',
50 | sortable: false
51 | },
52 | {
53 | id: 'ports',
54 | align: 'left',
55 | disablePadding: false,
56 | label: '端口',
57 | sortable: false
58 | },
59 | {
60 | id: 'created_at',
61 | align: 'left',
62 | disablePadding: false,
63 | label: '创建日期',
64 | sortable: false
65 | }
66 | ]
67 |
68 | const EnhancedServiceTableHead = (props) => {
69 | const { order, orderBy, onRequestSort } = props
70 | const createSortHandler = (property) => (event) => {
71 | onRequestSort(event, property)
72 | }
73 |
74 | return (
75 |
76 |
77 |
78 |
79 | {ServiceHeadCells.map((headCell) => (
80 |
86 | {headCell.sortable ? (
87 |
92 | {headCell.label}
93 | {orderBy === headCell.id ? (
94 |
95 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
96 |
97 | ) : null}
98 |
99 | ) : (
100 | {headCell.label}
101 | )}
102 |
103 | ))}
104 |
105 |
106 | )
107 | }
108 |
109 | export default EnhancedServiceTableHead
110 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/deploytablebody.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | // material-ui
3 | import { TableBody, TableCell, TableRow, Checkbox } from '@mui/material'
4 | import { DeployHeadCells } from './enhanceddeploytablehead'
5 |
6 | const DeployTableBody = (props) => {
7 | const { visibleRows, emptyRows } = props
8 | // console.log(visibleRows)
9 | const [selected, setSelected] = useState([])
10 |
11 | const isRowSelected = (name) => selected.findIndex((selData) => selData.name === name) !== -1
12 |
13 | const handlePodRowSelectClick = (event, name) => {
14 | // const selectedIndex = selected.indexOf(name)
15 | const selectedIndex = selected.findIndex((selData) => selData.name === name)
16 | let newSelected = []
17 |
18 | if (selectedIndex === -1) {
19 | newSelected = newSelected.concat(selected, { name: name })
20 | } else if (selectedIndex === 0) {
21 | newSelected = newSelected.concat(selected.slice(1))
22 | } else if (selectedIndex === selected.length - 1) {
23 | newSelected = newSelected.concat(selected.slice(0, -1))
24 | } else if (selectedIndex > 0) {
25 | newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1))
26 | }
27 | setSelected(newSelected)
28 | }
29 | return (
30 |
31 | {visibleRows.map((row, index) => {
32 | const isItemSelected = isRowSelected(row.name)
33 | const labelId = `enhanced-table-checkbox-${index}`
34 | return (
35 | handlePodRowSelectClick(event, row.id, row.name)}
38 | role='checkbox'
39 | aria-checked={isItemSelected}
40 | tabIndex={-1}
41 | key={row.name}
42 | selected={isItemSelected}
43 | sx={{ cursor: 'pointer' }}
44 | >
45 |
46 |
47 |
48 | {row.name}
49 |
50 |
51 | {row.namespace ? (
52 |
53 | {row.namespace}
54 |
55 | ) : (
56 | '-'
57 | )}
58 |
59 | {/* {row.images ? JSON.stringify(row.images) : '-'} */}
60 | {row.labels ? Object.keys(row.labels).map(function (key) {
61 | return "" + key + "=" + row.labels[key];
62 | }).join(" ") : '-'}
63 | {row.status ? Object.keys(row.status).map(function (key) {
64 | return "" + key + "=" + row.status[key];
65 | }).join(" ") : '-'}
66 |
67 |
68 | )
69 | })}
70 | {emptyRows > 0 ? (
71 |
72 |
73 |
74 | ) : null}
75 |
76 | )
77 | }
78 |
79 | export default DeployTableBody
80 |
--------------------------------------------------------------------------------
/src/ui-component/editor/prism-light.css:
--------------------------------------------------------------------------------
1 | code[class*='language-'],
2 | pre[class*='language-'] {
3 | text-align: left;
4 | white-space: pre;
5 | word-spacing: normal;
6 | word-break: normal;
7 | word-wrap: normal;
8 | color: #90a4ae;
9 | background: #fafafa;
10 | font-family: Roboto Mono, monospace;
11 | font-size: 1em;
12 | line-height: 1.5em;
13 |
14 | -moz-tab-size: 4;
15 | -o-tab-size: 4;
16 | tab-size: 4;
17 |
18 | -webkit-hyphens: none;
19 | -moz-hyphens: none;
20 | -ms-hyphens: none;
21 | hyphens: none;
22 | }
23 |
24 | code[class*='language-']::-moz-selection,
25 | pre[class*='language-']::-moz-selection,
26 | code[class*='language-'] ::-moz-selection,
27 | pre[class*='language-'] ::-moz-selection {
28 | background: #cceae7;
29 | color: #263238;
30 | }
31 |
32 | code[class*='language-']::selection,
33 | pre[class*='language-']::selection,
34 | code[class*='language-'] ::selection,
35 | pre[class*='language-'] ::selection {
36 | background: #cceae7;
37 | color: #263238;
38 | }
39 |
40 | :not(pre) > code[class*='language-'] {
41 | white-space: normal;
42 | border-radius: 0.2em;
43 | padding: 0.1em;
44 | }
45 |
46 | pre[class*='language-'] {
47 | overflow: auto;
48 | position: relative;
49 | margin: 0.5em 0;
50 | padding: 1.25em 1em;
51 | }
52 |
53 | .language-css > code,
54 | .language-sass > code,
55 | .language-scss > code {
56 | color: #f76d47;
57 | }
58 |
59 | [class*='language-'] .namespace {
60 | opacity: 0.7;
61 | }
62 |
63 | .token.atrule {
64 | color: #7c4dff;
65 | }
66 |
67 | .token.attr-name {
68 | color: #39adb5;
69 | }
70 |
71 | .token.attr-value {
72 | color: #f6a434;
73 | }
74 |
75 | .token.attribute {
76 | color: #f6a434;
77 | }
78 |
79 | .token.boolean {
80 | color: #7c4dff;
81 | }
82 |
83 | .token.builtin {
84 | color: #39adb5;
85 | }
86 |
87 | .token.cdata {
88 | color: #39adb5;
89 | }
90 |
91 | .token.char {
92 | color: #39adb5;
93 | }
94 |
95 | .token.class {
96 | color: #39adb5;
97 | }
98 |
99 | .token.class-name {
100 | color: #6182b8;
101 | }
102 |
103 | .token.comment {
104 | color: #aabfc9;
105 | }
106 |
107 | .token.constant {
108 | color: #7c4dff;
109 | }
110 |
111 | .token.deleted {
112 | color: #e53935;
113 | }
114 |
115 | .token.doctype {
116 | color: #aabfc9;
117 | }
118 |
119 | .token.entity {
120 | color: #e53935;
121 | }
122 |
123 | .token.function {
124 | color: #7c4dff;
125 | }
126 |
127 | .token.hexcode {
128 | color: #f76d47;
129 | }
130 |
131 | .token.id {
132 | color: #7c4dff;
133 | font-weight: bold;
134 | }
135 |
136 | .token.important {
137 | color: #7c4dff;
138 | font-weight: bold;
139 | }
140 |
141 | .token.inserted {
142 | color: #39adb5;
143 | }
144 |
145 | .token.keyword {
146 | color: #7c4dff;
147 | }
148 |
149 | .token.number {
150 | color: #f76d47;
151 | }
152 |
153 | .token.operator {
154 | color: #39adb5;
155 | }
156 |
157 | .token.prolog {
158 | color: #aabfc9;
159 | }
160 |
161 | .token.property {
162 | color: #39adb5;
163 | }
164 |
165 | .token.pseudo-class {
166 | color: #f6a434;
167 | }
168 |
169 | .token.pseudo-element {
170 | color: #f6a434;
171 | }
172 |
173 | .token.punctuation {
174 | color: #39adb5;
175 | }
176 |
177 | .token.regex {
178 | color: #6182b8;
179 | }
180 |
181 | .token.selector {
182 | color: #e53935;
183 | }
184 |
185 | .token.string {
186 | color: #f6a434;
187 | }
188 |
189 | .token.symbol {
190 | color: #7c4dff;
191 | }
192 |
193 | .token.tag {
194 | color: #e53935;
195 | }
196 |
197 | .token.unit {
198 | color: #f76d47;
199 | }
200 |
201 | .token.url {
202 | color: #e53935;
203 | }
204 |
205 | .token.variable {
206 | color: #e53935;
207 | }
208 |
--------------------------------------------------------------------------------
/src/views/resources/enhancedtabletoolbar.js:
--------------------------------------------------------------------------------
1 | import { alpha } from '@mui/material/styles'
2 | import Toolbar from '@mui/material/Toolbar'
3 | import Typography from '@mui/material/Typography'
4 | import Tooltip from '@mui/material/Tooltip'
5 | import IconButton from '@mui/material/IconButton'
6 | import { DoDisturb, ArrowCircleUp } from '@mui/icons-material'
7 | import useConfirm from 'hooks/useConfirm'
8 | import ConfirmDialog from 'ui-component/dialog/ConfirmDialog'
9 |
10 | const EnhancedTableToolbar = (props) => {
11 | const { numSelected, selectRows, closeNode, openNode, setSelected } = props
12 | const { confirm } = useConfirm()
13 |
14 | const getShowAppNames = () => {
15 | let allNames = ''
16 | if (selectRows) {
17 | selectRows.forEach((d) => {
18 | allNames += ',' + d.name
19 | })
20 | }
21 | if (allNames.length > 0) {
22 | allNames = allNames.substring(1)
23 | }
24 | return allNames
25 | }
26 |
27 | const closeNodes = async () => {
28 | let names = getShowAppNames()
29 | const confirmPayload = {
30 | title: 'Close',
31 | description: `are you sure to close ${names}?`,
32 | confirmButtonName: 'Yes',
33 | cancelButtonName: 'No'
34 | }
35 | const isConfirmed = await confirm(confirmPayload)
36 | if (isConfirmed) {
37 | selectRows.forEach((item) => {
38 | closeNode(item)
39 | })
40 | setSelected([])
41 | }
42 | }
43 |
44 | const openNodes = async () => {
45 | let names = getShowAppNames()
46 | const confirmPayload = {
47 | title: 'Open',
48 | description: `确认要打开${names}吗?`,
49 | confirmButtonName: '确定',
50 | cancelButtonName: '取消'
51 | }
52 | const isConfirmed = await confirm(confirmPayload)
53 | console.log('isConfirmed', isConfirmed, selectRows)
54 | if (isConfirmed) {
55 | selectRows.forEach((item) => {
56 | openNode(item)
57 | })
58 | setSelected([])
59 | }
60 | }
61 |
62 | return (
63 | <>
64 | {/* 0 && { bgcolor: (theme) => alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity) })
69 | }}
70 | >
71 | {numSelected > 0 ? (
72 |
73 | {numSelected} 条被选择
74 |
75 | ) : (
76 |
77 | 未选择条目
78 |
79 | )}
80 | {numSelected > 0 && (
81 | <>
82 |
83 | closeNodes()}>
84 |
85 |
86 |
87 |
88 | openNodes()}>
89 |
90 |
91 |
92 | >
93 | )}
94 | */}
95 |
96 | >
97 | )
98 | }
99 |
100 | export default EnhancedTableToolbar
101 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/podtablebody.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | // material-ui
3 | import { TableBody, TableCell, TableRow, Checkbox } from '@mui/material'
4 | import { PodHeadCells } from './enhancedpodtablehead'
5 |
6 | const PodTableBody = (props) => {
7 | const { visibleRows, emptyRows } = props
8 | // console.log(visibleRows)
9 | const [selected, setSelected] = useState([])
10 |
11 | const isRowSelected = (name) => selected.findIndex((selData) => selData.name === name) !== -1
12 |
13 | const handlePodRowSelectClick = (event, name) => {
14 | // const selectedIndex = selected.indexOf(name)
15 | const selectedIndex = selected.findIndex((selData) => selData.name === name)
16 | let newSelected = []
17 |
18 | if (selectedIndex === -1) {
19 | newSelected = newSelected.concat(selected, { name: name })
20 | } else if (selectedIndex === 0) {
21 | newSelected = newSelected.concat(selected.slice(1))
22 | } else if (selectedIndex === selected.length - 1) {
23 | newSelected = newSelected.concat(selected.slice(0, -1))
24 | } else if (selectedIndex > 0) {
25 | newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1))
26 | }
27 | setSelected(newSelected)
28 | }
29 | return (
30 |
31 | {visibleRows.map((row, index) => {
32 | const isItemSelected = isRowSelected(row.name)
33 | const labelId = `enhanced-table-checkbox-${index}`
34 | return (
35 | handlePodRowSelectClick(event, row.id, row.name)}
38 | role='checkbox'
39 | aria-checked={isItemSelected}
40 | tabIndex={-1}
41 | key={row.name}
42 | selected={isItemSelected}
43 | sx={{ cursor: 'pointer' }}
44 | >
45 |
46 |
47 |
48 | {row.name}
49 |
50 |
51 | {row.namespace ? (
52 |
53 | {row.namespace}
54 |
55 | ) : (
56 | '-'
57 | )}
58 |
59 | {row.status ? row.status : '-'}
60 | {row.pod_ip ? row.pod_ip : '-'}
61 | {row.labels ? Object.keys(row.labels).map(function (key) {
62 | return "" + key + "=" + row.labels[key];
63 | }).join(" ") : '-'}
64 | {row.node_name ? row.node_name : '-'}
65 | {row.node_ip ? row.node_ip : '-'}
66 | {row.start_time ? row.start_time : '-'}
67 |
68 | )
69 | })}
70 | {emptyRows > 0 ? (
71 |
72 |
73 |
74 | ) : null}
75 |
76 | )
77 | }
78 |
79 | export default PodTableBody
80 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/enhancedtabletoolbar.js:
--------------------------------------------------------------------------------
1 | import { alpha } from '@mui/material/styles'
2 | import Toolbar from '@mui/material/Toolbar'
3 | import Typography from '@mui/material/Typography'
4 | import Tooltip from '@mui/material/Tooltip'
5 | import IconButton from '@mui/material/IconButton'
6 | import FilterListIcon from '@mui/icons-material/FilterList'
7 | import StopCircleIcon from '@mui/icons-material/StopCircle'
8 | import useConfirm from 'hooks/useConfirm'
9 | import ConfirmDialog from 'ui-component/dialog/ConfirmDialog'
10 |
11 | const EnhancedTableToolbar = (props) => {
12 | const { numSelected, selectRows, stopJob, setSelected } = props
13 | const { confirm } = useConfirm()
14 |
15 | const getShowAppNames = () => {
16 | let allNames = ''
17 | if (selectRows) {
18 | selectRows.forEach((d) => {
19 | allNames += ',' + d.job_id
20 | })
21 | }
22 | if (allNames.length > 0) {
23 | allNames = allNames.substring(1)
24 | }
25 | return allNames
26 | }
27 |
28 |
29 | const stopApps = async () => {
30 | let names = getShowAppNames()
31 | const confirmPayload = {
32 | title: '停止',
33 | description: `确认要停止${numSelected}个作业"${names}"吗?`,
34 | confirmButtonName: '停止'
35 | // cancelButtonName: '取消'
36 | }
37 | const isConfirmed = await confirm(confirmPayload)
38 | console.log('isConfirmed', isConfirmed, selectRows)
39 | if (isConfirmed) {
40 | let jobids = []
41 | selectRows.forEach((item) => {
42 | jobids.push(item.job_id)
43 | })
44 | stopJob(jobids)
45 | setSelected([])
46 | }
47 | }
48 |
49 |
50 | return (
51 | <>
52 | 0 && { bgcolor: (theme) => alpha(theme.palette.primary.main, theme.palette.action.activatedOpacity) })
57 | }}
58 | >
59 | {numSelected > 0 ? (
60 |
61 | {numSelected} selected
62 |
63 | ) : (
64 |
65 | No selected items
66 |
67 | )}
68 | {numSelected > 0 ? (
69 | <>
70 | {/*
71 | susApps()}>
72 |
73 |
74 |
75 |
76 | resumeApps()}>
77 |
78 |
79 | */}
80 |
81 | stopApps()}>
82 |
83 |
84 |
85 | >
86 | ) : (
87 |
88 |
89 |
90 |
91 |
92 | )}
93 |
94 |
95 | >
96 | )
97 | }
98 |
99 | export default EnhancedTableToolbar
100 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/index.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useDispatch, useSelector } from 'react-redux'
3 | import { Outlet } from 'react-router-dom'
4 |
5 | // material-ui
6 | import { styled, useTheme } from '@mui/material/styles'
7 | import { AppBar, Box, CssBaseline, Toolbar, useMediaQuery } from '@mui/material'
8 |
9 | // project imports
10 | import Header from './Header'
11 | import Sidebar from './Sidebar'
12 | import { drawerWidth } from 'store/constant'
13 | import { SET_MENU } from 'store/actions'
14 |
15 | // styles
16 | const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })(({ theme, open }) => ({
17 | ...theme.typography.mainContent,
18 | ...(!open && {
19 | borderBottomLeftRadius: 0,
20 | borderBottomRightRadius: 0,
21 | transition: theme.transitions.create('margin', {
22 | easing: theme.transitions.easing.sharp,
23 | duration: theme.transitions.duration.leavingScreen
24 | }),
25 | [theme.breakpoints.up('md')]: {
26 | marginLeft: -(drawerWidth - 20),
27 | width: `calc(100% - ${drawerWidth}px)`
28 | },
29 | [theme.breakpoints.down('md')]: {
30 | marginLeft: '20px',
31 | width: `calc(100% - ${drawerWidth}px)`,
32 | padding: '16px'
33 | },
34 | [theme.breakpoints.down('sm')]: {
35 | marginLeft: '10px',
36 | width: `calc(100% - ${drawerWidth}px)`,
37 | padding: '16px',
38 | marginRight: '10px'
39 | }
40 | }),
41 | ...(open && {
42 | transition: theme.transitions.create('margin', {
43 | easing: theme.transitions.easing.easeOut,
44 | duration: theme.transitions.duration.enteringScreen
45 | }),
46 | marginLeft: 0,
47 | borderBottomLeftRadius: 0,
48 | borderBottomRightRadius: 0,
49 | width: `calc(100% - ${drawerWidth}px)`,
50 | [theme.breakpoints.down('md')]: {
51 | marginLeft: '20px'
52 | },
53 | [theme.breakpoints.down('sm')]: {
54 | marginLeft: '10px'
55 | }
56 | })
57 | }))
58 |
59 | // ==============================|| MAIN LAYOUT ||============================== //
60 |
61 | const MainLayout = () => {
62 | const theme = useTheme()
63 | const matchDownMd = useMediaQuery(theme.breakpoints.down('lg'))
64 |
65 | // Handle left drawer
66 | const leftDrawerOpened = useSelector((state) => state.customization.opened)
67 | const dispatch = useDispatch()
68 | const handleLeftDrawerToggle = () => {
69 | dispatch({ type: SET_MENU, opened: !leftDrawerOpened })
70 | }
71 | useEffect(() => {
72 | setTimeout(() => dispatch({ type: SET_MENU, opened: !matchDownMd }), 0)
73 | }, [matchDownMd])
74 |
75 | return (
76 |
77 |
78 | {/* header */}
79 |
89 |
90 |
91 |
92 |
93 |
94 | {/* drawer */}
95 |
96 |
97 | {/* main content */}
98 |
99 |
100 |
101 |
102 | )
103 | }
104 |
105 | export default MainLayout
106 |
--------------------------------------------------------------------------------
/src/views/k8sjobs/servicetablebody.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | // material-ui
3 | import { TableBody, TableCell, TableRow, Checkbox } from '@mui/material'
4 | import { ServiceHeadCells } from './enhancedservicetablehead'
5 |
6 | const ServiceTableBody = (props) => {
7 | const { visibleRows, emptyRows } = props
8 | // console.log(visibleRows)
9 | const [selected, setSelected] = useState([])
10 |
11 | const isRowSelected = (name) => selected.findIndex((selData) => selData.name === name) !== -1
12 |
13 | const handlePodRowSelectClick = (event, name) => {
14 | // const selectedIndex = selected.indexOf(name)
15 | const selectedIndex = selected.findIndex((selData) => selData.name === name)
16 | let newSelected = []
17 |
18 | if (selectedIndex === -1) {
19 | newSelected = newSelected.concat(selected, { name: name })
20 | } else if (selectedIndex === 0) {
21 | newSelected = newSelected.concat(selected.slice(1))
22 | } else if (selectedIndex === selected.length - 1) {
23 | newSelected = newSelected.concat(selected.slice(0, -1))
24 | } else if (selectedIndex > 0) {
25 | newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1))
26 | }
27 | setSelected(newSelected)
28 | }
29 | return (
30 |
31 | {visibleRows.map((row, index) => {
32 | const isItemSelected = isRowSelected(row.name)
33 | const labelId = `enhanced-table-checkbox-${index}`
34 | return (
35 | handlePodRowSelectClick(event, row.id, row.name)}
38 | role='checkbox'
39 | aria-checked={isItemSelected}
40 | tabIndex={-1}
41 | key={row.name}
42 | selected={isItemSelected}
43 | sx={{ cursor: 'pointer' }}
44 | >
45 |
46 |
47 |
48 | {row.name}
49 |
50 |
51 | {row.namespace ? (
52 |
53 | {row.namespace}
54 |
55 | ) : (
56 | '-'
57 | )}
58 |
59 | {row.type ? row.type : '-'}
60 | {row.cluster_ip ? row.cluster_ip : '-'}
61 | {row.external_ip ? row.external_ip : '-'}
62 | {row.labels ? Object.keys(row.labels).map(function (key) {
63 | return "" + key + "=" + row.labels[key];
64 | }).join(" ") : '-'}
65 | {row.ports ? JSON.stringify(row.ports) : '-'}
66 | {row.created_at ? row.created_at : '-'}
67 |
68 | )
69 | })}
70 | {emptyRows > 0 ? (
71 |
72 |
73 |
74 | ) : null}
75 |
76 | )
77 | }
78 |
79 | export default ServiceTableBody
80 |
--------------------------------------------------------------------------------
/src/themes/typography.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Typography used in theme
3 | * @param {JsonObject} theme theme customization object
4 | */
5 |
6 | export default function themeTypography(theme) {
7 | return {
8 | fontFamily: theme?.customization?.fontFamily,
9 | h6: {
10 | fontWeight: 500,
11 | color: theme.heading,
12 | fontSize: '0.75rem'
13 | },
14 | h5: {
15 | fontSize: '0.875rem',
16 | color: theme.heading,
17 | fontWeight: 500
18 | },
19 | h4: {
20 | fontSize: '1rem',
21 | color: theme.heading,
22 | fontWeight: 600
23 | },
24 | h3: {
25 | fontSize: '1.25rem',
26 | color: theme.heading,
27 | fontWeight: 600
28 | },
29 | h2: {
30 | fontSize: '1.5rem',
31 | color: theme.heading,
32 | fontWeight: 700
33 | },
34 | h1: {
35 | fontSize: '2.125rem',
36 | color: theme.heading,
37 | fontWeight: 700
38 | },
39 | subtitle1: {
40 | fontSize: '0.875rem',
41 | fontWeight: 500,
42 | color: theme.textDark
43 | },
44 | subtitle2: {
45 | fontSize: '0.75rem',
46 | fontWeight: 400,
47 | color: theme.darkTextSecondary
48 | },
49 | caption: {
50 | fontSize: '0.75rem',
51 | color: theme.darkTextSecondary,
52 | fontWeight: 400
53 | },
54 | body1: {
55 | fontSize: '0.875rem',
56 | fontWeight: 400,
57 | lineHeight: '1.334em'
58 | },
59 | body2: {
60 | letterSpacing: '0em',
61 | fontWeight: 400,
62 | lineHeight: '1.5em',
63 | color: theme.darkTextPrimary
64 | },
65 | button: {
66 | textTransform: 'capitalize'
67 | },
68 | customInput: {
69 | marginTop: 1,
70 | marginBottom: 1,
71 | '& > label': {
72 | top: 23,
73 | left: 0,
74 | color: theme.grey500,
75 | '&[data-shrink="false"]': {
76 | top: 5
77 | }
78 | },
79 | '& > div > input': {
80 | padding: '30.5px 14px 11.5px !important'
81 | },
82 | '& legend': {
83 | display: 'none'
84 | },
85 | '& fieldset': {
86 | top: 0
87 | }
88 | },
89 | mainContent: {
90 | backgroundColor: theme.background,
91 | width: '100%',
92 | minHeight: 'calc(100vh - 75px)',
93 | flexGrow: 1,
94 | padding: '20px',
95 | marginTop: '75px',
96 | marginRight: '20px',
97 | borderRadius: `${theme?.customization?.borderRadius}px`
98 | },
99 | menuCaption: {
100 | fontSize: '0.875rem',
101 | fontWeight: 500,
102 | color: theme.heading,
103 | padding: '6px',
104 | textTransform: 'capitalize',
105 | marginTop: '10px'
106 | },
107 | subMenuCaption: {
108 | fontSize: '0.6875rem',
109 | fontWeight: 500,
110 | color: theme.darkTextSecondary,
111 | textTransform: 'capitalize'
112 | },
113 | commonAvatar: {
114 | cursor: 'pointer',
115 | borderRadius: '8px'
116 | },
117 | smallAvatar: {
118 | width: '22px',
119 | height: '22px',
120 | fontSize: '1rem'
121 | },
122 | mediumAvatar: {
123 | width: '34px',
124 | height: '34px',
125 | fontSize: '1.2rem'
126 | },
127 | largeAvatar: {
128 | width: '44px',
129 | height: '44px',
130 | fontSize: '1.5rem'
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/views/resources/enhancedtablehead.js:
--------------------------------------------------------------------------------
1 | import TableHead from '@mui/material/TableHead'
2 | import TableRow from '@mui/material/TableRow'
3 | import TableCell from '@mui/material/TableCell'
4 | // import Checkbox from '@mui/material/Checkbox'
5 | import TableSortLabel from '@mui/material/TableSortLabel'
6 | import Box from '@mui/material/Box'
7 | import { visuallyHidden } from '@mui/utils'
8 |
9 | export const HeadCells = [
10 | {
11 | id: 'name',
12 | align: 'center',
13 | disablePadding: true,
14 | label: 'Host Name',
15 | sortable: true
16 | },
17 | {
18 | id: 'coreNum',
19 | align: 'left',
20 | disablePadding: false,
21 | label: 'Cores',
22 | sortable: true
23 | },
24 | {
25 | id: 'cpus',
26 | align: 'left',
27 | disablePadding: false,
28 | label: 'CPUs',
29 | sortable: false
30 | },
31 | {
32 | id: 'gpus',
33 | align: 'left',
34 | disablePadding: false,
35 | label: 'GPUs',
36 | sortable: false
37 | },
38 | {
39 | id: 'cpuload',
40 | align: 'right',
41 | disablePadding: false,
42 | label: 'cpu loads',
43 | sortable: false
44 | },
45 | {
46 | id: 'mem',
47 | align: 'right',
48 | disablePadding: false,
49 | label: 'Memory',
50 | sortable: true
51 | },
52 | {
53 | id: 'weight',
54 | align: 'right',
55 | disablePadding: false,
56 | label: 'Weight',
57 | sortable: true
58 | },
59 | {
60 | id: 'status',
61 | align: 'right',
62 | disablePadding: false,
63 | label: 'State',
64 | sortable: true
65 | }
66 | ]
67 |
68 | const EnhancedTableHead = (props) => {
69 | const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props
70 | const createSortHandler = (property) => (event) => {
71 | onRequestSort(event, property)
72 | }
73 |
74 | return (
75 |
76 |
77 | {/*
78 | 0 && numSelected < rowCount}
81 | checked={rowCount > 0 && numSelected === rowCount}
82 | onChange={onSelectAllClick}
83 | inputProps={{ 'aria-label': 'select all desserts' }}
84 | />
85 | */}
86 | {HeadCells.map((headCell) => (
87 |
93 | {headCell.sortable ? (
94 |
99 | {headCell.label}
100 | {orderBy === headCell.id ? (
101 |
102 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
103 |
104 | ) : null}
105 |
106 | ) : (
107 | {headCell.label}
108 | )}
109 |
110 | ))}
111 |
112 |
113 | )
114 | }
115 |
116 | export default EnhancedTableHead
117 |
--------------------------------------------------------------------------------
/src/assets/scss/_themes-vars.module.scss:
--------------------------------------------------------------------------------
1 | // paper & background
2 | $paper: #ffffff;
3 |
4 | // primary
5 | $primaryLight: #e3f2fd;
6 | $primaryMain: #2196f3;
7 | $primaryDark: #1e88e5;
8 | $primary200: #90caf9;
9 | $primary800: #1565c0;
10 |
11 | // secondary
12 | $secondaryLight: #ede7f6;
13 | $secondaryMain: #673ab7;
14 | $secondaryDark: #5e35b1;
15 | $secondary200: #b39ddb;
16 | $secondary800: #4527a0;
17 | $secondaryIcon: #1c8b7c;
18 | $secondaryIconHover: #2e9486;
19 | $secondaryIconText: #ffffff;
20 |
21 | // success Colors
22 | $successLight: #cdf5d8;
23 | $success200: #69f0ae;
24 | $successMain: #00e676;
25 | $successDark: #00c853;
26 |
27 | // error
28 | $errorLight: #f3d2d2;
29 | $errorMain: #f44336;
30 | $errorDark: #c62828;
31 |
32 | // orange
33 | $orangeLight: #fbe9e7;
34 | $orangeMain: #ffab91;
35 | $orangeDark: #d84315;
36 |
37 | // warning
38 | $warningLight: #fff8e1;
39 | $warningMain: #ffe57f;
40 | $warningDark: #ffc107;
41 |
42 | // grey
43 | $grey50: #fafafa;
44 | $grey100: #f5f5f5;
45 | $grey200: #eeeeee;
46 | $grey300: #e0e0e0;
47 | $grey500: #9e9e9e;
48 | $grey600: #757575;
49 | $grey700: #616161;
50 | $grey900: #212121;
51 |
52 | // ==============================|| DARK THEME VARIANTS ||============================== //
53 |
54 | // paper & background
55 | $darkBackground: #191b1f;
56 | $darkPaper: #191b1f;
57 |
58 | // dark 800 & 900
59 | $darkLevel1: #252525; // level 1
60 | $darkLevel2: #242424; // level 2
61 |
62 | // primary dark
63 | $darkPrimaryLight: #23262c;
64 | $darkPrimaryMain: #23262c;
65 | $darkPrimaryDark: #191b1f;
66 | $darkPrimary200: #c9d4e9;
67 | $darkPrimary800: #32353b;
68 |
69 | // secondary dark
70 | $darkSecondaryLight: #454c59;
71 | $darkSecondaryMain: #7c4dff;
72 | $darkSecondaryDark: #ffffff;
73 | $darkSecondary200: #32353b;
74 | $darkSecondary800: #6200ea;
75 |
76 | // text variants
77 | $darkTextTitle: #d7dcec;
78 | $darkTextPrimary: #bdc8f0;
79 | $darkTextSecondary: #8492c4;
80 |
81 | // ==============================|| JAVASCRIPT ||============================== //
82 |
83 | :export {
84 | // paper & background
85 | paper: $paper;
86 |
87 | // primary
88 | primaryLight: $primaryLight;
89 | primary200: $primary200;
90 | primaryMain: $primaryMain;
91 | primaryDark: $primaryDark;
92 | primary800: $primary800;
93 |
94 | // secondary
95 | secondaryLight: $secondaryLight;
96 | secondary200: $secondary200;
97 | secondaryMain: $secondaryMain;
98 | secondaryDark: $secondaryDark;
99 | secondary800: $secondary800;
100 | secondaryIcon: $secondaryIcon;
101 | secondaryIconHover: $secondaryIconHover;
102 | secondaryIconText: $secondaryIconText;
103 |
104 | // success
105 | successLight: $successLight;
106 | success200: $success200;
107 | successMain: $successMain;
108 | successDark: $successDark;
109 |
110 | // error
111 | errorLight: $errorLight;
112 | errorMain: $errorMain;
113 | errorDark: $errorDark;
114 |
115 | // orange
116 | orangeLight: $orangeLight;
117 | orangeMain: $orangeMain;
118 | orangeDark: $orangeDark;
119 |
120 | // warning
121 | warningLight: $warningLight;
122 | warningMain: $warningMain;
123 | warningDark: $warningDark;
124 |
125 | // grey
126 | grey50: $grey50;
127 | grey100: $grey100;
128 | grey200: $grey200;
129 | grey300: $grey300;
130 | grey500: $grey500;
131 | grey600: $grey600;
132 | grey700: $grey700;
133 | grey900: $grey900;
134 |
135 | // ==============================|| DARK THEME VARIANTS ||============================== //
136 |
137 | // paper & background
138 | darkPaper: $darkPaper;
139 | darkBackground: $darkBackground;
140 |
141 | // dark 800 & 900
142 | darkLevel1: $darkLevel1;
143 | darkLevel2: $darkLevel2;
144 |
145 | // text variants
146 | darkTextTitle: $darkTextTitle;
147 | darkTextPrimary: $darkTextPrimary;
148 | darkTextSecondary: $darkTextSecondary;
149 |
150 | // primary dark
151 | darkPrimaryLight: $darkPrimaryLight;
152 | darkPrimaryMain: $darkPrimaryMain;
153 | darkPrimaryDark: $darkPrimaryDark;
154 | darkPrimary200: $darkPrimary200;
155 | darkPrimary800: $darkPrimary800;
156 |
157 | // secondary dark
158 | darkSecondaryLight: $darkSecondaryLight;
159 | darkSecondaryMain: $darkSecondaryMain;
160 | darkSecondaryDark: $darkSecondaryDark;
161 | darkSecondary200: $darkSecondary200;
162 | darkSecondary800: $darkSecondary800;
163 | }
164 |
--------------------------------------------------------------------------------
/src/themes/palette.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Color intention that you want to used in your theme
3 | * @param {JsonObject} theme Theme customization object
4 | */
5 |
6 | export default function themePalette(theme) {
7 | return {
8 | mode: theme?.customization?.navType,
9 | common: {
10 | black: theme.colors?.darkPaper,
11 | dark: theme.colors?.darkPrimaryMain
12 | },
13 | primary: {
14 | light: theme.customization.isDarkMode ? theme.colors?.darkPrimaryLight : theme.colors?.primaryLight,
15 | main: theme.colors?.primaryMain,
16 | dark: theme.customization.isDarkMode ? theme.colors?.darkPrimaryDark : theme.colors?.primaryDark,
17 | 200: theme.customization.isDarkMode ? theme.colors?.darkPrimary200 : theme.colors?.primary200,
18 | 800: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.primary800
19 | },
20 | secondary: {
21 | light: theme.customization.isDarkMode ? theme.colors?.darkSecondaryLight : theme.colors?.secondaryLight,
22 | main: theme.customization.isDarkMode ? theme.colors?.darkSecondaryMain : theme.colors?.secondaryMain,
23 | dark: theme.customization.isDarkMode ? theme.colors?.darkSecondaryDark : theme.colors?.secondaryDark,
24 | iconBg: theme.customization.isDarkMode ? theme.colors?.darkSecondaryLight : theme.colors?.secondaryIcon,
25 | iconHover: theme.customization.isDarkMode ? theme.colors?.darkSecondaryLight : theme.colors?.secondaryIconHover,
26 | iconText: theme.customization.isDarkMode ? theme.colors?.darkSecondaryLight : theme.colors?.secondaryIconText,
27 | 200: theme.colors?.secondary200,
28 | 800: theme.colors?.secondary800
29 | },
30 | error: {
31 | light: theme.colors?.errorLight,
32 | main: theme.colors?.errorMain,
33 | dark: theme.colors?.errorDark
34 | },
35 | orange: {
36 | light: theme.colors?.orangeLight,
37 | main: theme.colors?.orangeMain,
38 | dark: theme.colors?.orangeDark
39 | },
40 | warning: {
41 | light: theme.colors?.warningLight,
42 | main: theme.colors?.warningMain,
43 | dark: theme.colors?.warningDark
44 | },
45 | success: {
46 | light: theme.colors?.successLight,
47 | 200: theme.colors?.success200,
48 | main: theme.colors?.successMain,
49 | dark: theme.colors?.successDark
50 | },
51 | grey: {
52 | 50: theme.colors?.grey50,
53 | 100: theme.colors?.grey100,
54 | 200: theme.colors?.grey200,
55 | 300: theme.colors?.grey300,
56 | 500: theme.darkTextSecondary,
57 | 600: theme.heading,
58 | 700: theme.darkTextPrimary,
59 | 900: theme.textDark
60 | },
61 | dark: {
62 | light: theme.colors?.darkTextPrimary,
63 | main: theme.colors?.darkLevel1,
64 | dark: theme.colors?.darkLevel2,
65 | 800: theme.colors?.darkBackground,
66 | 900: theme.colors?.darkPaper
67 | },
68 | text: {
69 | primary: theme.darkTextPrimary,
70 | secondary: theme.darkTextSecondary,
71 | dark: theme.textDark,
72 | hint: theme.colors?.grey100
73 | },
74 | background: {
75 | paper: theme.paper,
76 | default: theme.backgroundDefault
77 | },
78 | card: {
79 | main: theme.customization.isDarkMode ? theme.colors?.darkPrimaryMain : theme.colors?.paper,
80 | light: theme.customization.isDarkMode ? theme.colors?.darkPrimary200 : theme.colors?.paper,
81 | hover: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.paper
82 | },
83 | asyncSelect: {
84 | main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.grey50
85 | },
86 | canvasHeader: {
87 | deployLight: theme.colors?.primaryLight,
88 | deployDark: theme.colors?.primaryDark,
89 | saveLight: theme.colors?.secondaryLight,
90 | saveDark: theme.colors?.secondaryDark,
91 | settingsLight: theme.colors?.grey300,
92 | settingsDark: theme.colors?.grey700
93 | },
94 | codeEditor: {
95 | main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.primaryLight
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/ui-component/markdown/CodeBlock.js:
--------------------------------------------------------------------------------
1 | import { IconClipboard, IconDownload } from '@tabler/icons'
2 | import { memo, useState } from 'react'
3 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
4 | import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
5 | import PropTypes from 'prop-types'
6 | import { Box, IconButton, Popover, Typography } from '@mui/material'
7 | import { useTheme } from '@mui/material/styles'
8 |
9 | const programmingLanguages = {
10 | javascript: '.js',
11 | python: '.py',
12 | java: '.java',
13 | c: '.c',
14 | cpp: '.cpp',
15 | 'c++': '.cpp',
16 | 'c#': '.cs',
17 | ruby: '.rb',
18 | php: '.php',
19 | swift: '.swift',
20 | 'objective-c': '.m',
21 | kotlin: '.kt',
22 | typescript: '.ts',
23 | go: '.go',
24 | perl: '.pl',
25 | rust: '.rs',
26 | scala: '.scala',
27 | haskell: '.hs',
28 | lua: '.lua',
29 | shell: '.sh',
30 | sql: '.sql',
31 | html: '.html',
32 | css: '.css'
33 | }
34 |
35 | export const CodeBlock = memo(({ language, chatflowid, isDialog, value }) => {
36 | const theme = useTheme()
37 | const [anchorEl, setAnchorEl] = useState(null)
38 | const openPopOver = Boolean(anchorEl)
39 |
40 | const handleClosePopOver = () => {
41 | setAnchorEl(null)
42 | }
43 |
44 | const copyToClipboard = (event) => {
45 | if (!navigator.clipboard || !navigator.clipboard.writeText) {
46 | return
47 | }
48 |
49 | navigator.clipboard.writeText(value)
50 | setAnchorEl(event.currentTarget)
51 | setTimeout(() => {
52 | handleClosePopOver()
53 | }, 1500)
54 | }
55 |
56 | const downloadAsFile = () => {
57 | const fileExtension = programmingLanguages[language] || '.file'
58 | const suggestedFileName = `file-${chatflowid}${fileExtension}`
59 | const fileName = suggestedFileName
60 |
61 | if (!fileName) {
62 | // user pressed cancel on prompt
63 | return
64 | }
65 |
66 | const blob = new Blob([value], { type: 'text/plain' })
67 | const url = URL.createObjectURL(blob)
68 | const link = document.createElement('a')
69 | link.download = fileName
70 | link.href = url
71 | link.style.display = 'none'
72 | document.body.appendChild(link)
73 | link.click()
74 | document.body.removeChild(link)
75 | URL.revokeObjectURL(url)
76 | }
77 |
78 | return (
79 |
80 |
81 |
82 | {language}
83 |
84 |
85 |
86 |
87 |
100 |
101 | Copied!
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 | {value}
112 |
113 |
114 | )
115 | })
116 | CodeBlock.displayName = 'CodeBlock'
117 |
118 | CodeBlock.propTypes = {
119 | language: PropTypes.string,
120 | chatflowid: PropTypes.string,
121 | isDialog: PropTypes.bool,
122 | value: PropTypes.string
123 | }
124 |
--------------------------------------------------------------------------------
/src/views/hpcjobs/enhancedtablehead.js:
--------------------------------------------------------------------------------
1 | import TableHead from '@mui/material/TableHead'
2 | import TableRow from '@mui/material/TableRow'
3 | import TableCell from '@mui/material/TableCell'
4 | import Checkbox from '@mui/material/Checkbox'
5 | import TableSortLabel from '@mui/material/TableSortLabel'
6 | import Box from '@mui/material/Box'
7 | import { visuallyHidden } from '@mui/utils'
8 |
9 | export const HeadCells = [
10 | {
11 | id: 'jobid',
12 | align: 'left',
13 | disablePadding: true,
14 | label: 'Job ID',
15 | sortable: true
16 | },
17 | {
18 | id: 'partition',
19 | align: 'left',
20 | disablePadding: false,
21 | label: 'Queues',
22 | sortable: true
23 | },
24 | {
25 | id: 'jobname',
26 | align: 'left',
27 | disablePadding: false,
28 | label: 'Job Name',
29 | sortable: true
30 | },
31 | {
32 | id: 'status',
33 | align: 'left',
34 | disablePadding: false,
35 | label: 'Job Status',
36 | sortable: false
37 | },
38 | {
39 | id: 'reason',
40 | align: 'left',
41 | disablePadding: false,
42 | label: 'State Reason',
43 | sortable: false
44 | },
45 | {
46 | id: 'hostNum',
47 | align: 'center',
48 | disablePadding: false,
49 | label: 'Resource',
50 | sortable: false
51 | },
52 | {
53 | id: 'submitted',
54 | align: 'center',
55 | disablePadding: false,
56 | label: 'Submission Time',
57 | sortable: true
58 | },
59 | {
60 | id: 'started',
61 | align: 'center',
62 | disablePadding: false,
63 | label: 'Start Time',
64 | sortable: true
65 | },
66 | {
67 | id: 'usergroup',
68 | align: 'center',
69 | disablePadding: false,
70 | label: 'Account',
71 | sortable: true
72 | },
73 | {
74 | id: 'user',
75 | align: 'center',
76 | disablePadding: false,
77 | label: 'Submission User',
78 | sortable: true
79 | }
80 | ]
81 |
82 | const EnhancedTableHead = (props) => {
83 | const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort } = props
84 | const createSortHandler = (property) => (event) => {
85 | onRequestSort(event, property)
86 | }
87 |
88 | return (
89 |
90 |
91 |
92 | 0 && numSelected < rowCount}
95 | checked={rowCount > 0 && numSelected === rowCount}
96 | onChange={onSelectAllClick}
97 | inputProps={{ 'aria-label': 'select all desserts' }}
98 | />
99 |
100 | {HeadCells.map((headCell) => (
101 |
107 | {headCell.sortable ? (
108 |
113 | {headCell.label}
114 | {orderBy === headCell.id ? (
115 |
116 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
117 |
118 | ) : null}
119 |
120 | ) : (
121 | {headCell.label}
122 | )}
123 |
124 | ))}
125 |
126 |
127 | )
128 | }
129 |
130 | export default EnhancedTableHead
131 |
--------------------------------------------------------------------------------
/src/ui-component/dialog/SaveChatflowDialog.js:
--------------------------------------------------------------------------------
1 | import { createPortal } from 'react-dom'
2 | import { useState, useEffect, useContext } from 'react'
3 | import PropTypes from 'prop-types'
4 |
5 | import { Button, Dialog, DialogActions, DialogContent, OutlinedInput, DialogTitle, FormGroup, FormControlLabel, Checkbox, FormLabel, FormControl } from '@mui/material'
6 | import { StyledButton } from 'ui-component/button/StyledButton'
7 | import MetaContext from 'store/context/MetaContext'
8 |
9 | const SaveChatflowDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
10 | const portalElement = document.getElementById('portal')
11 |
12 | const [chatflowName, setChatflowName] = useState('')
13 | const [isReadyToSave, setIsReadyToSave] = useState(false)
14 | const [selected, setSelected] = useState([])
15 | const [metaData] = useContext(MetaContext)
16 | const [tags, setTags] = useState([])
17 |
18 | useEffect(() => {
19 | if (dialogProps.data) {
20 | let metatags = metaData.tags
21 | setChatflowName(dialogProps.data.name)
22 | if (dialogProps.data.tags) {
23 | let selectedTemp = dialogProps.data.tags.split(",")
24 | for (const tag of metatags) {
25 | if (selectedTemp.indexOf(tag.label) > -1) {
26 | tag.selected = true
27 | } else {
28 | tag.selected = false
29 | }
30 | }
31 | setSelected(selectedTemp)
32 | } else {
33 | for (const tag of metatags) {
34 | tag.selected = false
35 | }
36 | setSelected([])
37 | }
38 | setTags(metatags)
39 | }
40 | }, [dialogProps])
41 |
42 | useEffect(() => {
43 | if (chatflowName) setIsReadyToSave(true)
44 | else setIsReadyToSave(false)
45 | }, [chatflowName])
46 |
47 | const handleChange = (event, tag) => {
48 | tag.selected = !tag.selected
49 | if (tag.selected) {
50 | selected.push(tag.label)
51 | } else {
52 | let index = selected.indexOf(tag.label)
53 | selected.splice(index, 1);
54 | }
55 | setSelected(selected)
56 | let newObj = Object.assign([], tags);
57 | setTags(newObj)
58 | console.log(selected)
59 | }
60 |
61 | const component = show ? (
62 |
106 | ) : null
107 |
108 | return createPortal(component, portalElement)
109 | }
110 |
111 | SaveChatflowDialog.propTypes = {
112 | show: PropTypes.bool,
113 | dialogProps: PropTypes.object,
114 | onCancel: PropTypes.func,
115 | onConfirm: PropTypes.func
116 | }
117 |
118 | export default SaveChatflowDialog
119 |
--------------------------------------------------------------------------------
/src/store/context/ReactFlowContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import { getUniqueNodeId } from 'utils/genericHelper'
4 | import { cloneDeep } from 'lodash'
5 |
6 | const initialValue = {
7 | reactFlowInstance: null,
8 | setReactFlowInstance: () => {},
9 | duplicateNode: () => {},
10 | deleteNode: () => {},
11 | deleteEdge: () => {}
12 | }
13 |
14 | export const flowContext = createContext(initialValue)
15 |
16 | export const ReactFlowContext = ({ children }) => {
17 | const [reactFlowInstance, setReactFlowInstance] = useState(null)
18 |
19 | const deleteNode = (nodeid) => {
20 | deleteConnectedInput(nodeid, 'node')
21 | reactFlowInstance.setNodes(reactFlowInstance.getNodes().filter((n) => n.id !== nodeid))
22 | reactFlowInstance.setEdges(reactFlowInstance.getEdges().filter((ns) => ns.source !== nodeid && ns.target !== nodeid))
23 | }
24 |
25 | const deleteEdge = (edgeid) => {
26 | deleteConnectedInput(edgeid, 'edge')
27 | reactFlowInstance.setEdges(reactFlowInstance.getEdges().filter((edge) => edge.id !== edgeid))
28 | }
29 |
30 | const deleteConnectedInput = (id, type) => {
31 | const connectedEdges =
32 | type === 'node'
33 | ? reactFlowInstance.getEdges().filter((edge) => edge.source === id)
34 | : reactFlowInstance.getEdges().filter((edge) => edge.id === id)
35 |
36 | for (const edge of connectedEdges) {
37 | const targetNodeId = edge.target
38 | const sourceNodeId = edge.source
39 | const targetInput = edge.targetHandle.split('-')[2]
40 |
41 | reactFlowInstance.setNodes((nds) =>
42 | nds.map((node) => {
43 | if (node.id === targetNodeId) {
44 | let value
45 | const inputAnchor = node.data.inputAnchors.find((ancr) => ancr.name === targetInput)
46 | const inputParam = node.data.inputParams.find((param) => param.name === targetInput)
47 |
48 | if (inputAnchor && inputAnchor.list) {
49 | const values = node.data.inputs[targetInput] || []
50 | value = values.filter((item) => !item.includes(sourceNodeId))
51 | } else if (inputParam && inputParam.acceptVariable) {
52 | value = node.data.inputs[targetInput].replace(`{{${sourceNodeId}.data.instance}}`, '') || ''
53 | } else {
54 | value = ''
55 | }
56 | node.data = {
57 | ...node.data,
58 | inputs: {
59 | ...node.data.inputs,
60 | [targetInput]: value
61 | }
62 | }
63 | }
64 | return node
65 | })
66 | )
67 | }
68 | }
69 |
70 | const duplicateNode = (id) => {
71 | const nodes = reactFlowInstance.getNodes()
72 | const originalNode = nodes.find((n) => n.id === id)
73 | if (originalNode) {
74 | const newNodeId = getUniqueNodeId(originalNode.data, nodes)
75 | const clonedNode = cloneDeep(originalNode)
76 |
77 | const duplicatedNode = {
78 | ...clonedNode,
79 | id: newNodeId,
80 | position: {
81 | x: clonedNode.position.x + 400,
82 | y: clonedNode.position.y
83 | },
84 | positionAbsolute: {
85 | x: clonedNode.positionAbsolute.x + 400,
86 | y: clonedNode.positionAbsolute.y
87 | },
88 | data: {
89 | ...clonedNode.data,
90 | id: newNodeId
91 | },
92 | selected: false
93 | }
94 |
95 | const dataKeys = ['inputParams', 'inputAnchors', 'outputAnchors']
96 |
97 | for (const key of dataKeys) {
98 | for (const item of duplicatedNode.data[key]) {
99 | if (item.id) {
100 | item.id = item.id.replace(id, newNodeId)
101 | }
102 | }
103 | }
104 |
105 | reactFlowInstance.setNodes([...nodes, duplicatedNode])
106 | }
107 | }
108 |
109 | return (
110 |
119 | {children}
120 |
121 | )
122 | }
123 |
124 | ReactFlowContext.propTypes = {
125 | children: PropTypes.any
126 | }
127 |
--------------------------------------------------------------------------------
/src/menu-items/dashboard.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 | // assets
3 | import LineStyleOutlinedIcon from '@mui/icons-material/LineStyleOutlined'
4 | import ManageAccountsIcon from '@mui/icons-material/ManageAccounts'
5 | import GroupsIcon from '@mui/icons-material/Groups'
6 | import AccountBoxIcon from '@mui/icons-material/AccountBox'
7 | import SocialDistanceIcon from '@mui/icons-material/SocialDistance'
8 | import StorageIcon from '@mui/icons-material/Storage'
9 | import FitbitIcon from '@mui/icons-material/Fitbit'
10 | import AccountCircleIcon from '@mui/icons-material/AccountCircle'
11 | import {
12 | IconBabyCarriage,
13 | IconBuildingFactory2,
14 | IconBuildingStore,
15 | IconContainer,
16 | IconCpu,
17 | IconHierarchy,
18 | IconKey,
19 | IconTool,
20 | IconServer2,
21 | IconSettingsAutomation
22 | } from '@tabler/icons'
23 | // constant
24 | const icons = {
25 | IconCpu,
26 | IconContainer,
27 | IconHierarchy,
28 | IconBuildingStore,
29 | IconKey,
30 | IconTool,
31 | IconBuildingFactory2,
32 | IconBabyCarriage,
33 | LineStyleOutlinedIcon,
34 | ManageAccountsIcon,
35 | GroupsIcon,
36 | AccountBoxIcon,
37 | SocialDistanceIcon,
38 | IconServer2,
39 | StorageIcon,
40 | FitbitIcon,
41 | IconSettingsAutomation,
42 | AccountCircleIcon
43 | }
44 | import config from 'config'
45 |
46 | // ==============================|| DASHBOARD MENU ITEMS ||============================== //
47 |
48 | const dashboard = () => {
49 | let userInfo = JSON.parse(localStorage.getItem('userinfos'))
50 | let isAdmin = userInfo.isAdmin
51 | let db = {
52 | id: 'dashboard',
53 | title: '',
54 | type: 'group',
55 | children: [
56 | {
57 | id: 'dashboard',
58 | title: 'Dashboard',
59 | type: 'item',
60 | url: '/dashboard',
61 | icon: icons.LineStyleOutlinedIcon,
62 | breadcrumbs: true
63 | },
64 | {
65 | id: 'resources',
66 | title: 'Resources',
67 | type: 'item',
68 | url: '/resources',
69 | icon: icons.StorageIcon,
70 | breadcrumbs: true
71 | },
72 | {
73 | id: 'workload',
74 | title: 'Tasks',
75 | type: 'collapse',
76 | url: '',
77 | icon: icons.FitbitIcon,
78 | breadcrumbs: true,
79 | children: [
80 | {
81 | id: 'hpcjobs',
82 | title: 'Jobs',
83 | type: 'item',
84 | url: '/hpcjobs',
85 | icon: icons.IconCpu,
86 | breadcrumbs: true
87 | },
88 | {
89 | id: 'k8sjobs',
90 | title: 'Services',
91 | type: 'item',
92 | url: '/k8sjobs',
93 | icon: icons.IconContainer,
94 | breadcrumbs: true,
95 | hide: !isAdmin || !config.FEATURE_TOGGLE_K8S
96 | }
97 | ]
98 | },
99 | {
100 | id: 'tools',
101 | title: 'Partitions',
102 | type: 'item',
103 | url: '/jobqueue',
104 | icon: AccountCircleIcon,
105 | breadcrumbs: true
106 | },
107 | {
108 | id: 'users',
109 | title: 'Organizations',
110 | type: 'collapse',
111 | icon: icons.ManageAccountsIcon,
112 | breadcrumbs: true,
113 | hide: !isAdmin,
114 | children: [
115 | {
116 | id: 'orgmanage',
117 | title: 'Organization',
118 | type: 'item',
119 | url: '/orgmanage',
120 | icon: icons.GroupsIcon,
121 | breadcrumbs: true
122 | },
123 | {
124 | id: 'usermanage',
125 | title: 'Members',
126 | type: 'item',
127 | url: '/usermanage',
128 | icon: icons.AccountBoxIcon,
129 | breadcrumbs: true
130 | },
131 | {
132 | id: 'qosmanage',
133 | title: 'QOS',
134 | type: 'item',
135 | url: '/qosmanage',
136 | icon: icons.IconSettingsAutomation,
137 | breadcrumbs: true
138 | }
139 | ]
140 | }
141 | ]
142 | }
143 | let res = {
144 | ...db
145 | }
146 | return res
147 | }
148 |
149 | export default dashboard
150 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Sidebar/MenuList/NavCollapse/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { useState } from 'react'
3 | import { useSelector } from 'react-redux'
4 |
5 | // material-ui
6 | import { useTheme } from '@mui/material/styles'
7 | import { Collapse, List, ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material'
8 |
9 | // project imports
10 | import NavItem from '../NavItem'
11 |
12 | // assets
13 | import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'
14 | import { IconChevronDown, IconChevronUp } from '@tabler/icons'
15 |
16 | // ==============================|| SIDEBAR MENU LIST COLLAPSE ITEMS ||============================== //
17 |
18 | const NavCollapse = ({ menu, level }) => {
19 | const theme = useTheme()
20 | const customization = useSelector((state) => state.customization)
21 |
22 | const [open, setOpen] = useState(false)
23 | // const [selected, setSelected] = useState(null)
24 |
25 | const handleClick = () => {
26 | setOpen(!open)
27 | // setSelected(!selected ? menu.id : null)
28 | }
29 |
30 | // menu collapse & item
31 | const menus = menu.children
32 | ?.filter((item) => {
33 | if (Object.hasOwn(item, 'hide') && item.hide) {
34 | return false
35 | }
36 | return true
37 | })
38 | .map((item) => {
39 | switch (item.type) {
40 | case 'collapse':
41 | return
42 | case 'item':
43 | return
44 | default:
45 | return (
46 |
47 | Menu Items Error
48 |
49 | )
50 | }
51 | })
52 |
53 | const Icon = menu.icon
54 | const menuIcon = menu.icon ? (
55 |
56 | ) : (
57 | 0 ? 'inherit' : 'medium'}
63 | />
64 | )
65 |
66 | return (
67 | <>
68 | 1 ? 'transparent !important' : 'inherit',
74 | py: level > 1 ? 1 : 1.25,
75 | pl: `${level * 24}px`
76 | }}
77 | // selected={selected === menu.id}
78 | onClick={handleClick}
79 | >
80 | {menuIcon}
81 |
84 | {menu.title}
85 |
86 | }
87 | secondary={
88 | menu.caption && (
89 |
90 | {menu.caption}
91 |
92 | )
93 | }
94 | />
95 | {open ? (
96 |
97 | ) : (
98 |
99 | )}
100 |
101 |
102 |
119 | {menus}
120 |
121 |
122 | >
123 | )
124 | }
125 |
126 | NavCollapse.propTypes = {
127 | menu: PropTypes.object,
128 | level: PropTypes.number
129 | }
130 |
131 | export default NavCollapse
132 |
--------------------------------------------------------------------------------
/src/layout/MainLayout/Sidebar/MenuList/NavItem/index.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { forwardRef, useEffect } from 'react'
3 | import { Link } from 'react-router-dom'
4 | import { useDispatch, useSelector } from 'react-redux'
5 |
6 | // material-ui
7 | import { useTheme } from '@mui/material/styles'
8 | import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography, useMediaQuery } from '@mui/material'
9 |
10 | // project imports
11 | import { MENU_OPEN, SET_MENU } from 'store/actions'
12 | import config from 'config'
13 |
14 | // assets
15 | import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'
16 |
17 | // ==============================|| SIDEBAR MENU LIST ITEMS ||============================== //
18 |
19 | const NavItem = ({ item, level, navType, onClick, onUploadFile }) => {
20 | const theme = useTheme()
21 | const dispatch = useDispatch()
22 | const customization = useSelector((state) => state.customization)
23 | const matchesSM = useMediaQuery(theme.breakpoints.down('lg'))
24 |
25 | const Icon = item.icon
26 | const itemIcon = item?.icon ? (
27 |
28 | ) : (
29 | id === item?.id) > -1 ? 8 : 6,
32 | height: customization.isOpen.findIndex((id) => id === item?.id) > -1 ? 8 : 6
33 | }}
34 | fontSize={level > 0 ? 'inherit' : 'medium'}
35 | />
36 | )
37 |
38 | let itemTarget = '_self'
39 | if (item.target) {
40 | itemTarget = '_blank'
41 | }
42 |
43 | let listItemProps = {
44 | component: forwardRef(function ListItemPropsComponent(props, ref) {
45 | return
46 | })
47 | }
48 | if (item?.external) {
49 | listItemProps = { component: 'a', href: item.url, target: itemTarget }
50 | }
51 | if (item?.id === 'loadChatflow') {
52 | listItemProps.component = 'label'
53 | }
54 |
55 | const handleFileUpload = (e) => {
56 | if (!e.target.files) return
57 |
58 | const file = e.target.files[0]
59 |
60 | const reader = new FileReader()
61 | reader.onload = (evt) => {
62 | if (!evt?.target?.result) {
63 | return
64 | }
65 | const { result } = evt.target
66 | onUploadFile(result)
67 | }
68 | reader.readAsText(file)
69 | }
70 |
71 | const itemHandler = (id) => {
72 | if (navType === 'SETTINGS' && id !== 'loadChatflow') {
73 | onClick(id)
74 | } else {
75 | dispatch({ type: MENU_OPEN, id })
76 | if (matchesSM) dispatch({ type: SET_MENU, opened: false })
77 | }
78 | }
79 |
80 | // active menu item on page load
81 | useEffect(() => {
82 | if (navType === 'MENU') {
83 | const currentIndex = document.location.pathname
84 | .toString()
85 | .split('/')
86 | .findIndex((id) => id === item.id)
87 | if (currentIndex > -1) {
88 | dispatch({ type: MENU_OPEN, id: item.id })
89 | }
90 | }
91 | }, [navType])
92 |
93 | return (
94 | 1 ? 'inherit' : 'inherit',
102 | py: level > 1 ? 1 : 1.25,
103 | pl: `${level * 24}px`
104 | }}
105 | selected={customization.isOpen.findIndex((id) => id === item.id) > -1}
106 | onClick={() => itemHandler(item.id)}
107 | >
108 | {itemIcon}
109 | id === item.id) > -1 ? 'h5' : 'body1'} color='inherit'>
112 | {item.title}
113 |
114 | }
115 | secondary={
116 | item.caption && (
117 |
118 | {item.caption}
119 |
120 | )
121 | }
122 | />
123 | {item.chip && (
124 | {item.chip.avatar}}
130 | />
131 | )}
132 |
133 | )
134 | }
135 |
136 | NavItem.propTypes = {
137 | item: PropTypes.object,
138 | level: PropTypes.number,
139 | navType: PropTypes.string,
140 | onClick: PropTypes.func,
141 | onUploadFile: PropTypes.func
142 | }
143 |
144 | export default NavItem
145 |
--------------------------------------------------------------------------------