diff --git a/src/components/CippComponents/CippApiResults.jsx b/src/components/CippComponents/CippApiResults.jsx index 21af58b8cc39..0c5807c379ed 100644 --- a/src/components/CippComponents/CippApiResults.jsx +++ b/src/components/CippComponents/CippApiResults.jsx @@ -10,7 +10,7 @@ const extractAllResults = (data) => { const getSeverity = (text) => { if (typeof text !== "string") return "success"; - return /error|failed|exception|not found/i.test(text) ? "error" : "success"; + return /error|failed|exception|not found|invalid_grant/i.test(text) ? "error" : "success"; }; const processResultItem = (item) => { diff --git a/src/components/CippComponents/CippFormComponent.jsx b/src/components/CippComponents/CippFormComponent.jsx index 28abfd910586..d44f547f94d7 100644 --- a/src/components/CippComponents/CippFormComponent.jsx +++ b/src/components/CippComponents/CippFormComponent.jsx @@ -24,12 +24,17 @@ import { } from "mui-tiptap"; import StarterKit from "@tiptap/starter-kit"; import { CippDataTable } from "../CippTable/CippDataTable"; +import React from "react"; // Helper function to convert bracket notation to dot notation const convertBracketsToDots = (name) => { return name.replace(/\[(\d+)\]/g, ".$1"); // Replace [0] with .0 }; +const MemoizedCippAutoComplete = React.memo((props) => { + return ; +}); + export const CippFormComponent = (props) => { const { validators, @@ -233,19 +238,17 @@ export const CippFormComponent = (props) => { name={convertedName} control={formControl.control} rules={validators} - render={({ field }) => - React.memo( - field.onChange(value.value)} - /> - ) - } + render={({ field }) => ( + field.onChange(value.value)} + /> + )} /> @@ -263,7 +266,7 @@ export const CippFormComponent = (props) => { control={formControl.control} rules={validators} render={({ field }) => ( - { }, { label: "Clear Immutable ID", - type: "GET", + type: "POST", icon: , url: "/api/ExecClrImmId", data: { diff --git a/src/components/CippWizard/CippWizardCSVImport.jsx b/src/components/CippWizard/CippWizardCSVImport.jsx index 6f665df4523b..141672f68874 100644 --- a/src/components/CippWizard/CippWizardCSVImport.jsx +++ b/src/components/CippWizard/CippWizardCSVImport.jsx @@ -32,6 +32,11 @@ export const CippWizardCSVImport = (props) => { const [newTableData, setTableData] = useState([]); const [open, setOpen] = useState(false); + // Register form field with validation + formControl.register(name, { + validate: (value) => Array.isArray(value) && value.length > 0, + }); + const handleRemoveItem = (row) => { if (row === undefined) return false; const index = tableData?.findIndex((item) => item === row); @@ -49,7 +54,9 @@ export const CippWizardCSVImport = (props) => { }; useEffect(() => { - formControl.setValue(name, newTableData); + formControl.setValue(name, newTableData, { + shouldValidate: true, + }); }, [newTableData]); const actions = [ diff --git a/src/components/CippWizard/CippWizardOffboarding.jsx b/src/components/CippWizard/CippWizardOffboarding.jsx index d61e9365c1e5..6c79070dcd81 100644 --- a/src/components/CippWizard/CippWizardOffboarding.jsx +++ b/src/components/CippWizard/CippWizardOffboarding.jsx @@ -97,6 +97,12 @@ export const CippWizardOffboarding = (props) => { type="switch" formControl={formControl} /> + { url: "/.auth/me", queryKey: "authmecipp", }); + const [hideSidebar, setHideSidebar] = useState(false); useEffect(() => { if (currentRole.isSuccess && !currentRole.isFetching) { const userRoles = currentRole.data?.clientPrincipal?.userRoles; if (!userRoles) { + setMenuItems([]); + setHideSidebar(true); return; } const filterItemsByRole = (items) => { @@ -200,15 +203,19 @@ export const Layout = (props) => { return ( <> - - {mdDown && ( - + {hideSidebar === false && ( + <> + + {mdDown && ( + + )} + {!mdDown && } + )} - {!mdDown && } diff --git a/src/pages/email/resources/management/list-rooms/edit.jsx b/src/pages/email/resources/management/list-rooms/edit.jsx new file mode 100644 index 000000000000..91e9ecd66b11 --- /dev/null +++ b/src/pages/email/resources/management/list-rooms/edit.jsx @@ -0,0 +1,311 @@ +import React, { useEffect } from "react"; +import { Grid, Divider, Typography } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { Layout as DashboardLayout } from "/src/layouts/index.js"; +import CippFormPage from "/src/components/CippFormPages/CippFormPage"; +import CippFormComponent from "/src/components/CippComponents/CippFormComponent"; +import { useSettings } from "/src/hooks/use-settings"; +import { useRouter } from "next/router"; +import { ApiGetCall } from "/src/api/ApiCall"; +import countryList from "/src/data/countryList.json"; + +const EditRoomMailbox = () => { + const router = useRouter(); + const { roomId } = router.query; + const tenantDomain = useSettings().currentTenant; + const formControl = useForm({ + mode: "onChange", + }); + + const roomInfo = ApiGetCall({ + url: `/api/ListRooms?roomId=${roomId}&tenantFilter=${tenantDomain}`, + queryKey: `Room-${roomId}`, + waiting: false, + }); + + useEffect(() => { + if (roomInfo.isSuccess && roomInfo.data?.[0]) { + const room = roomInfo.data[0]; + formControl.reset({ + // Core Properties + displayName: room.displayName, + hiddenFromAddressListsEnabled: room.hiddenFromAddressListsEnabled, + + // Room Booking Settings + capacity: room.capacity, + + // Location Information + building: room.building, + floor: room.floor, + floorLabel: room.floorLabel, + street: room.street, + city: room.city, + state: room.state, + postalCode: room.postalCode, + countryOrRegion: room.countryOrRegion + ? countryList.find((c) => c.Name === room.countryOrRegion)?.Code || "" + : "", + + // Room Equipment + audioDeviceName: room.audioDeviceName, + videoDeviceName: room.videoDeviceName, + displayDeviceName: room.displayDeviceName, + + // Room Features + isWheelChairAccessible: room.isWheelChairAccessible, + phone: room.phone, + tags: room.tags?.map(tag => ({ label: tag, value: tag })) || [], + }); + } + }, [roomInfo.isSuccess, roomInfo.data]); + + useEffect(() => { + if (roomId) { + roomInfo.refetch(); + } + }, [router.query, roomId, tenantDomain]); + + return ( + ({ + tenantID: tenantDomain, + roomId: roomId, + displayName: values.displayName?.trim(), + hiddenFromAddressListsEnabled: values.hiddenFromAddressListsEnabled, + + // Room Booking Settings + capacity: values.capacity, + + // Location Information + building: values.building?.trim(), + floor: values.floor, + floorLabel: values.floorLabel?.trim(), + street: values.street?.trim(), + city: values.city?.trim(), + state: values.state?.trim(), + postalCode: values.postalCode?.trim(), + countryOrRegion: values.countryOrRegion?.value || values.countryOrRegion, + + // Room Equipment + audioDeviceName: values.audioDeviceName?.trim(), + videoDeviceName: values.videoDeviceName?.trim(), + displayDeviceName: values.displayDeviceName?.trim(), + + // Room Features + isWheelChairAccessible: values.isWheelChairAccessible, + phone: values.phone?.trim(), + tags: values.tags?.map(tag => tag.value), + })} + > + + {/* Core & Booking Settings */} + + + + + + + + + + + + + + + + + + + {/* Location Information */} + + Location Information + + + {/* Building and Floor Info */} + + + + + + + + + + + + + + + + {/* Address Fields */} + + + + + + {/* City and Postal Code */} + + + + + + + + + {/* State and Country */} + + + + + + ({ + label: Name, + value: Code, + }))} + formControl={formControl} + /> + + + + + + + {/* Room Equipment */} + + Room Equipment + + + + + + + + + + + + + + + + + {/* Room Features */} + + Room Features + + + + + + + + + + + + ); +}; + +EditRoomMailbox.getLayout = (page) => {page}; + +export default EditRoomMailbox; \ No newline at end of file diff --git a/src/pages/email/resources/management/list-rooms/index.js b/src/pages/email/resources/management/list-rooms/index.js index a3fe7dab1547..9fc307d60e63 100644 --- a/src/pages/email/resources/management/list-rooms/index.js +++ b/src/pages/email/resources/management/list-rooms/index.js @@ -2,16 +2,68 @@ import { Layout as DashboardLayout } from "/src/layouts/index.js"; import { CippTablePage } from "/src/components/CippComponents/CippTablePage.jsx"; import { Button } from "@mui/material"; import Link from "next/link"; -import { AddHomeWork } from "@mui/icons-material"; +import { AddHomeWork, Edit, Block, LockOpen } from "@mui/icons-material"; +import { TrashIcon } from "@heroicons/react/24/outline"; const Page = () => { const pageTitle = "Rooms"; + const actions = [ + { + label: "Edit Room", + link: `/email/resources/management/list-rooms/edit?roomId=[id]`, + icon: , + color: "info", + condition: (row) => !row.isDirSynced, + }, + { + label: "Block Sign In", + type: "GET", + icon: , + url: "/api/ExecDisableUser", + data: { ID: "id" }, + confirmText: "Are you sure you want to block the sign-in for this room mailbox?", + multiPost: false, + condition: (row) => !row.accountDisabled && !row.isDirSynced, + }, + { + label: "Unblock Sign In", + type: "GET", + icon: , + url: "/api/ExecDisableUser", + data: { ID: "id", Enable: true }, + confirmText: "Are you sure you want to unblock sign-in for this room mailbox?", + multiPost: false, + condition: (row) => row.accountDisabled && !row.isDirSynced, + }, + { + label: "Delete Room", + type: "GET", + icon: , + url: "/api/RemoveMailbox", + data: { ID: "mail" }, + confirmText: "Are you sure you want to delete this room mailbox?", + multiPost: false, + condition: (row) => !row.isDirSynced, + }, + ]; + return ( { title={pageTitle} apiUrl={apiUrl} actions={actions} + apiDataKey="ListRoomListsResults" offCanvas={offCanvas} simpleColumns={simpleColumns} /> diff --git a/src/pages/tools/community-repos/index.js b/src/pages/tools/community-repos/index.js index 8fe3c2b65da1..f862ba2aa028 100644 --- a/src/pages/tools/community-repos/index.js +++ b/src/pages/tools/community-repos/index.js @@ -18,6 +18,7 @@ import { Typography, Alert, Link, + Chip, } from "@mui/material"; import { TrashIcon } from "@heroicons/react/24/outline"; import { ApiPostCall } from "/src/api/ApiCall"; @@ -37,6 +38,7 @@ const Page = () => { const [results, setResults] = useState([]); const [repo, setRepo] = useState(""); const [user, setUser] = useState(""); + const [org, setOrg] = useState(""); const actions = [ { @@ -51,6 +53,7 @@ const Page = () => { data: { Action: "Delete", Id: "Id" }, confirmText: "Are you sure you want to delete this repo?", icon: , + multiPost: false, queryKey: "CommunityRepos", }, ]; @@ -80,8 +83,9 @@ const Page = () => { url: "/api/ExecGitHubAction", data: { Search: { - Repository: repo ? [repo] : [], - User: user ? [user] : [], + Repository: repo ? repo : "", + User: user ? user : "", + Org: org ? org : "", SearchTerm: searchTerms, Type: "repositories", }, @@ -143,6 +147,7 @@ const Page = () => { onChange={(e) => searchForm.setValue("searchType", e.target.value)} > } label="User" /> + } label="Org" /> } label="Repository" /> @@ -182,6 +187,28 @@ const Page = () => { label="Search Terms" /> + + setOrg(e.target.value)} + /> + + @@ -217,8 +244,32 @@ const Page = () => { - - {r.full_name} + + + + {r.full_name} + + + {r.html_url} diff --git a/src/pages/tools/templatelib/index.jsx b/src/pages/tools/templatelib/index.jsx index e654b87f1a8e..fbe16255ecc8 100644 --- a/src/pages/tools/templatelib/index.jsx +++ b/src/pages/tools/templatelib/index.jsx @@ -76,7 +76,11 @@ const TemplateLibrary = () => { > - + @@ -89,18 +93,14 @@ const TemplateLibrary = () => { `${option.Name} (${option.URL})`, + }} formControl={formControl} multiple={false} /> diff --git a/src/pages/unauthenticated.js b/src/pages/unauthenticated.js index ce64a19fa462..6c06e2ca3a80 100644 --- a/src/pages/unauthenticated.js +++ b/src/pages/unauthenticated.js @@ -3,6 +3,7 @@ import Head from "next/head"; import { CippImageCard } from "../components/CippCards/CippImageCard"; import { Layout as DashboardLayout } from "../layouts/index.js"; import { ApiGetCall } from "../api/ApiCall"; +import { useState, useEffect } from "react"; const Page = () => { const orgData = ApiGetCall({ @@ -10,9 +11,16 @@ const Page = () => { queryKey: "me", }); const blockedRoles = ["anonymous", "authenticated"]; - const userRoles = orgData.data?.clientPrincipal?.userRoles.filter( - (role) => !blockedRoles.includes(role) - ); + const [userRoles, setUserRoles] = useState([]); + + useEffect(() => { + if (orgData.isSuccess) { + const roles = orgData.data?.clientPrincipal?.userRoles.filter( + (role) => !blockedRoles.includes(role) + ); + setUserRoles(roles ?? []); + } + }, [orgData, blockedRoles]); return ( <> @@ -36,14 +44,16 @@ const Page = () => { sx={{ height: "100%" }} // Ensure the container takes full height > - 0 ? "Return" : "Login"} - link={userRoles.length > 0 ? "/" : "/.auth/login/aad"} - /> + {orgData.isSuccess && Array.isArray(userRoles) && ( + 0 ? "Return" : "Login"} + link={userRoles.length > 0 ? "/" : "/.auth/login/aad"} + /> + )} diff --git a/src/utils/get-cipp-formatting.js b/src/utils/get-cipp-formatting.js index 2feaacacf94d..b6b7fa399571 100644 --- a/src/utils/get-cipp-formatting.js +++ b/src/utils/get-cipp-formatting.js @@ -429,6 +429,25 @@ export const getCippFormatting = (data, cellName, type, canReceive) => { ); } + if (cellName === "Visibility") { + const gitHubVisibility = ["public", "private", "internal"]; + if (gitHubVisibility.includes(data)) { + return isText ? ( + data + ) : ( + + ); + } + } + if (cellName === "AutoMapUrl") { return isText ? data : ; }