Skip to content

Commit

Permalink
Task modal (#1126)
Browse files Browse the repository at this point in the history
* task_modal enhancements

* remove console log

* task_modal

* allow remove of all assignees and responsibilities

* fix weird behaviour when selecting and clicking away

* index.tsx

---------

Co-authored-by: Tom Chapman <tchapman000@gmail.com>
  • Loading branch information
TomChapmanGov and tom0827 authored Nov 2, 2023
1 parent 8e3deb6 commit 6ddc76d
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 27 deletions.
10 changes: 4 additions & 6 deletions epictrack-api/src/api/services/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,10 @@ def update_task_event(cls, data: dict, task_event_id) -> TaskEvent:
"Only team members can be assigned to a task"
)
task_event.update(data, commit=False)
if data.get("assignee_ids"):
cls._handle_assignees(data.get("assignee_ids"), [task_event.id])
if data.get("responsibility_ids"):
cls._handle_responsibilities(
data.get("responsibility_ids"), [task_event.id]
)
cls._handle_assignees(data.get("assignee_ids"), [task_event.id])
cls._handle_responsibilities(
data.get("responsibility_ids"), [task_event.id]
)
db.session.commit()
return task_event

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";
import { Box, Checkbox } from "@mui/material";
import { components, OptionProps } from "react-select";

const Option = ({
getStyles,
isDisabled,
isFocused,
children,
innerProps,
isMulti,
...rest
}: OptionProps) => {
const [isSelected, setIsSelected] = React.useState(false);
const { filterProps } = rest.selectProps;

React.useEffect(() => {
if (filterProps?.selectedOptions && filterProps.getOptionValue) {
const val = filterProps.getOptionValue(rest.data);
setIsSelected(filterProps?.selectedOptions.indexOf(val) > -1);
}
}, [filterProps?.selectedOptions]);

return (
<Box>
<components.Option
{...rest}
isMulti={isMulti}
isDisabled={isDisabled}
isFocused={isFocused}
isSelected={isSelected}
getStyles={getStyles}
innerProps={innerProps}
>
<Checkbox checked={isSelected} />
{children}
</components.Option>
</Box>
);
};

export default Option;
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import React from "react";
import { FormHelperText } from "@mui/material";
import { Controller, useFormContext } from "react-hook-form";
import Select, { CSSObjectWithLabel } from "react-select";
import { Palette } from "../../../../styles/theme";
import Option from "./Option";

type IFormInputProps = {
placeholder?: string;
name: string;
options: Array<any>;
defaultValue?: string[] | undefined;
isMulti?: boolean;
getOptionLabel: (option: any) => string;
getOptionValue: (option: any) => string;
helperText?: string | undefined;
disabled?: boolean;
onHandleChange?: (val: any) => void;
closeMenuOnSelect?: boolean;
hideSelectedOptions?: boolean;
// menuPortalTarget: HTMLElement | undefined;
};

const ControlledSelectV2: React.ForwardRefRenderFunction<
HTMLDivElement,
IFormInputProps
> = (
{
placeholder,
name,
options,
getOptionLabel,
getOptionValue,
isMulti,
disabled,
helperText,
onHandleChange,
// menuPortalTarget,
closeMenuOnSelect,
hideSelectedOptions,
defaultValue,
...otherProps
},
ref
) => {
const [selectedOptions, setSelectedOptions] = React.useState<any>([]);

React.useEffect(() => {
setSelectedOptions(Array.from(new Set<string>(defaultValue)));
}, [defaultValue]);

const handleChange = (item: any) => {
const selected: Array<any> = [];
item.map((o: any) => {
const val = getOptionValue(o);
selected.push(val);
});
setSelectedOptions(Array.from(new Set<string>(selected)));
};

const {
control,
formState: { errors, defaultValues },
} = useFormContext();
return (
<Controller
control={control}
name={name}
defaultValue={defaultValues?.[name] || ""}
render={({ field }) => {
const { onChange, value, ref } = field;
return (
<>
<Select
placeholder={placeholder}
{...field}
ref={ref}
{...otherProps}
options={options}
menuPosition="fixed"
getOptionValue={getOptionValue}
getOptionLabel={getOptionLabel}
isSearchable={true}
isDisabled={!!disabled}
isClearable={false}
hideSelectedOptions={!!hideSelectedOptions}
closeMenuOnSelect={!!closeMenuOnSelect}
filterProps={{
selectedOptions,
options,
getOptionLabel,
getOptionValue,
}}
components={{
Option,
MultiValueRemove: () => null,
}}
value={options.filter((c) => {
if (isMulti && value) {
return (value as any[])
.map((p) => p.toString())
.includes(getOptionValue(c));
}
return getOptionValue(c) === value?.toString();
})}
isMulti={isMulti}
onChange={(val: any) => {
let v;
if (isMulti) v = val.map((v: any) => getOptionValue(v));
else v = getOptionValue(val);
if (onHandleChange !== undefined) onHandleChange(v);
handleChange(val);
return onChange(v);
}}
menuPortalTarget={document.body}
styles={{
control: (baseStyles, state) => {
return {
...baseStyles,
borderColor: !!errors[name]
? "#d32f2f"
: Palette.neutral.accent.light,
borderWidth: "2px",
fontSize: "16px",
lineHeight: "24px",
backgroundColor: !!disabled
? Palette.neutral.bg.dark
: Palette.white,
fontWeight: "400",
"&:hover": {
borderColor: Palette.primary.accent.light,
},
};
},
menuPortal: (base: CSSObjectWithLabel) => ({
...base,
zIndex: 99999,
fontSize: "1rem",
}),
multiValue(base, props) {
return {
...base,
padding: "0px 4px 0px 2px",
};
},
}}
></Select>
{helperText && (
<FormHelperText
error={true}
className="MuiFormHelperText-sizeSmall"
style={{ marginInline: "14px" }}
>
{String(errors[name]?.message || "")}
</FormHelperText>
)}
</>
);
}}
/>
);
};

export default React.forwardRef(ControlledSelectV2);
12 changes: 8 additions & 4 deletions epictrack-web/src/components/shared/filterSelect/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ declare module "react-select/dist/declarations/src/Select" {
// Marking as optional here to not raise errors for ControlledSelect
// Make sure to add for FilterSelect
filterProps?: {
applyFilters: () => void;
clearFilters: () => void;
applyFilters?: () => void;
clearFilters?: () => void;
selectedOptions: any[];
onCancel: () => void;
variant: "inline" | "bar";
options?: any[];
onCancel?: () => void;
variant?: "inline" | "bar";
getOptionLabel?: (option: any) => string;
getOptionValue?: (option: any) => string;
label?: string;
};
filterAppliedCallback?: (value?: string[] | string) => void;
filterClearedCallback?: (value?: [] | "") => void;
Expand Down
2 changes: 1 addition & 1 deletion epictrack-web/src/components/workPlan/event/EventList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ const EventList = () => {
</Grid>
<TrackDialog
open={showTaskForm}
dialogTitle="Add Task"
dialogTitle={taskEvent ? taskEvent?.name : "Add Task"}
//onClose={(event, reason) => onDialogClose(event, reason)}
disableEscapeKeyDown
fullWidth
Expand Down
Loading

0 comments on commit 6ddc76d

Please sign in to comment.