Skip to content

Commit 90c8160

Browse files
jacobshandlingJacob Shandling
authored andcommitted
17445 calendar events modal (#17717)
Addresses #17445 Follow-up iteration: - Finalize styling of dropdown tooltips - All `//TODO`s <img width="1393" alt="Screenshot 2024-03-20 at 1 43 54 PM" src="https://github.com/fleetdm/fleet/assets/61553566/9b792cf0-058a-4ae6-8f5f-a49eb936ebef"> <img width="1393" alt="Screenshot 2024-03-20 at 1 44 01 PM" src="https://github.com/fleetdm/fleet/assets/61553566/86195dcf-ec28-4cf0-ab8b-d785d12372ed"> <img width="1393" alt="Screenshot 2024-03-20 at 1 44 21 PM" src="https://github.com/fleetdm/fleet/assets/61553566/01effdec-ca20-49ec-a442-5fe754a5e12b"> <img width="1393" alt="Screenshot 2024-03-20 at 1 44 26 PM" src="https://github.com/fleetdm/fleet/assets/61553566/b6de6891-6eae-426e-bbff-b01184094ac9"> <img width="1393" alt="Screenshot 2024-03-20 at 1 44 33 PM" src="https://github.com/fleetdm/fleet/assets/61553566/96e167dd-752c-4b49-a1a7-69fe9b4f42ac"> <img width="1393" alt="Screenshot 2024-03-20 at 1 44 43 PM" src="https://github.com/fleetdm/fleet/assets/61553566/feedbda5-e915-4e5e-84ee-2316db49434a"> <img width="1393" alt="Screenshot 2024-03-20 at 1 44 47 PM" src="https://github.com/fleetdm/fleet/assets/61553566/c4b5ac47-3357-43ef-95ca-dd0953994f6f"> <img width="1393" alt="Screenshot 2024-03-20 at 1 45 02 PM" src="https://github.com/fleetdm/fleet/assets/61553566/17838415-5bf4-46f0-9bde-522deb0f0886"> <img width="1393" alt="Screenshot 2024-03-20 at 1 45 10 PM" src="https://github.com/fleetdm/fleet/assets/61553566/b7228484-bb9f-4119-9fbf-a60ce990ba0e"> --------- Co-authored-by: Jacob Shandling <[email protected]>
1 parent c057b82 commit 90c8160

File tree

23 files changed

+1917
-145
lines changed

23 files changed

+1917
-145
lines changed

frontend/__mocks__/configMock.ts

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const DEFAULT_CONFIG_MOCK: IConfig = {
7676
integrations: {
7777
jira: [],
7878
zendesk: [],
79+
google_calendar: [],
7980
},
8081
logging: {
8182
debug: false,

frontend/__mocks__/policyMock.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const DEFAULT_POLICY_MOCK: IPolicyStats = {
2222
webhook: "Off",
2323
has_run: true,
2424
next_update_ms: 3600000,
25+
calendar_events_enabled: true,
2526
};
2627

2728
const createMockPolicy = (overrides?: Partial<IPolicyStats>): IPolicyStats => {

frontend/components/forms/FormField/FormField.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import classnames from "classnames";
33
import { isEmpty } from "lodash";
44

55
import TooltipWrapper from "components/TooltipWrapper";
6+
import { PlacesType } from "react-tooltip-5";
67

78
// all form-field styles are defined in _global.scss, which apply here and elsewhere
89
const baseClass = "form-field";
@@ -16,6 +17,7 @@ export interface IFormFieldProps {
1617
name: string;
1718
type: string;
1819
tooltip?: React.ReactNode;
20+
labelTooltipPosition?: PlacesType;
1921
}
2022

2123
const FormField = ({
@@ -27,6 +29,7 @@ const FormField = ({
2729
name,
2830
type,
2931
tooltip,
32+
labelTooltipPosition,
3033
}: IFormFieldProps): JSX.Element => {
3134
const renderLabel = () => {
3235
const labelWrapperClasses = classnames(`${baseClass}__label`, {
@@ -45,7 +48,10 @@ const FormField = ({
4548
>
4649
{error ||
4750
(tooltip ? (
48-
<TooltipWrapper tipContent={tooltip}>
51+
<TooltipWrapper
52+
tipContent={tooltip}
53+
position={labelTooltipPosition || "top-start"}
54+
>
4955
{label as string}
5056
</TooltipWrapper>
5157
) : (

frontend/components/forms/fields/InputField/InputField.jsx

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class InputField extends Component {
3333
]).isRequired,
3434
parseTarget: PropTypes.bool,
3535
tooltip: PropTypes.string,
36+
labelTooltipPosition: PropTypes.string,
3637
helpText: PropTypes.oneOfType([
3738
PropTypes.string,
3839
PropTypes.arrayOf(PropTypes.string),
@@ -55,6 +56,7 @@ class InputField extends Component {
5556
value: "",
5657
parseTarget: false,
5758
tooltip: "",
59+
labelTooltipPosition: "",
5860
helpText: "",
5961
enableCopy: false,
6062
ignore1password: false,
@@ -124,6 +126,7 @@ class InputField extends Component {
124126
"error",
125127
"name",
126128
"tooltip",
129+
"labelTooltipPosition",
127130
]);
128131

129132
const copyValue = (e) => {

frontend/components/forms/fields/Slider/Slider.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import FormField from "components/forms/FormField";
66
import { IFormFieldProps } from "components/forms/FormField/FormField";
77

88
interface ISliderProps {
9-
onChange: () => void;
9+
onChange: (newValue?: {
10+
name: string;
11+
value: string | number | boolean;
12+
}) => void;
1013
value: boolean;
1114
inactiveText: string;
1215
activeText: string;

frontend/components/graphics/CalendarEventPreview.tsx

+1,184
Large diffs are not rendered by default.

frontend/components/graphics/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import EmptyTeams from "./EmptyTeams";
1717
import EmptyPacks from "./EmptyPacks";
1818
import EmptySchedule from "./EmptySchedule";
1919
import CollectingResults from "./CollectingResults";
20+
import CalendarEventPreview from "./CalendarEventPreview";
2021

2122
export const GRAPHIC_MAP = {
2223
// Empty state graphics
@@ -41,6 +42,7 @@ export const GRAPHIC_MAP = {
4142
"file-pem": FilePem,
4243
// Other graphics
4344
"collecting-results": CollectingResults,
45+
"calendar-event-preview": CalendarEventPreview,
4446
};
4547

4648
export type GraphicNames = keyof typeof GRAPHIC_MAP;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useState } from "react";
2+
3+
import { IPolicy } from "interfaces/policy";
4+
5+
interface ICheckedPolicy {
6+
name?: string;
7+
id: number;
8+
isChecked: boolean;
9+
}
10+
11+
const useCheckboxListStateManagement = (
12+
allPolicies: IPolicy[],
13+
automatedPolicies: number[] | undefined
14+
) => {
15+
const [policyItems, setPolicyItems] = useState<ICheckedPolicy[]>(() => {
16+
return allPolicies.map(({ name, id }) => ({
17+
name,
18+
id,
19+
isChecked: !!automatedPolicies?.includes(id),
20+
}));
21+
});
22+
23+
const updatePolicyItems = (policyId: number) => {
24+
setPolicyItems((prevItems) =>
25+
prevItems.map((policy) =>
26+
policy.id !== policyId
27+
? policy
28+
: { ...policy, isChecked: !policy.isChecked }
29+
)
30+
);
31+
};
32+
33+
return { policyItems, updatePolicyItems };
34+
};
35+
36+
export default useCheckboxListStateManagement;

frontend/interfaces/config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
IWebhookFailingPolicies,
55
IWebhookSoftwareVulnerabilities,
66
} from "interfaces/webhook";
7-
import { IIntegrations } from "./integration";
7+
import { IGlobalIntegrations } from "./integration";
88

99
export interface ILicense {
1010
tier: string;
@@ -175,7 +175,7 @@ export interface IConfig {
175175
// databases_path: string;
176176
// };
177177
webhook_settings: IWebhookSettings;
178-
integrations: IIntegrations;
178+
integrations: IGlobalIntegrations;
179179
logging: {
180180
debug: boolean;
181181
json: boolean;

frontend/interfaces/integration.ts

+24
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,31 @@ export interface IIntegrationFormErrors {
6060
enableSoftwareVulnerabilities?: boolean;
6161
}
6262

63+
export interface IGlobalCalendarIntegration {
64+
email: string;
65+
private_key: string;
66+
domain: string;
67+
}
68+
69+
interface ITeamCalendarSettings {
70+
enable_calendar_events: boolean;
71+
webhook_url: string;
72+
}
73+
74+
// zendesk and jira fields are coupled – if one is present, the other needs to be present. If
75+
// one is present and the other is null/missing, the other will be nullified. google_calendar is
76+
// separated – it can be present without the other 2 without nullifying them.
77+
// TODO: Update these types to reflect this.
78+
6379
export interface IIntegrations {
6480
zendesk: IZendeskIntegration[];
6581
jira: IJiraIntegration[];
6682
}
83+
84+
export interface IGlobalIntegrations extends IIntegrations {
85+
google_calendar?: IGlobalCalendarIntegration[] | null;
86+
}
87+
88+
export interface ITeamIntegrations extends IIntegrations {
89+
google_calendar?: ITeamCalendarSettings | null;
90+
}

frontend/interfaces/policy.ts

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export interface IPolicy {
4040
created_at: string;
4141
updated_at: string;
4242
critical: boolean;
43+
calendar_events_enabled: boolean;
4344
}
4445

4546
// Used on the manage hosts page and other places where aggregate stats are displayed
@@ -90,6 +91,7 @@ export interface IPolicyFormData {
9091
query?: string | number | boolean | undefined;
9192
team_id?: number;
9293
id?: number;
94+
calendar_events_enabled?: boolean;
9395
}
9496

9597
export interface IPolicyNew {

frontend/interfaces/team.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import PropTypes from "prop-types";
22
import { IConfigFeatures, IWebhookSettings } from "./config";
33
import enrollSecretInterface, { IEnrollSecret } from "./enroll_secret";
4-
import { IIntegrations } from "./integration";
4+
import { ITeamIntegrations } from "./integration";
55
import { UserRole } from "./user";
66

77
export default PropTypes.shape({
@@ -82,7 +82,7 @@ export type ITeamWebhookSettings = Pick<
8282
*/
8383
export interface ITeamAutomationsConfig {
8484
webhook_settings: ITeamWebhookSettings;
85-
integrations: IIntegrations;
85+
integrations: ITeamIntegrations;
8686
}
8787

8888
/**
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,9 @@
11
.host-actions-dropdown {
2-
.form-field {
3-
margin: 0;
2+
@include button-dropdown;
3+
.Select-multi-value-wrapper {
4+
width: 55px;
45
}
5-
6-
.Select {
7-
position: relative;
8-
border: 0;
9-
height: auto;
10-
11-
&.is-focused,
12-
&:hover {
13-
border: 0;
14-
}
15-
16-
&.is-focused:not(.is-open) {
17-
.Select-control {
18-
background-color: initial;
19-
}
20-
}
21-
22-
.Select-control {
23-
display: flex;
24-
background-color: initial;
25-
height: auto;
26-
justify-content: space-between;
27-
border: 0;
28-
cursor: pointer;
29-
30-
&:hover {
31-
box-shadow: none;
32-
}
33-
34-
&:hover .Select-placeholder {
35-
color: $core-vibrant-blue;
36-
}
37-
38-
.Select-placeholder {
39-
color: $core-fleet-black;
40-
font-size: 14px;
41-
line-height: normal;
42-
padding-left: 0;
43-
margin-top: 1px;
44-
}
45-
46-
.Select-input {
47-
height: auto;
48-
}
49-
50-
.Select-arrow-zone {
51-
display: flex;
52-
}
53-
}
54-
55-
.Select-multi-value-wrapper {
56-
width: 55px;
57-
}
58-
59-
.Select-placeholder {
60-
display: flex;
61-
align-items: center;
62-
}
63-
64-
.Select-menu-outer {
65-
margin-top: $pad-xsmall;
66-
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
67-
border-radius: $border-radius;
68-
z-index: 6;
69-
overflow: hidden;
70-
border: 0;
71-
width: 188px;
72-
left: unset;
73-
top: unset;
74-
max-height: none;
75-
padding: $pad-small;
76-
position: absolute;
77-
left: -120px;
78-
79-
.Select-menu {
80-
max-height: none;
81-
}
82-
}
83-
84-
.Select-arrow {
85-
transition: transform 0.25s ease;
86-
}
87-
88-
&:not(.is-open) {
89-
.Select-control:hover .Select-arrow {
90-
content: url("../assets/images/[email protected]");
91-
}
92-
}
93-
94-
&.is-open {
95-
.Select-control .Select-placeholder {
96-
color: $core-vibrant-blue;
97-
}
98-
99-
.Select-arrow {
100-
transform: rotate(180deg);
101-
}
102-
}
6+
.Select > .Select-menu-outer {
7+
left: -120px;
1038
}
1049
}

frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { QueryContext } from "context/query";
1313
import { NotificationContext } from "context/notification";
1414

1515
import activitiesAPI, {
16-
IActivitiesResponse,
1716
IPastActivitiesResponse,
1817
IUpcomingActivitiesResponse,
1918
} from "services/entities/activities";

0 commit comments

Comments
 (0)