From 54c209dff3ed1ccef6c6a27648b742cd65ac7231 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 23 Apr 2026 20:27:22 +0200 Subject: [PATCH 1/4] remapped to ctrl+shift+f --- src/layouts/top-nav.js | 573 +++++++++++++++++++++-------------------- 1 file changed, 288 insertions(+), 285 deletions(-) diff --git a/src/layouts/top-nav.js b/src/layouts/top-nav.js index 4fe373bb2623..41d5f07e0f2f 100644 --- a/src/layouts/top-nav.js +++ b/src/layouts/top-nav.js @@ -1,21 +1,21 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import NextLink from "next/link"; -import PropTypes from "prop-types"; -import Bars3Icon from "@heroicons/react/24/outline/Bars3Icon"; -import MagnifyingGlassIcon from "@heroicons/react/24/outline/MagnifyingGlassIcon"; -import MoonIcon from "@heroicons/react/24/outline/MoonIcon"; -import SunIcon from "@heroicons/react/24/outline/SunIcon"; -import BookmarkIcon from "@mui/icons-material/Bookmark"; -import TravelExploreIcon from "@mui/icons-material/TravelExplore"; -import DragIndicatorIcon from "@mui/icons-material/DragIndicator"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import CloseIcon from "@mui/icons-material/Close"; -import SwapVertIcon from "@mui/icons-material/SwapVert"; -import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; -import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; -import LockIcon from "@mui/icons-material/Lock"; -import LockOpenIcon from "@mui/icons-material/LockOpen"; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import NextLink from 'next/link' +import PropTypes from 'prop-types' +import Bars3Icon from '@heroicons/react/24/outline/Bars3Icon' +import MagnifyingGlassIcon from '@heroicons/react/24/outline/MagnifyingGlassIcon' +import MoonIcon from '@heroicons/react/24/outline/MoonIcon' +import SunIcon from '@heroicons/react/24/outline/SunIcon' +import BookmarkIcon from '@mui/icons-material/Bookmark' +import TravelExploreIcon from '@mui/icons-material/TravelExplore' +import DragIndicatorIcon from '@mui/icons-material/DragIndicator' +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' +import CloseIcon from '@mui/icons-material/Close' +import SwapVertIcon from '@mui/icons-material/SwapVert' +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward' +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward' +import LockIcon from '@mui/icons-material/Lock' +import LockOpenIcon from '@mui/icons-material/LockOpen' import { Box, Divider, @@ -31,212 +31,215 @@ import { ListItem, ListItemText, Typography, -} from "@mui/material"; -import { Logo } from "../components/logo"; -import { useSettings } from "../hooks/use-settings"; -import { useUserBookmarks } from "../hooks/use-user-bookmarks"; -import { paths } from "../paths"; -import { AccountPopover } from "./account-popover"; -import { CippTenantSelector } from "../components/CippComponents/CippTenantSelector"; -import { NotificationsPopover } from "./notifications-popover"; -import { useDialog } from "../hooks/use-dialog"; -import { CippUniversalSearchV2 } from "../components/CippCards/CippUniversalSearchV2"; +} from '@mui/material' +import { Logo } from '../components/logo' +import { useSettings } from '../hooks/use-settings' +import { useUserBookmarks } from '../hooks/use-user-bookmarks' +import { paths } from '../paths' +import { AccountPopover } from './account-popover' +import { CippTenantSelector } from '../components/CippComponents/CippTenantSelector' +import { NotificationsPopover } from './notifications-popover' +import { useDialog } from '../hooks/use-dialog' +import { CippUniversalSearchV2 } from '../components/CippCards/CippUniversalSearchV2' -const TOP_NAV_HEIGHT = 64; +const TOP_NAV_HEIGHT = 64 export const TopNav = (props) => { - const universalSearchDialog = useDialog(); - const { onNavOpen } = props; - const settings = useSettings(); - const { bookmarks, setBookmarks } = useUserBookmarks(); - const mdDown = useMediaQuery((theme) => theme.breakpoints.down("md")); - const showPopoverBookmarks = settings.bookmarkPopover === true; - const reorderMode = settings.bookmarkReorderMode || "arrows"; - const locked = settings.bookmarkLocked ?? false; + const universalSearchDialog = useDialog() + const { onNavOpen } = props + const settings = useSettings() + const { bookmarks, setBookmarks } = useUserBookmarks() + const mdDown = useMediaQuery((theme) => theme.breakpoints.down('md')) + const showPopoverBookmarks = settings.bookmarkPopover === true + const reorderMode = settings.bookmarkReorderMode || 'arrows' + const locked = settings.bookmarkLocked ?? false const handleThemeSwitch = useCallback(() => { - const themeName = settings.currentTheme?.value === "light" ? "dark" : "light"; + const themeName = settings.currentTheme?.value === 'light' ? 'dark' : 'light' settings.handleUpdate({ currentTheme: { value: themeName, label: themeName }, paletteMode: themeName, - }); - }, [settings]); + }) + }, [settings]) - const [anchorEl, setAnchorEl] = useState(null); - const [sortOrder, setSortOrder] = useState(settings.bookmarkSortOrder || "custom"); - const [dragIndex, setDragIndex] = useState(null); - const [dragOverIndex, setDragOverIndex] = useState(null); - const [animatingPair, setAnimatingPair] = useState(null); - const [flashSort, setFlashSort] = useState(false); - const [flashLock, setFlashLock] = useState(false); - const [universalSearchKey, setUniversalSearchKey] = useState(0); - const [universalSearchDefaultType, setUniversalSearchDefaultType] = useState("Users"); - const itemRefs = useRef({}); - const touchDragRef = useRef({ startIdx: null, overIdx: null }); - const tenantSelectorRef = useRef(null); + const [anchorEl, setAnchorEl] = useState(null) + const [sortOrder, setSortOrder] = useState(settings.bookmarkSortOrder || 'custom') + const [dragIndex, setDragIndex] = useState(null) + const [dragOverIndex, setDragOverIndex] = useState(null) + const [animatingPair, setAnimatingPair] = useState(null) + const [flashSort, setFlashSort] = useState(false) + const [flashLock, setFlashLock] = useState(false) + const [universalSearchKey, setUniversalSearchKey] = useState(0) + const [universalSearchDefaultType, setUniversalSearchDefaultType] = useState('Users') + const itemRefs = useRef({}) + const touchDragRef = useRef({ startIdx: null, overIdx: null }) + const tenantSelectorRef = useRef(null) const handleBookmarkClick = (event) => { - setAnchorEl(event.currentTarget); - }; + setAnchorEl(event.currentTarget) + } const handleBookmarkClose = () => { - setAnchorEl(null); - }; + setAnchorEl(null) + } const handleDragStart = (index) => { - setDragIndex(index); - }; + setDragIndex(index) + } const handleDragOver = (e, index) => { - e.preventDefault(); - setDragOverIndex(index); - }; + e.preventDefault() + setDragOverIndex(index) + } const handleDrop = (e, dropIndex) => { - e.preventDefault(); + e.preventDefault() if (dragIndex === null || dragIndex === dropIndex) { - setDragIndex(null); - setDragOverIndex(null); - return; + setDragIndex(null) + setDragOverIndex(null) + return } - const items = [...bookmarks]; - const [reordered] = items.splice(dragIndex, 1); - items.splice(dropIndex, 0, reordered); - setBookmarks(items); - setDragIndex(null); - setDragOverIndex(null); - }; + const items = [...bookmarks] + const [reordered] = items.splice(dragIndex, 1) + items.splice(dropIndex, 0, reordered) + setBookmarks(items) + setDragIndex(null) + setDragOverIndex(null) + } const handleDragEnd = () => { - setDragIndex(null); - setDragOverIndex(null); - }; + setDragIndex(null) + setDragOverIndex(null) + } const moveBookmarkUp = (index) => { - if (index <= 0) return; - const updatedBookmarks = [...bookmarks]; - const temp = updatedBookmarks[index]; - updatedBookmarks[index] = updatedBookmarks[index - 1]; - updatedBookmarks[index - 1] = temp; - setBookmarks(updatedBookmarks); - }; + if (index <= 0) return + const updatedBookmarks = [...bookmarks] + const temp = updatedBookmarks[index] + updatedBookmarks[index] = updatedBookmarks[index - 1] + updatedBookmarks[index - 1] = temp + setBookmarks(updatedBookmarks) + } const moveBookmarkDown = (index) => { - if (index >= bookmarks.length - 1) return; - const updatedBookmarks = [...bookmarks]; - const temp = updatedBookmarks[index]; - updatedBookmarks[index] = updatedBookmarks[index + 1]; - updatedBookmarks[index + 1] = temp; - setBookmarks(updatedBookmarks); - }; + if (index >= bookmarks.length - 1) return + const updatedBookmarks = [...bookmarks] + const temp = updatedBookmarks[index] + updatedBookmarks[index] = updatedBookmarks[index + 1] + updatedBookmarks[index + 1] = temp + setBookmarks(updatedBookmarks) + } const removeBookmark = (path) => { - const updatedBookmarks = [...bookmarks]; - const origIdx = updatedBookmarks.findIndex((b) => b.path === path); + const updatedBookmarks = [...bookmarks] + const origIdx = updatedBookmarks.findIndex((b) => b.path === path) if (origIdx !== -1) { - updatedBookmarks.splice(origIdx, 1); - setBookmarks(updatedBookmarks); + updatedBookmarks.splice(origIdx, 1) + setBookmarks(updatedBookmarks) } - }; + } const triggerSortFlash = () => { - setFlashSort(true); - setTimeout(() => setFlashSort(false), 600); - }; + setFlashSort(true) + setTimeout(() => setFlashSort(false), 600) + } const triggerLockFlash = () => { - setFlashLock(true); - setTimeout(() => setFlashLock(false), 600); - }; + setFlashLock(true) + setTimeout(() => setFlashLock(false), 600) + } const handleToggleLock = () => { - settings.handleUpdate({ bookmarkLocked: !locked }); - }; + settings.handleUpdate({ bookmarkLocked: !locked }) + } const animatedMoveUp = (index) => { - if (index <= 0 || animatingPair) return; - const el1 = itemRefs.current[index]; - const el2 = itemRefs.current[index - 1]; + if (index <= 0 || animatingPair) return + const el1 = itemRefs.current[index] + const el2 = itemRefs.current[index - 1] if (!el1 || !el2) { - moveBookmarkUp(index); - return; + moveBookmarkUp(index) + return } - const distance = el1.getBoundingClientRect().top - el2.getBoundingClientRect().top; - setAnimatingPair({ idx1: index, idx2: index - 1, offset1: -distance, offset2: distance }); + const distance = el1.getBoundingClientRect().top - el2.getBoundingClientRect().top + setAnimatingPair({ idx1: index, idx2: index - 1, offset1: -distance, offset2: distance }) setTimeout(() => { - moveBookmarkUp(index); - setAnimatingPair(null); - }, 250); - }; + moveBookmarkUp(index) + setAnimatingPair(null) + }, 250) + } const animatedMoveDown = (index) => { - if (index >= bookmarks.length - 1 || animatingPair) return; - const el1 = itemRefs.current[index]; - const el2 = itemRefs.current[index + 1]; + if (index >= bookmarks.length - 1 || animatingPair) return + const el1 = itemRefs.current[index] + const el2 = itemRefs.current[index + 1] if (!el1 || !el2) { - moveBookmarkDown(index); - return; + moveBookmarkDown(index) + return } - const distance = el2.getBoundingClientRect().top - el1.getBoundingClientRect().top; - setAnimatingPair({ idx1: index, idx2: index + 1, offset1: distance, offset2: -distance }); + const distance = el2.getBoundingClientRect().top - el1.getBoundingClientRect().top + setAnimatingPair({ idx1: index, idx2: index + 1, offset1: distance, offset2: -distance }) setTimeout(() => { - moveBookmarkDown(index); - setAnimatingPair(null); - }, 250); - }; + moveBookmarkDown(index) + setAnimatingPair(null) + }, 250) + } const handleSortCycle = () => { - const next = sortOrder === "custom" ? "asc" : sortOrder === "asc" ? "desc" : "custom"; - setSortOrder(next); - settings.handleUpdate({ bookmarkSortOrder: next }); - }; + const next = sortOrder === 'custom' ? 'asc' : sortOrder === 'asc' ? 'desc' : 'custom' + setSortOrder(next) + settings.handleUpdate({ bookmarkSortOrder: next }) + } const displayBookmarks = useMemo(() => { - if (sortOrder === "custom") return bookmarks; + if (sortOrder === 'custom') return bookmarks return [...bookmarks].sort((a, b) => { - const cmp = (a.label || "").localeCompare(b.label || ""); - return sortOrder === "asc" ? cmp : -cmp; - }); - }, [bookmarks, sortOrder]); - const popoverOpen = Boolean(anchorEl); - const popoverId = popoverOpen ? "bookmark-popover" : undefined; + const cmp = (a.label || '').localeCompare(b.label || '') + return sortOrder === 'asc' ? cmp : -cmp + }) + }, [bookmarks, sortOrder]) + const popoverOpen = Boolean(anchorEl) + const popoverId = popoverOpen ? 'bookmark-popover' : undefined - const openUniversalSearch = useCallback((defaultType = "Users") => { - setUniversalSearchDefaultType(defaultType); - universalSearchDialog.handleOpen(); - }, [universalSearchDialog.handleOpen]); + const openUniversalSearch = useCallback( + (defaultType = 'Users') => { + setUniversalSearchDefaultType(defaultType) + universalSearchDialog.handleOpen() + }, + [universalSearchDialog.handleOpen] + ) const closeUniversalSearch = useCallback(() => { - universalSearchDialog.handleClose(); - setUniversalSearchKey((prev) => prev + 1); - }, [universalSearchDialog.handleClose]); + universalSearchDialog.handleClose() + setUniversalSearchKey((prev) => prev + 1) + }, [universalSearchDialog.handleClose]) useEffect(() => { const handleKeyDown = (event) => { - if ((event.metaKey || event.ctrlKey) && event.altKey && event.key === "k") { - event.preventDefault(); - tenantSelectorRef.current?.focus(); - } else if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === "K") { - event.preventDefault(); - openUniversalSearch("Users"); - } else if ((event.metaKey || event.ctrlKey) && event.key === "k") { - event.preventDefault(); - openUniversalSearch("Pages"); + if ((event.metaKey || event.ctrlKey) && event.altKey && event.key === 'k') { + event.preventDefault() + tenantSelectorRef.current?.focus() + } else if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'F') { + event.preventDefault() + openUniversalSearch('Users') + } else if ((event.metaKey || event.ctrlKey) && event.key === 'k') { + event.preventDefault() + openUniversalSearch('Pages') } - }; - window.addEventListener("keydown", handleKeyDown); + } + window.addEventListener('keydown', handleKeyDown) return () => { - window.removeEventListener("keydown", handleKeyDown); - }; - }, [openUniversalSearch]); + window.removeEventListener('keydown', handleKeyDown) + } + }, [openUniversalSearch]) return ( theme.zIndex.appBar, }} > @@ -257,7 +260,7 @@ export const TopNav = (props) => { @@ -267,7 +270,7 @@ export const TopNav = (props) => { component={NextLink} href={paths.index} sx={{ - display: "inline-flex", + display: 'inline-flex', height: 24, width: 24, }} @@ -289,8 +292,8 @@ export const TopNav = (props) => { {!mdDown && ( openUniversalSearch("Users")} - title="Open Universal Search (Ctrl/Cmd+Shift+K)" + onClick={() => openUniversalSearch('Users')} + title="Open Universal Search (Ctrl/Cmd+Shift+F)" > @@ -298,14 +301,14 @@ export const TopNav = (props) => { {!mdDown && ( - {settings?.currentTheme?.value === "dark" ? : } + {settings?.currentTheme?.value === 'dark' ? : } )} {!mdDown && ( openUniversalSearch("Pages")} + onClick={() => openUniversalSearch('Pages')} title="Open Page Search (Ctrl/Cmd+K)" > @@ -327,33 +330,33 @@ export const TopNav = (props) => { onClose={handleBookmarkClose} disableScrollLock anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} > - + {locked ? : } @@ -361,39 +364,39 @@ export const TopNav = (props) => { size="small" onClick={handleSortCycle} sx={{ - color: sortOrder === "custom" ? "neutral.500" : "primary.main", + color: sortOrder === 'custom' ? 'neutral.500' : 'primary.main', ...(flashSort && { - animation: "sortFlash 600ms ease-in-out", - "@keyframes sortFlash": { - "0%": { transform: "scale(1)" }, - "25%": { transform: "scale(1.5)", color: "#ff9800" }, - "50%": { transform: "scale(1)" }, - "75%": { transform: "scale(1.3)", color: "#ff9800" }, - "100%": { transform: "scale(1)" }, + animation: 'sortFlash 600ms ease-in-out', + '@keyframes sortFlash': { + '0%': { transform: 'scale(1)' }, + '25%': { transform: 'scale(1.5)', color: '#ff9800' }, + '50%': { transform: 'scale(1)' }, + '75%': { transform: 'scale(1.3)', color: '#ff9800' }, + '100%': { transform: 'scale(1)' }, }, }), }} title={ - sortOrder === "custom" - ? "Custom order" - : sortOrder === "asc" - ? "A > Z" - : "Z > A" + sortOrder === 'custom' + ? 'Custom order' + : sortOrder === 'asc' + ? 'A > Z' + : 'Z > A' } > - {sortOrder === "custom" && } - {sortOrder === "asc" && } - {sortOrder === "desc" && } + {sortOrder === 'custom' && } + {sortOrder === 'asc' && } + {sortOrder === 'desc' && } - {sortOrder === "custom" - ? "Custom order" - : sortOrder === "asc" - ? "A > Z" - : "Z > A"} + {sortOrder === 'custom' + ? 'Custom order' + : sortOrder === 'asc' + ? 'A > Z' + : 'Z > A'} @@ -408,27 +411,27 @@ export const TopNav = (props) => { { - itemRefs.current[idx] = el; + itemRefs.current[idx] = el }} data-bookmark-index={idx} - draggable={reorderMode === "drag" && sortOrder === "custom" && !locked} - {...(reorderMode === "drag" + draggable={reorderMode === 'drag' && sortOrder === 'custom' && !locked} + {...(reorderMode === 'drag' ? { onDragStart: (e) => { if (locked) { - e.preventDefault(); - triggerLockFlash(); - return; + e.preventDefault() + triggerLockFlash() + return } - if (sortOrder !== "custom") { - e.preventDefault(); - triggerSortFlash(); - return; + if (sortOrder !== 'custom') { + e.preventDefault() + triggerSortFlash() + return } - handleDragStart(idx); + handleDragStart(idx) }, onDragEnd: handleDragEnd, - ...(sortOrder === "custom" && !locked + ...(sortOrder === 'custom' && !locked ? { onDragOver: (e) => handleDragOver(e, idx), onDrop: (e) => handleDrop(e, idx), @@ -437,77 +440,77 @@ export const TopNav = (props) => { } : {})} sx={{ - color: "inherit", - display: "flex", - justifyContent: "space-between", - "&:hover .bookmark-controls": { + color: 'inherit', + display: 'flex', + justifyContent: 'space-between', + '&:hover .bookmark-controls': { opacity: 1, }, - ...(sortOrder === "custom" && - reorderMode === "drag" && + ...(sortOrder === 'custom' && + reorderMode === 'drag' && dragIndex === idx && { opacity: 0.4, }), - ...(sortOrder === "custom" && - reorderMode === "drag" && + ...(sortOrder === 'custom' && + reorderMode === 'drag' && dragOverIndex === idx && dragIndex !== idx && { - borderTop: "2px solid", - borderColor: "primary.main", + borderTop: '2px solid', + borderColor: 'primary.main', }), ...(animatingPair && (animatingPair.idx1 === idx || animatingPair.idx2 === idx) && { transform: `translateY(${animatingPair.idx1 === idx ? animatingPair.offset1 : animatingPair.offset2}px)`, - transition: "transform 250ms ease-in-out", - position: "relative", + transition: 'transform 250ms ease-in-out', + position: 'relative', zIndex: animatingPair.idx1 === idx ? 1 : 0, }), }} > - {reorderMode === "drag" && !locked && ( + {reorderMode === 'drag' && !locked && ( { - if (sortOrder !== "custom") { - triggerSortFlash(); - return; + if (sortOrder !== 'custom') { + triggerSortFlash() + return } - touchDragRef.current.startIdx = idx; - setDragIndex(idx); + touchDragRef.current.startIdx = idx + setDragIndex(idx) }} onTouchMove={(e) => { - if (touchDragRef.current.startIdx === null) return; - const touch = e.touches[0]; - const draggedLi = itemRefs.current[touchDragRef.current.startIdx]; - if (draggedLi) draggedLi.style.pointerEvents = "none"; - const el = document.elementFromPoint(touch.clientX, touch.clientY); - if (draggedLi) draggedLi.style.pointerEvents = ""; - const li = el?.closest("[data-bookmark-index]"); + if (touchDragRef.current.startIdx === null) return + const touch = e.touches[0] + const draggedLi = itemRefs.current[touchDragRef.current.startIdx] + if (draggedLi) draggedLi.style.pointerEvents = 'none' + const el = document.elementFromPoint(touch.clientX, touch.clientY) + if (draggedLi) draggedLi.style.pointerEvents = '' + const li = el?.closest('[data-bookmark-index]') if (li) { - const overIdx = parseInt(li.dataset.bookmarkIndex, 10); + const overIdx = parseInt(li.dataset.bookmarkIndex, 10) if (!isNaN(overIdx) && overIdx >= 0 && overIdx < bookmarks.length) { - touchDragRef.current.overIdx = overIdx; - setDragOverIndex(overIdx); + touchDragRef.current.overIdx = overIdx + setDragOverIndex(overIdx) } } }} onTouchEnd={() => { - const { startIdx, overIdx } = touchDragRef.current; + const { startIdx, overIdx } = touchDragRef.current if (startIdx !== null && overIdx !== null && startIdx !== overIdx) { - const items = [...bookmarks]; - const [reordered] = items.splice(startIdx, 1); - items.splice(overIdx, 0, reordered); - setBookmarks(items); + const items = [...bookmarks] + const [reordered] = items.splice(startIdx, 1) + items.splice(overIdx, 0, reordered) + setBookmarks(items) } - touchDragRef.current = { startIdx: null, overIdx: null }; - setDragIndex(null); - setDragOverIndex(null); + touchDragRef.current = { startIdx: null, overIdx: null } + setDragIndex(null) + setDragOverIndex(null) }} sx={{ - touchAction: "none", - display: "flex", - alignItems: "center", - color: "neutral.500", - cursor: sortOrder === "custom" ? "grab" : "default", + touchAction: 'none', + display: 'flex', + alignItems: 'center', + color: 'neutral.500', + cursor: sortOrder === 'custom' ? 'grab' : 'default', mr: 1, }} > @@ -519,8 +522,8 @@ export const TopNav = (props) => { href={bookmark.path} onClick={() => handleBookmarkClose()} sx={{ - textDecoration: "none", - color: "inherit", + textDecoration: 'none', + color: 'inherit', flexGrow: 1, marginRight: 2, }} @@ -533,60 +536,60 @@ export const TopNav = (props) => { spacing={0} sx={{ opacity: 0, - transition: "opacity 150ms ease-in-out", - "@media (hover: none)": { + transition: 'opacity 150ms ease-in-out', + '@media (hover: none)': { opacity: 1, }, }} > - {reorderMode === "arrows" && ( + {reorderMode === 'arrows' && ( <> { - e.preventDefault(); + e.preventDefault() if (locked) { - triggerLockFlash(); - return; + triggerLockFlash() + return } - sortOrder === "custom" ? animatedMoveUp(idx) : triggerSortFlash(); + sortOrder === 'custom' ? animatedMoveUp(idx) : triggerSortFlash() }} - disabled={sortOrder === "custom" && idx === 0} - sx={{ opacity: sortOrder !== "custom" || locked ? 0.4 : 1 }} + disabled={sortOrder === 'custom' && idx === 0} + sx={{ opacity: sortOrder !== 'custom' || locked ? 0.4 : 1 }} > { - e.preventDefault(); + e.preventDefault() if (locked) { - triggerLockFlash(); - return; + triggerLockFlash() + return } - sortOrder === "custom" + sortOrder === 'custom' ? animatedMoveDown(idx) - : triggerSortFlash(); + : triggerSortFlash() }} disabled={ - sortOrder === "custom" && idx === displayBookmarks.length - 1 + sortOrder === 'custom' && idx === displayBookmarks.length - 1 } - sx={{ opacity: sortOrder !== "custom" || locked ? 0.4 : 1 }} + sx={{ opacity: sortOrder !== 'custom' || locked ? 0.4 : 1 }} > )} - {!(reorderMode === "drag" && locked) && ( + {!(reorderMode === 'drag' && locked) && ( { - e.preventDefault(); + e.preventDefault() if (locked) { - triggerLockFlash(); - return; + triggerLockFlash() + return } - removeBookmark(bookmark.path); + removeBookmark(bookmark.path) }} sx={{ ...(locked && { opacity: 0.4 }) }} > @@ -607,10 +610,10 @@ export const TopNav = (props) => { fullWidth maxWidth="md" sx={{ - "& .MuiDialog-container": { - alignItems: "flex-start", + '& .MuiDialog-container': { + alignItems: 'flex-start', }, - "& .MuiDialog-paper": { + '& .MuiDialog-paper': { mt: 8, }, }} @@ -631,15 +634,15 @@ export const TopNav = (props) => { - ); -}; + ) +} TopNav.propTypes = { onNavOpen: PropTypes.func, openNav: PropTypes.bool, -}; +} From 09ef5a9bc969952436190ab133802aa253508963 Mon Sep 17 00:00:00 2001 From: KelvinTegelaar <49186168+KelvinTegelaar@users.noreply.github.com> Date: Thu, 23 Apr 2026 22:01:28 +0200 Subject: [PATCH 2/4] add detection script to dev --- .../CippComponents/CippAppTemplateDrawer.jsx | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/components/CippComponents/CippAppTemplateDrawer.jsx b/src/components/CippComponents/CippAppTemplateDrawer.jsx index 6be184eebaa6..e9ad31ad70f9 100644 --- a/src/components/CippComponents/CippAppTemplateDrawer.jsx +++ b/src/components/CippComponents/CippAppTemplateDrawer.jsx @@ -735,22 +735,55 @@ export const CippAppTemplateDrawer = ({ rows={6} /> - - - - + + + + + + + + + + + + + + Date: Thu, 23 Apr 2026 22:01:39 +0200 Subject: [PATCH 3/4] detection nscript linting --- .../CippComponents/CippAppTemplateDrawer.jsx | 362 +++++++++--------- 1 file changed, 184 insertions(+), 178 deletions(-) diff --git a/src/components/CippComponents/CippAppTemplateDrawer.jsx b/src/components/CippComponents/CippAppTemplateDrawer.jsx index e9ad31ad70f9..242ef110a8b2 100644 --- a/src/components/CippComponents/CippAppTemplateDrawer.jsx +++ b/src/components/CippComponents/CippAppTemplateDrawer.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useCallback, useState } from "react"; +import React, { useEffect, useCallback, useState } from 'react' import { Button, Divider, @@ -10,263 +10,265 @@ import { ListItemText, ListItemSecondaryAction, Alert, -} from "@mui/material"; -import { Grid } from "@mui/system"; -import { useForm, useWatch } from "react-hook-form"; -import { Add, Delete, Edit, Save } from "@mui/icons-material"; -import { CippOffCanvas } from "./CippOffCanvas"; -import CippFormComponent from "./CippFormComponent"; -import { CippFormCondition } from "./CippFormCondition"; -import { CippApiResults } from "./CippApiResults"; -import { ApiGetCall, ApiPostCall } from "../../api/ApiCall"; -import languageList from "../../data/languageList.json"; +} from '@mui/material' +import { Grid } from '@mui/system' +import { useForm, useWatch } from 'react-hook-form' +import { Add, Delete, Edit, Save } from '@mui/icons-material' +import { CippOffCanvas } from './CippOffCanvas' +import CippFormComponent from './CippFormComponent' +import { CippFormCondition } from './CippFormCondition' +import { CippApiResults } from './CippApiResults' +import { ApiGetCall, ApiPostCall } from '../../api/ApiCall' +import languageList from '../../data/languageList.json' const appTypeLabels = { - mspApp: "MSP Vendor App", - StoreApp: "Store App", - chocolateyApp: "Chocolatey App", - officeApp: "Microsoft Office", - win32ScriptApp: "Custom Application", -}; + mspApp: 'MSP Vendor App', + StoreApp: 'Store App', + chocolateyApp: 'Chocolatey App', + officeApp: 'Microsoft Office', + win32ScriptApp: 'Custom Application', +} export const CippAppTemplateDrawer = ({ - buttonText = "Create Template", + buttonText = 'Create Template', editData = null, open = false, onClose, }) => { - const [drawerVisible, setDrawerVisible] = useState(false); - const [apps, setApps] = useState([]); - const [editGUID, setEditGUID] = useState(null); - const formControl = useForm({ mode: "onChange" }); - const templateFormControl = useForm({ mode: "onChange" }); + const [drawerVisible, setDrawerVisible] = useState(false) + const [apps, setApps] = useState([]) + const [editGUID, setEditGUID] = useState(null) + const formControl = useForm({ mode: 'onChange' }) + const templateFormControl = useForm({ mode: 'onChange' }) - const [fetchKey, setFetchKey] = useState(null); + const [fetchKey, setFetchKey] = useState(null) useEffect(() => { if (open && editData?.GUID) { - setFetchKey(`AppTemplate-${editData.GUID}-${Date.now()}`); + setFetchKey(`AppTemplate-${editData.GUID}-${Date.now()}`) } - }, [open, editData?.GUID]); + }, [open, editData?.GUID]) const templateFetch = ApiGetCall({ url: editData?.GUID ? `/api/ListAppTemplates?ID=${editData.GUID}` : null, queryKey: fetchKey, waiting: !!(open && editData?.GUID && fetchKey), - }); + }) useEffect(() => { if (open && editData && templateFetch.isSuccess && templateFetch.data) { - const template = Array.isArray(templateFetch.data) ? templateFetch.data[0] : templateFetch.data; - if (!template) return; + const template = Array.isArray(templateFetch.data) + ? templateFetch.data[0] + : templateFetch.data + if (!template) return - setEditGUID(template.GUID || editData.GUID || null); + setEditGUID(template.GUID || editData.GUID || null) templateFormControl.reset({ - templateName: template.displayName || editData.displayName || "", - templateDescription: template.description || editData.description || "", - }); + templateName: template.displayName || editData.displayName || '', + templateDescription: template.description || editData.description || '', + }) - let appsArray = template.Apps || []; - if (typeof appsArray === "string") { + let appsArray = template.Apps || [] + if (typeof appsArray === 'string') { try { - appsArray = JSON.parse(appsArray); + appsArray = JSON.parse(appsArray) } catch { - appsArray = []; + appsArray = [] } } if (!Array.isArray(appsArray)) { - appsArray = []; + appsArray = [] } const loadedApps = appsArray.map((app) => ({ appType: app.appType, appName: app.appName, - config: typeof app.config === "string" ? app.config : JSON.stringify(app.config), - })); - setApps(loadedApps); - setDrawerVisible(true); + config: typeof app.config === 'string' ? app.config : JSON.stringify(app.config), + })) + setApps(loadedApps) + setDrawerVisible(true) } - }, [open, editData, templateFetch.isSuccess, templateFetch.data]); + }, [open, editData, templateFetch.isSuccess, templateFetch.data]) const applicationType = useWatch({ control: formControl.control, - name: "appType", - }); + name: 'appType', + }) const searchQuerySelection = useWatch({ control: formControl.control, - name: "packageSearch", - }); + name: 'packageSearch', + }) const updateSearchSelection = useCallback( (searchQuerySelection) => { if (searchQuerySelection) { - formControl.setValue("packagename", searchQuerySelection.value.packagename); - formControl.setValue("applicationName", searchQuerySelection.value.applicationName); - formControl.setValue("description", searchQuerySelection.value.description); + formControl.setValue('packagename', searchQuerySelection.value.packagename) + formControl.setValue('applicationName', searchQuerySelection.value.applicationName) + formControl.setValue('description', searchQuerySelection.value.description) if (searchQuerySelection.value.customRepo) { - formControl.setValue("customRepo", searchQuerySelection.value.customRepo); + formControl.setValue('customRepo', searchQuerySelection.value.customRepo) } } }, - [formControl.setValue], - ); + [formControl.setValue] + ) useEffect(() => { - updateSearchSelection(searchQuerySelection); - }, [updateSearchSelection, searchQuerySelection]); + updateSearchSelection(searchQuerySelection) + }, [updateSearchSelection, searchQuerySelection]) - const ChocosearchResults = ApiPostCall({ urlFromData: true }); - const winGetSearchResults = ApiPostCall({ urlFromData: true }); + const ChocosearchResults = ApiPostCall({ urlFromData: true }) + const winGetSearchResults = ApiPostCall({ urlFromData: true }) const saveTemplate = ApiPostCall({ urlFromData: true, - relatedQueryKeys: ["ListAppTemplates"], - }); + relatedQueryKeys: ['ListAppTemplates'], + }) const searchApp = (searchText, type) => { - if (type === "choco") { + if (type === 'choco') { ChocosearchResults.mutate({ - url: "/api/ListAppsRepository", + url: '/api/ListAppsRepository', data: { search: searchText }, queryKey: `SearchApp-${searchText}-${type}`, - }); + }) } - if (type === "StoreApp") { + if (type === 'StoreApp') { winGetSearchResults.mutate({ - url: "/api/ListPotentialApps", - data: { searchString: searchText, type: "WinGet" }, + url: '/api/ListPotentialApps', + data: { searchString: searchText, type: 'WinGet' }, queryKey: `SearchApp-${searchText}-${type}`, - }); + }) } - }; + } const getAppName = (formData) => { - const type = formData.appType?.value; - if (type === "mspApp") return formData.displayName || formData.rmmname?.label || "MSP App"; - if (type === "officeApp") return "Microsoft 365 Apps"; - return formData.applicationName || formData.packagename || "Unnamed App"; - }; + const type = formData.appType?.value + if (type === 'mspApp') return formData.displayName || formData.rmmname?.label || 'MSP App' + if (type === 'officeApp') return 'Microsoft 365 Apps' + return formData.applicationName || formData.packagename || 'Unnamed App' + } const handleAddApp = () => { - const formData = formControl.getValues(); - if (!formData.appType?.value) return; + const formData = formControl.getValues() + if (!formData.appType?.value) return const appEntry = { appType: formData.appType.value, appName: getAppName(formData), config: JSON.stringify(formData), - }; + } - setApps((prev) => [...prev, appEntry]); - formControl.reset({ appType: null }); - }; + setApps((prev) => [...prev, appEntry]) + formControl.reset({ appType: null }) + } const handleEditApp = (index) => { - const currentForm = formControl.getValues(); - const appToEdit = apps[index]; + const currentForm = formControl.getValues() + const appToEdit = apps[index] setApps((prev) => { - const updated = [...prev]; + const updated = [...prev] if (currentForm.appType?.value) { updated.push({ appType: currentForm.appType.value, appName: getAppName(currentForm), config: JSON.stringify(currentForm), - }); + }) } - return updated.filter((_, i) => i !== index); - }); + return updated.filter((_, i) => i !== index) + }) - const config = JSON.parse(appToEdit.config); - if (!config.appType || typeof config.appType === "string") { - const typeValue = appToEdit.appType || config.appType; + const config = JSON.parse(appToEdit.config) + if (!config.appType || typeof config.appType === 'string') { + const typeValue = appToEdit.appType || config.appType config.appType = { label: appTypeLabels[typeValue] || typeValue, value: typeValue, - }; + } } // Normalize "Save as Template" configs (IntuneBody format) to form fields if (config.IntuneBody && !config.applicationName) { - const body = config.IntuneBody; - config.applicationName = config.ApplicationName || body.displayName || ""; - config.description = body.description || ""; - config.AssignTo = config.assignTo || "On"; + const body = config.IntuneBody + config.applicationName = config.ApplicationName || body.displayName || '' + config.description = body.description || '' + config.AssignTo = config.assignTo || 'On' // WinGet/Store: packageIdentifier if (body.packageIdentifier) { - config.packagename = body.packageIdentifier; + config.packagename = body.packageIdentifier } // Chocolatey: extract package name from detection rules or install command if (!config.packagename && body.detectionRules?.[0]?.fileOrFolderName) { - config.packagename = body.detectionRules[0].fileOrFolderName; + config.packagename = body.detectionRules[0].fileOrFolderName } if (!config.packagename && body.installCommandLine) { - const match = body.installCommandLine.match(/-Packagename\s+(\S+)/i); - if (match) config.packagename = match[1]; + const match = body.installCommandLine.match(/-Packagename\s+(\S+)/i) + if (match) config.packagename = match[1] } // Chocolatey: custom repo if (body.installCommandLine) { - const repoMatch = body.installCommandLine.match(/-CustomRepo\s+(\S+)/i); - if (repoMatch) config.customRepo = repoMatch[1]; + const repoMatch = body.installCommandLine.match(/-CustomRepo\s+(\S+)/i) + if (repoMatch) config.customRepo = repoMatch[1] } } - formControl.reset({ appType: config.appType }); + formControl.reset({ appType: config.appType }) setTimeout(() => { Object.entries(config).forEach(([key, value]) => { - if (key !== "appType") { - formControl.setValue(key, value); + if (key !== 'appType') { + formControl.setValue(key, value) } - }); - }, 100); - }; + }) + }, 100) + } const handleRemoveApp = (index) => { - setApps((prev) => prev.filter((_, i) => i !== index)); - }; + setApps((prev) => prev.filter((_, i) => i !== index)) + } const getTotalApps = () => { - const currentForm = formControl.getValues(); - const formHasApp = !!currentForm.appType?.value; - return apps.length + (formHasApp ? 1 : 0); - }; + const currentForm = formControl.getValues() + const formHasApp = !!currentForm.appType?.value + return apps.length + (formHasApp ? 1 : 0) + } const handleSaveTemplate = () => { - const templateData = templateFormControl.getValues(); - const currentForm = formControl.getValues(); + const templateData = templateFormControl.getValues() + const currentForm = formControl.getValues() - const allApps = [...apps]; + const allApps = [...apps] if (currentForm.appType?.value) { allApps.push({ appType: currentForm.appType.value, appName: getAppName(currentForm), config: JSON.stringify(currentForm), - }); + }) } - if (!templateData.templateName || allApps.length === 0) return; + if (!templateData.templateName || allApps.length === 0) return const payload = { displayName: templateData.templateName, - description: templateData.templateDescription || "", + description: templateData.templateDescription || '', apps: allApps, - }; + } if (editGUID) { - payload.GUID = editGUID; + payload.GUID = editGUID } saveTemplate.mutate({ - url: "/api/AddAppTemplate", + url: '/api/AddAppTemplate', data: payload, - }); - }; + }) + } const handleClose = () => { - setDrawerVisible(false); - formControl.reset({ appType: null }); - templateFormControl.reset({ templateName: "", templateDescription: "" }); - setApps([]); - setEditGUID(null); - saveTemplate.reset(); - if (onClose) onClose(); - }; + setDrawerVisible(false) + formControl.reset({ appType: null }) + templateFormControl.reset({ templateName: '', templateDescription: '' }) + setApps([]) + setEditGUID(null) + saveTemplate.reset() + if (onClose) onClose() + } return ( <> @@ -276,12 +278,12 @@ export const CippAppTemplateDrawer = ({ )} +