Format code

This commit is contained in:
Nicola Benaglia 2025-04-20 09:40:44 +02:00
parent 515379ab71
commit 6dea8e2468
3 changed files with 243 additions and 236 deletions

View File

@ -1,51 +1,34 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from 'react';
import {
AppsNavBarLeft,
AppsNavBarParent,
AppsNavBarRight,
} from "./Apps-styles";
import NavBack from "../../assets/svgs/NavBack.svg";
import NavAdd from "../../assets/svgs/NavAdd.svg";
import NavMoreMenu from "../../assets/svgs/NavMoreMenu.svg";
import {
ButtonBase,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
Tab,
Tabs,
} from "@mui/material";
} from './Apps-styles';
import { NavBack } from '../../assets/svgs/NavBack.tsx';
import { NavAdd } from '../../assets/svgs/NavAdd.tsx';
import { ButtonBase, Tab, Tabs } from '@mui/material';
import {
executeEvent,
subscribeToEvent,
unsubscribeFromEvent,
} from "../../utils/events";
import TabComponent from "./TabComponent";
import PushPinIcon from "@mui/icons-material/PushPin";
import RefreshIcon from "@mui/icons-material/Refresh";
import { useRecoilState, useSetRecoilState } from "recoil";
import {
navigationControllerAtom,
settingsLocalLastUpdatedAtom,
sortablePinnedAppsAtom,
} from "../../atoms/global";
import { AppsDevModeTabComponent } from "./AppsDevModeTabComponent";
} from '../../utils/events';
import RefreshIcon from '@mui/icons-material/Refresh';
import { useRecoilState } from 'recoil';
import { navigationControllerAtom } from '../../atoms/global';
import { AppsDevModeTabComponent } from './AppsDevModeTabComponent';
export const AppsDevModeNavBar = () => {
const [tabs, setTabs] = useState([]);
const [selectedTab, setSelectedTab] = useState(null);
const [navigationController, setNavigationController] = useRecoilState(navigationControllerAtom)
const [navigationController, setNavigationController] = useRecoilState(
navigationControllerAtom
);
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
const tabsRef = useRef(null);
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
@ -57,27 +40,25 @@ export const AppsDevModeNavBar = () => {
useEffect(() => {
// Scroll to the last tab whenever the tabs array changes (e.g., when a new tab is added)
if (tabsRef.current) {
const tabElements = tabsRef.current.querySelectorAll(".MuiTab-root");
const tabElements = tabsRef.current.querySelectorAll('.MuiTab-root');
if (tabElements.length > 0) {
const lastTab = tabElements[tabElements.length - 1];
lastTab.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "end",
behavior: 'smooth',
block: 'nearest',
inline: 'end',
});
}
}
}, [tabs.length]); // Dependency on the number of tabs
const isDisableBackButton = useMemo(()=> {
if(selectedTab && navigationController[selectedTab?.tabId]?.hasBack) return false
if(selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) return true
return false
}, [navigationController, selectedTab])
const isDisableBackButton = useMemo(() => {
if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack)
return false;
if (selectedTab && !navigationController[selectedTab?.tabId]?.hasBack)
return true;
return false;
}, [navigationController, selectedTab]);
const setTabsToNav = (e) => {
const { tabs, selectedTab, isNewTabWindow } = e.detail?.data;
@ -88,45 +69,43 @@ export const AppsDevModeNavBar = () => {
};
useEffect(() => {
subscribeToEvent("appsDevModeSetTabsToNav", setTabsToNav);
subscribeToEvent('appsDevModeSetTabsToNav', setTabsToNav);
return () => {
unsubscribeFromEvent("appsDevModeSetTabsToNav", setTabsToNav);
unsubscribeFromEvent('appsDevModeSetTabsToNav', setTabsToNav);
};
}, []);
return (
<AppsNavBarParent
sx={{
position: "relative",
flexDirection: "column",
width: "60px",
height: "unset",
maxHeight: "70vh",
borderRadius: "0px 30px 30px 0px",
padding: "10px",
position: 'relative',
flexDirection: 'column',
width: '60px',
height: 'unset',
maxHeight: '70vh',
borderRadius: '0px 30px 30px 0px',
padding: '10px',
}}
>
<AppsNavBarLeft
sx={{
flexDirection: "column",
flexDirection: 'column',
}}
>
<ButtonBase
onClick={() => {
executeEvent("devModeNavigateBack", selectedTab?.tabId);
executeEvent('devModeNavigateBack', selectedTab?.tabId);
}}
disabled={isDisableBackButton}
sx={{
opacity: !isDisableBackButton ? 1 : 0.1,
cursor: !isDisableBackButton ? 'pointer': 'default'
cursor: !isDisableBackButton ? 'pointer' : 'default',
}}
>
<img src={NavBack} />
<NavBack />
</ButtonBase>
<Tabs
orientation="vertical"
ref={tabsRef}
@ -134,11 +113,11 @@ export const AppsDevModeNavBar = () => {
variant="scrollable" // Make tabs scrollable
scrollButtons={true}
sx={{
"& .MuiTabs-indicator": {
backgroundColor: "white",
'& .MuiTabs-indicator': {
backgroundColor: 'white',
},
maxHeight: `320px`, // Ensure the tabs container fits within the available space
overflow: "hidden", // Prevents overflow on small screens
overflow: 'hidden', // Prevents overflow on small screens
}}
>
{tabs?.map((tab) => (
@ -153,65 +132,61 @@ export const AppsDevModeNavBar = () => {
/>
} // Pass custom component
sx={{
"&.Mui-selected": {
color: "white",
'&.Mui-selected': {
color: 'white',
},
padding: "0px",
margin: "0px",
minWidth: "0px",
width: "50px",
padding: '0px',
margin: '0px',
minWidth: '0px',
width: '50px',
}}
/>
))}
</Tabs>
</AppsNavBarLeft>
{selectedTab && (
<AppsNavBarRight
sx={{
gap: "10px",
flexDirection: "column",
}}
>
<ButtonBase
onClick={() => {
setSelectedTab(null);
executeEvent("devModeNewTabWindow", {});
sx={{
gap: '10px',
flexDirection: 'column',
}}
>
<img
style={{
height: "40px",
width: "40px",
<ButtonBase
onClick={() => {
setSelectedTab(null);
executeEvent('devModeNewTabWindow', {});
}}
src={NavAdd}
/>
</ButtonBase>
<ButtonBase
onClick={(e) => {
if(selectedTab?.refreshFunc){
selectedTab.refreshFunc(selectedTab?.tabId)
} else {
executeEvent("refreshApp", {
tabId: selectedTab?.tabId,
});
}
}}
>
<RefreshIcon
sx={{
color: "rgba(250, 250, 250, 0.5)",
>
<NavAdd
style={{
height: '40px',
width: '40px',
height: 'auto'
}}
/>
</ButtonBase>
</AppsNavBarRight>
</ButtonBase>
<ButtonBase
onClick={(e) => {
if (selectedTab?.refreshFunc) {
selectedTab.refreshFunc(selectedTab?.tabId);
} else {
executeEvent('refreshApp', {
tabId: selectedTab?.tabId,
});
}
}}
>
<RefreshIcon
sx={{
color: 'rgba(250, 250, 250, 0.5)',
width: '40px',
height: 'auto',
}}
/>
</ButtonBase>
</AppsNavBarRight>
)}
</AppsNavBarParent>
);
};

View File

@ -1,11 +1,4 @@
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
@ -13,7 +6,6 @@ import {
AppLibrarySubTitle,
AppsContainer,
AppsLibraryContainer,
AppsParent,
AppsSearchContainer,
AppsSearchLeft,
AppsSearchRight,
@ -24,16 +16,22 @@ import {
PublishQAppCTARight,
PublishQAppDotsBG,
} from './Apps-styles';
import { Avatar, Box, ButtonBase, InputBase, styled } from '@mui/material';
import { Add } from '@mui/icons-material';
import {
Avatar,
Box,
ButtonBase,
InputBase,
styled,
useTheme,
} from '@mui/material';
import { MyContext, getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import IconSearch from '../../assets/svgs/Search.svg';
import IconClearInput from '../../assets/svgs/ClearInput.svg';
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
import qappDots from '../../assets/svgs/qappDots.svg';
// import { Return } from './assets/svgs/Return.tsx';
import ReturnSVG from '../../assets/svgs/Return.svg';
import { Spacer } from '../../common/Spacer';
import { AppInfoSnippet } from './AppInfoSnippet';
import { Virtuoso } from 'react-virtuoso';
@ -43,6 +41,7 @@ import {
MailIconImg,
ShowMessageReturnButton,
} from '../Group/Forum/Mail-styles';
const officialAppList = [
'q-tube',
'q-blog',
@ -59,8 +58,6 @@ const officialAppList = [
'q-nodecontrol',
];
// TODO: apply dark/light style
const ScrollerStyled = styled('div')({
// Hide scrollbar for WebKit browsers (Chrome, Safari)
'::-webkit-scrollbar': {
@ -76,10 +73,10 @@ const ScrollerStyled = styled('div')({
});
const StyledVirtuosoContainer = styled('div')({
position: 'relative',
width: '100%',
display: 'flex',
flexDirection: 'column',
position: 'relative',
width: '100%',
// Hide scrollbar for WebKit browsers (Chrome, Safari)
'::-webkit-scrollbar': {
@ -148,6 +145,8 @@ export const AppsLibrary = ({
);
};
const theme = useTheme();
return (
<AppsLibraryContainer
sx={{
@ -165,6 +164,7 @@ export const AppsLibrary = ({
<AppsSearchContainer>
<AppsSearchLeft>
<img src={IconSearch} />
<InputBase
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
@ -191,7 +191,9 @@ export const AppsLibrary = ({
</AppsSearchContainer>
</Box>
</AppsWidthLimiter>
<Spacer height="25px" />
<ShowMessageReturnButton
sx={{
padding: '2px',
@ -200,10 +202,12 @@ export const AppsLibrary = ({
executeEvent('navigateBack', {});
}}
>
<MailIconImg src={ReturnSVG} />
<MailIconImg src={ReturnSVG} /> // TODO return icon
<ComposeP>Return to Apps Dashboard</ComposeP>
</ShowMessageReturnButton>
<Spacer height="25px" />
{searchedList?.length > 0 ? (
<AppsWidthLimiter>
<StyledVirtuosoContainer
@ -227,7 +231,9 @@ export const AppsLibrary = ({
<>
<AppsWidthLimiter>
<AppLibrarySubTitle>Official Apps</AppLibrarySubTitle>
<Spacer height="18px" />
<AppsContainer>
{officialApps?.map((qapp) => {
return (
@ -271,6 +277,7 @@ export const AppsLibrary = ({
/>
</Avatar>
</AppCircle>
<AppCircleLabel>
{qapp?.metadata?.title || qapp?.name}
</AppCircleLabel>
@ -279,20 +286,27 @@ export const AppsLibrary = ({
);
})}
</AppsContainer>
<Spacer height="30px" />
<AppLibrarySubTitle>
{hasPublishApp ? 'Update Apps!' : 'Create Apps!'}
</AppLibrarySubTitle>
<Spacer height="18px" />
</AppsWidthLimiter>
<PublishQAppCTAParent>
<PublishQAppCTALeft>
<PublishQAppDotsBG>
<img src={qappDots} />
</PublishQAppDotsBG>
<Spacer width="29px" />
<img src={qappDevelopText} />
</PublishQAppCTALeft>
<PublishQAppCTARight
onClick={() => {
setMode('publish');
@ -301,13 +315,18 @@ export const AppsLibrary = ({
<PublishQAppCTAButton>
{hasPublishApp ? 'Update' : 'Publish'}
</PublishQAppCTAButton>
<Spacer width="20px" />
</PublishQAppCTARight>
</PublishQAppCTAParent>
<AppsWidthLimiter>
<Spacer height="18px" />
<AppLibrarySubTitle>Categories</AppLibrarySubTitle>
<Spacer height="18px" />
<AppsWidthLimiter
sx={{
flexDirection: 'row',
@ -338,18 +357,17 @@ export const AppsLibrary = ({
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '110px',
width: '110px',
background:
'linear-gradient(163.47deg, #4BBCFE 27.55%, #1386C9 86.56%)',
color: '#1D1D1E',
fontWeight: 700,
fontSize: '16px',
flexShrink: 0,
background: theme.palette.background.default,
borderRadius: '11px',
color: theme.palette.text.primary,
display: 'flex',
flexShrink: 0,
fontSize: '16px',
fontWeight: 700,
height: '110px',
justifyContent: 'center',
width: '110px',
}}
>
{category?.name}

View File

@ -1,12 +1,12 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from 'react';
import {
AppsNavBarLeft,
AppsNavBarParent,
AppsNavBarRight,
} from "./Apps-styles";
import NavBack from "../../assets/svgs/NavBack.svg";
import NavAdd from "../../assets/svgs/NavAdd.svg";
import NavMoreMenu from "../../assets/svgs/NavMoreMenu.svg";
} from './Apps-styles';
import { NavBack } from '../../assets/svgs/NavBack.tsx';
import { NavAdd } from '../../assets/svgs/NavAdd.tsx';
import { NavMoreMenu } from '../../assets/svgs/NavMoreMenu.tsx';
import {
ButtonBase,
ListItemIcon,
@ -15,27 +15,33 @@ import {
MenuItem,
Tab,
Tabs,
} from "@mui/material";
} from '@mui/material';
import {
executeEvent,
subscribeToEvent,
unsubscribeFromEvent,
} from "../../utils/events";
import TabComponent from "./TabComponent";
import PushPinIcon from "@mui/icons-material/PushPin";
import RefreshIcon from "@mui/icons-material/Refresh";
import { useRecoilState, useSetRecoilState } from "recoil";
} from '../../utils/events';
import TabComponent from './TabComponent';
import PushPinIcon from '@mui/icons-material/PushPin';
import RefreshIcon from '@mui/icons-material/Refresh';
import { useRecoilState, useSetRecoilState } from 'recoil';
import {
navigationControllerAtom,
settingsLocalLastUpdatedAtom,
sortablePinnedAppsAtom,
} from "../../atoms/global";
} from '../../atoms/global';
export function saveToLocalStorage(key, subKey, newValue, otherRootData = {}, deleteWholeKey) {
export function saveToLocalStorage(
key,
subKey,
newValue,
otherRootData = {},
deleteWholeKey
) {
try {
if(deleteWholeKey){
if (deleteWholeKey) {
localStorage.setItem(key, null);
return
return;
}
// Fetch existing data
const existingData = localStorage.getItem(key);
@ -64,7 +70,7 @@ export function saveToLocalStorage(key, subKey, newValue, otherRootData = {}, de
const serializedValue = JSON.stringify(combinedData);
localStorage.setItem(key, serializedValue);
} catch (error) {
console.error("Error saving to localStorage:", error);
console.error('Error saving to localStorage:', error);
}
}
@ -78,13 +84,17 @@ export const AppsNavBar = () => {
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
sortablePinnedAppsAtom
);
const [navigationController, setNavigationController] = useRecoilState(navigationControllerAtom)
const [navigationController, setNavigationController] = useRecoilState(
navigationControllerAtom
);
const isDisableBackButton = useMemo(()=> {
if(selectedTab && navigationController[selectedTab?.tabId]?.hasBack) return false
if(selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) return true
return false
}, [navigationController, selectedTab])
const isDisableBackButton = useMemo(() => {
if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack)
return false;
if (selectedTab && !navigationController[selectedTab?.tabId]?.hasBack)
return true;
return false;
}, [navigationController, selectedTab]);
const setSettingsLocalLastUpdated = useSetRecoilState(
settingsLocalLastUpdatedAtom
@ -101,13 +111,13 @@ export const AppsNavBar = () => {
useEffect(() => {
// Scroll to the last tab whenever the tabs array changes (e.g., when a new tab is added)
if (tabsRef.current) {
const tabElements = tabsRef.current.querySelectorAll(".MuiTab-root");
const tabElements = tabsRef.current.querySelectorAll('.MuiTab-root');
if (tabElements.length > 0) {
const lastTab = tabElements[tabElements.length - 1];
lastTab.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "end",
behavior: 'smooth',
block: 'nearest',
inline: 'end',
});
}
}
@ -122,10 +132,10 @@ export const AppsNavBar = () => {
};
useEffect(() => {
subscribeToEvent("setTabsToNav", setTabsToNav);
subscribeToEvent('setTabsToNav', setTabsToNav);
return () => {
unsubscribeFromEvent("setTabsToNav", setTabsToNav);
unsubscribeFromEvent('setTabsToNav', setTabsToNav);
};
}, []);
@ -137,28 +147,29 @@ export const AppsNavBar = () => {
<AppsNavBarParent>
<AppsNavBarLeft>
<ButtonBase
onClick={() => {
executeEvent("navigateBack", selectedTab?.tabId);
onClick={() => {
executeEvent('navigateBack', selectedTab?.tabId);
}}
disabled={isDisableBackButton}
sx={{
opacity: !isDisableBackButton ? 1 : 0.1,
cursor: !isDisableBackButton ? 'pointer': 'default'
opacity: !isDisableBackButton ? 1 : 0.3,
cursor: !isDisableBackButton ? 'pointer' : 'default',
}}
>
<img src={NavBack} />
<NavBack />
</ButtonBase>
<Tabs
ref={tabsRef}
aria-label="basic tabs example"
variant="scrollable" // Make tabs scrollable
scrollButtons={false}
sx={{
"& .MuiTabs-indicator": {
backgroundColor: "white",
'& .MuiTabs-indicator': {
backgroundColor: 'white',
},
maxWidth: `calc(100vw - 150px)`, // Ensure the tabs container fits within the available space
overflow: "hidden", // Prevents overflow on small screens
overflow: 'hidden', // Prevents overflow on small screens
}}
>
{tabs?.map((tab) => (
@ -173,83 +184,83 @@ export const AppsNavBar = () => {
/>
} // Pass custom component
sx={{
"&.Mui-selected": {
color: "white",
'&.Mui-selected': {
color: 'white',
},
padding: "0px",
margin: "0px",
minWidth: "0px",
width: "50px",
padding: '0px',
margin: '0px',
minWidth: '0px',
width: '50px',
}}
/>
))}
</Tabs>
</AppsNavBarLeft>
{selectedTab && (
<AppsNavBarRight
sx={{
gap: "10px",
}}
>
<ButtonBase
onClick={() => {
setSelectedTab(null);
executeEvent("newTabWindow", {});
}}
>
<img
style={{
height: "40px",
width: "40px",
}}
src={NavAdd}
/>
</ButtonBase>
<ButtonBase
onClick={(e) => {
if (!selectedTab) return;
handleClick(e);
}}
>
<img
style={{
height: "34px",
width: "34px",
}}
src={NavMoreMenu}
/>
</ButtonBase>
</AppsNavBarRight>
<AppsNavBarRight
sx={{
gap: '10px',
}}
>
<ButtonBase
onClick={() => {
setSelectedTab(null);
executeEvent('newTabWindow', {});
}}
>
<NavAdd
style={{
height: '40px',
width: '40px',
}}
/>
</ButtonBase>
<ButtonBase
onClick={(e) => {
if (!selectedTab) return;
handleClick(e);
}}
>
<NavMoreMenu
style={{
height: '34px',
width: '34px',
}}
/>
</ButtonBase>
</AppsNavBarRight>
)}
<Menu
id="navbar-more-mobile"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": "basic-button",
'aria-labelledby': 'basic-button',
}}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
vertical: 'top',
horizontal: 'center',
}}
slotProps={{
paper: {
sx: {
backgroundColor: "var(--bg-primary)",
color: "#fff",
width: "148px",
borderRadius: "5px",
backgroundColor: 'var(--bg-primary)',
color: '#fff',
width: '148px',
borderRadius: '5px',
},
},
}}
sx={{
marginTop: "10px",
marginTop: '10px',
}}
>
<MenuItem
@ -280,8 +291,8 @@ export const AppsNavBar = () => {
}
saveToLocalStorage(
"ext_saved_settings",
"sortablePinnedApps",
'ext_saved_settings',
'sortablePinnedApps',
updatedApps
);
return updatedApps;
@ -293,31 +304,33 @@ export const AppsNavBar = () => {
>
<ListItemIcon
sx={{
minWidth: "24px !important",
marginRight: "5px",
minWidth: '24px !important',
marginRight: '5px',
}}
>
<PushPinIcon
height={20}
sx={{
color: isSelectedAppPinned ? "red" : "rgba(250, 250, 250, 0.5)",
color: isSelectedAppPinned ? 'red' : 'rgba(250, 250, 250, 0.5)',
}}
/>
</ListItemIcon>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "12px",
'& .MuiTypography-root': {
fontSize: '12px',
fontWeight: 600,
color: isSelectedAppPinned ? "red" : "rgba(250, 250, 250, 0.5)",
color: isSelectedAppPinned ? 'red' : 'rgba(250, 250, 250, 0.5)',
},
}}
primary={`${isSelectedAppPinned ? "Unpin app" : "Pin app"}`}
primary={`${isSelectedAppPinned ? 'Unpin app' : 'Pin app'}`}
/>
</MenuItem>
<MenuItem
onClick={() => {
executeEvent("refreshApp", {
executeEvent('refreshApp', {
tabId: selectedTab?.tabId,
});
handleClose();
@ -325,23 +338,24 @@ export const AppsNavBar = () => {
>
<ListItemIcon
sx={{
minWidth: "24px !important",
marginRight: "5px",
minWidth: '24px !important',
marginRight: '5px',
}}
>
<RefreshIcon
height={20}
sx={{
color: "rgba(250, 250, 250, 0.5)",
color: 'rgba(250, 250, 250, 0.5)',
}}
/>
</ListItemIcon>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "12px",
'& .MuiTypography-root': {
fontSize: '12px',
fontWeight: 600,
color: "rgba(250, 250, 250, 0.5)",
color: 'rgba(250, 250, 250, 0.5)',
},
}}
primary="Refresh"