Skip to content

Commit

Permalink
Merge pull request #70 from KelvinTegelaar/dev
Browse files Browse the repository at this point in the history
[pull] dev from KelvinTegelaar:dev
  • Loading branch information
pull[bot] authored Feb 5, 2025
2 parents 5608787 + be34d7b commit a3747a7
Show file tree
Hide file tree
Showing 9 changed files with 351 additions and 23 deletions.
1 change: 1 addition & 0 deletions public/discord-mark-blue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/CippComponents/CippFormCondition.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const CippFormCondition = (props) => {
if (watcher.includes(compareValue)) {
return children;
}
} else if (typeof watcher === "object" && compareValue in watcher) {
} else if (typeof watcher === "object" && watcher !== null && compareValue in watcher) {
// Check if object contains the key
return children;
}
Expand Down
222 changes: 222 additions & 0 deletions src/components/CippComponents/CippSpeedDial.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import React, { useState, useEffect } from "react";
import {
SpeedDial,
SpeedDialAction,
SpeedDialIcon,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Snackbar,
Alert,
CircularProgress,
} from "@mui/material";
import { Close as CloseIcon } from "@mui/icons-material";
import { useForm } from "react-hook-form";
import { CippFormComponent } from "../../components/CippComponents/CippFormComponent";

const CippSpeedDial = ({
actions = [],
position = { bottom: 16, right: 16 },
icon,
openIcon = <CloseIcon />,
}) => {
const [openDialogs, setOpenDialogs] = useState({});
const [loading, setLoading] = useState(false);
const [showSnackbar, setShowSnackbar] = useState(false);
const [speedDialOpen, setSpeedDialOpen] = useState(false);
const [isHovering, setIsHovering] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");

const formControls = actions.reduce((acc, action) => {
if (action.form) {
acc[action.id] = useForm({
mode: "onChange",
defaultValues: action.form.defaultValues || {},
});
}
return acc;
}, {});

const handleSpeedDialClose = () => {
if (!isHovering) {
setTimeout(() => {
setSpeedDialOpen(false);
}, 200);
}
};

const handleMouseEnter = () => {
setIsHovering(true);
setSpeedDialOpen(true);
};

const handleMouseLeave = () => {
setIsHovering(false);
handleSpeedDialClose();
};

const handleDialogOpen = (actionId) => {
setOpenDialogs((prev) => ({ ...prev, [actionId]: true }));
};

const handleDialogClose = (actionId) => {
setOpenDialogs((prev) => ({ ...prev, [actionId]: false }));
};

const handleSubmit = async (actionId, data) => {
if (!actions.find((a) => a.id === actionId)?.onSubmit) return;

setLoading(true);
try {
const action = actions.find((a) => a.id === actionId);
const result = await action.onSubmit(data);

if (result.success) {
formControls[actionId]?.reset();
handleDialogClose(actionId);
}
setSnackbarMessage(result.message);
setShowSnackbar(true);
} catch (error) {
console.error(`Error submitting ${actionId}:`, error);
setSnackbarMessage("An error occurred while submitting");
setShowSnackbar(true);
} finally {
setLoading(false);
}
};

useEffect(() => {
const handleClickOutside = (event) => {
if (speedDialOpen) {
const speedDial = document.querySelector('[aria-label="Navigation SpeedDial"]');
if (speedDial && !speedDial.contains(event.target)) {
setSpeedDialOpen(false);
}
}
};

document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, [speedDialOpen]);

return (
<>
<SpeedDial
ariaLabel="Navigation SpeedDial"
sx={{
position: "fixed",
...position,
"& .MuiFab-primary": {
width: 46,
height: 46,
"&:hover": {
backgroundColor: "primary.dark",
},
},
}}
icon={<SpeedDialIcon icon={icon} openIcon={openIcon} />}
open={speedDialOpen}
onClose={handleSpeedDialClose}
onOpen={() => setSpeedDialOpen(true)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{actions.map((action) => (
<SpeedDialAction
key={action.id}
icon={action.icon}
tooltipTitle={action.name}
onClick={() => {
if (action.form) {
handleDialogOpen(action.id);
} else if (action.onClick) {
action.onClick();
}
setSpeedDialOpen(false);
}}
tooltipOpen
sx={{
"&.MuiSpeedDialAction-fab": {
backgroundColor: "background.paper",
"&:hover": {
backgroundColor: "action.hover",
},
},
"& .MuiSpeedDialAction-staticTooltipLabel": {
cursor: "pointer",
whiteSpace: "nowrap",
marginRight: "10px",
padding: "6px 10px",
"&:hover": {
backgroundColor: "action.hover",
},
},
}}
/>
))}
</SpeedDial>

{actions
.filter((action) => action.form)
.map((action) => (
<Dialog
key={action.id}
open={openDialogs[action.id] || false}
onClose={() => handleDialogClose(action.id)}
maxWidth="md"
fullWidth
>
<DialogTitle>{action.form.title}</DialogTitle>
<DialogContent>
<CippFormComponent
type="richText"
name={action.form.fieldName}
required
formControl={formControls[action.id]}
style={{ minHeight: "150px" }}
editorProps={{
attributes: {
style: "min-height: 150px; font-size: 1.1rem; padding: 1rem;",
},
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => handleDialogClose(action.id)} disabled={loading}>
Cancel
</Button>
<Button
onClick={formControls[action.id]?.handleSubmit((data) =>
handleSubmit(action.id, data)
)}
variant="contained"
color="primary"
disabled={loading}
startIcon={loading ? <CircularProgress size={20} /> : null}
>
{loading ? "Submitting..." : action.form.submitText || "Submit"}
</Button>
</DialogActions>
</Dialog>
))}

<Snackbar
open={showSnackbar}
autoHideDuration={6000}
onClose={() => setShowSnackbar(false)}
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
>
<Alert onClose={() => setShowSnackbar(false)} severity="success" sx={{ width: "100%" }}>
{snackbarMessage}
</Alert>
</Snackbar>
</>
);
};

export default CippSpeedDial;
2 changes: 1 addition & 1 deletion src/components/CippComponents/CippUserActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export const CippUserActions = () => {
},
confirmText: "Are you sure you want to clear the Immutable ID for this user?",
multiPost: false,
condition: (row) => row.onPremisesSyncEnabled,
condition: (row) => !row.onPremisesSyncEnabled && row?.onPremisesImmutableId,
},
{
label: "Revoke all user sessions",
Expand Down
40 changes: 40 additions & 0 deletions src/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
import TimeAgo from "javascript-time-ago";
import en from "javascript-time-ago/locale/en.json";
import CippSpeedDial from "../components/CippComponents/CippSpeedDial";
import {
Help as HelpIcon,
BugReport as BugReportIcon,
Feedback as FeedbackIcon,
} from "@mui/icons-material";
import { SvgIcon } from "@mui/material";
import discordIcon from "../../public/discord-mark-blue.svg";
import React from "react";
TimeAgo.addDefaultLocale(en);

Expand All @@ -36,6 +44,33 @@ const App = (props) => {
const getLayout = Component.getLayout ?? ((page) => page);
const preferredTheme = useMediaPredicate("(prefers-color-scheme: dark)") ? "dark" : "light";

const speedDialActions = [
{
id: "bug-report",
icon: <BugReportIcon />,
name: "Report Bug",
href: "https://github.com/KelvinTegelaar/CIPP/issues/new?template=bug.yml",
onClick: () => window.open("https://github.com/KelvinTegelaar/CIPP/issues/new?template=bug.yml", "_blank")
},
{
id: "feature-request",
icon: <FeedbackIcon />,
name: "Request Feature",
href: "https://github.com/KelvinTegelaar/CIPP/issues/new?template=feature.yml",
onClick: () => window.open("https://github.com/KelvinTegelaar/CIPP/issues/new?template=feature.yml", "_blank")
},
{
id: "discord",
icon: (
<SvgIcon component={discordIcon} viewBox="0 0 127.14 96.36" sx={{ fontSize: '1.5rem' }}>
</SvgIcon>
),
name: "Join the Discord!",
href: "https://discord.gg/cyberdrain",
onClick: () => window.open("https://discord.gg/cyberdrain", "_blank")
},
];

return (
<CacheProvider value={emotionCache}>
<Head>
Expand Down Expand Up @@ -69,6 +104,11 @@ const App = (props) => {
<PrivateRoute>{getLayout(<Component {...pageProps} />)}</PrivateRoute>
</ErrorBoundary>
<Toaster position="top-center" />
<CippSpeedDial
actions={speedDialActions}
icon={<HelpIcon />}
position={{ bottom: 16, right: 16 }}
/>
</RTL>
</ThemeProvider>
{settings?.showDevtools && (
Expand Down
8 changes: 4 additions & 4 deletions src/pages/email/transport/list-connectors/add.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ const AddPolicy = () => {
return (
<CippFormPage
formControl={formControl}
queryKey="AddTransportRule"
title="Add Transport Rule"
backButtonTitle="Transport Rules Overview"
queryKey="AddConnector"
title="Add Connector"
backButtonTitle="Connectors Overview"
postUrl="/api/AddExConnector"
>
<Grid container spacing={2} sx={{ mb: 2 }}>
Expand All @@ -55,7 +55,7 @@ const AddPolicy = () => {
formControl={formControl}
multiple={false}
api={{
queryKey: `TemplateListTransport`,
queryKey: `TemplateListConnectors`,
labelField: "name",
valueField: (option) => option,
url: "/api/ListExconnectorTemplates",
Expand Down
11 changes: 4 additions & 7 deletions src/pages/email/transport/list-connectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ const Page = () => {
type: "POST",
url: "/api/EditExConnector",
icon: <Check />,
condition: (row) => !row.Enabled,
data: {
State: "Enable",
State: "!Enable",
GUID: "Guid",
Type: "cippconnectortype",
},
Expand All @@ -35,8 +36,9 @@ const Page = () => {
type: "POST",
url: "/api/EditExConnector",
icon: <Block />,
condition: (row) => row.Enabled,
data: {
State: "Disable",
State: "!Disable",
GUID: "Guid",
Type: "cippconnectortype",
},
Expand Down Expand Up @@ -82,11 +84,6 @@ const Page = () => {
actions={actions}
offCanvas={offCanvas}
simpleColumns={simpleColumns}
titleButton={{
label: "Deploy Connector",
href: "/email/connectors/deploy-connector",
startIcon: <RocketLaunch />, // Added icon
}}
cardButton={
<>
<Button
Expand Down
5 changes: 3 additions & 2 deletions src/pages/identity/administration/users/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const Page = () => {
const offCanvas = {
extendedInfoFields: [
"createdDateTime", // Created Date (UTC)
"id", // Unique ID
"userPrincipalName", // UPN
"givenName", // Given Name
"surname", // Surname
Expand All @@ -42,7 +43,7 @@ const Page = () => {
"city", // City
"department", // Department
"onPremisesLastSyncDateTime", // OnPrem Last Sync
"id", // Unique ID
"onPremisesDistinguishedName", // OnPrem DN
"otherMails", // Alternate Email Addresses
],
actions: CippUserActions(),
Expand All @@ -69,7 +70,7 @@ const Page = () => {
Endpoint: "users",
manualPagination: true,
$select:
"id,accountEnabled,businessPhones,city,createdDateTime,companyName,country,department,displayName,faxNumber,givenName,isResourceAccount,jobTitle,mail,mailNickname,mobilePhone,onPremisesDistinguishedName,officeLocation,onPremisesLastSyncDateTime,otherMails,postalCode,preferredDataLocation,preferredLanguage,proxyAddresses,showInAddressList,state,streetAddress,surname,usageLocation,userPrincipalName,userType,assignedLicenses,onPremisesSyncEnabled",
"id,accountEnabled,businessPhones,city,createdDateTime,companyName,country,department,displayName,faxNumber,givenName,isResourceAccount,jobTitle,mail,mailNickname,mobilePhone,officeLocation,otherMails,postalCode,preferredDataLocation,preferredLanguage,proxyAddresses,showInAddressList,state,streetAddress,surname,usageLocation,userPrincipalName,userType,assignedLicenses,onPremisesSyncEnabled,OnPremisesImmutableId,onPremisesLastSyncDateTime,onPremisesDistinguishedName",
$count: true,
$orderby: "displayName",
$top: 999,
Expand Down
Loading

0 comments on commit a3747a7

Please sign in to comment.