Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Asset Class field and Improve Asset Type option's descriptions #2653

Merged
merged 13 commits into from
Jun 23, 2022
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@babel/core": "^7.14.3",
"@date-io/date-fns": "^1.3.13",
"@glennsl/bs-json": "^5.0.3",
"@headlessui/react": "^1.6.4",
"@loadable/component": "^5.15.0",
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
Expand Down
6 changes: 5 additions & 1 deletion src/Components/Assets/AssetTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export interface AssetLocationObject {
};
}

export type AssetType = "INTERNAL" | "EXTERNAL";
export type AssetClass = "ONVIF" | "HL7MONITOR";

export interface AssetData {
id: string;
name: string;
Expand All @@ -21,7 +24,8 @@ export interface AssetData {
modified_date: string;
serial_number: string;
warranty_details: string;
asset_type: "INTERNAL" | "EXTERNAL";
asset_type: AssetType;
asset_class?: AssetClass;
location_object: AssetLocationObject;
status: "ACTIVE" | "TRANSFER_IN_PROGRESS";
vendor_name: string;
Expand Down
116 changes: 116 additions & 0 deletions src/Components/Common/components/SelectMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { Fragment } from "react";
import { Listbox, Transition } from "@headlessui/react";
import { Check, KeyboardArrowDown } from "@material-ui/icons";
import classNames from "classnames";

type Props<T> = {
options: {
title: string;
description?: string;
value: T;
}[];
onSelect: (value: T) => void;
selected?: T;
label?: string;
};

export default function SelectMenu<T>(props: Props<T>) {
const options = props.options.map((option) => {
return {
...option,
current: option.value === props.selected,
};
});

const selected = options.find((option) => option.current) || options[0];

return (
<Listbox
value={selected}
onChange={(selection) => {
props.onSelect(selection.value);
}}
>
{({ open }) => (
<>
<Listbox.Label className="sr-only">{props.label}</Listbox.Label>
<div className="relative">
<div className="inline-flex shadow-sm rounded-md divide-x divide-primary-600">
<div className="relative z-0 inline-flex shadow-sm rounded-md divide-x divide-primary-600">
<div className="relative inline-flex items-center bg-primary-500 py-2 pl-3 pr-4 border border-transparent rounded-l-md shadow-sm text-white">
{selected.value && (
<Check className="h-5 w-5" aria-hidden="true" />
)}
<p className="ml-2.5 text-sm font-medium">{selected.title}</p>
</div>
<Listbox.Button className="relative inline-flex items-center bg-primary-500 p-2 rounded-l-none rounded-r-md text-sm font-medium text-white hover:bg-primary-600 focus:outline-none focus:z-10 ">
<span className="sr-only">Change published status</span>
<KeyboardArrowDown
className="h-5 w-5 text-white"
aria-hidden="true"
/>
</Listbox.Button>
</div>
</div>

<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="origin-top-right absolute z-10 left-0 mt-2 w-72 rounded-md shadow-lg overflow-hidden bg-white divide-y divide-gray-200 ring-1 ring-black ring-opacity-5 focus:outline-none">
{options.map((option) => (
<Listbox.Option
key={option.title}
className={({ active }) =>
classNames(
active ? "text-white bg-primary-500" : "text-gray-900",
"cursor-default select-none relative p-4 text-sm"
)
}
value={option}
>
{({ selected, active }) => (
<div className="flex flex-col">
<div className="flex justify-between">
<p
className={
selected ? "font-semibold" : "font-normal"
}
>
{option.title}
</p>
{selected ? (
<span
className={
active ? "text-white" : "text-primary-500"
}
>
<Check className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</div>
{option.description && (
<p
className={classNames(
active ? "text-primary-200" : "text-gray-500",
"mt-2"
)}
>
{option.description}
</p>
)}
</div>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
);
}
96 changes: 54 additions & 42 deletions src/Components/Facility/AssetCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,19 @@ import {
PhoneNumberField,
ErrorHelperText,
} from "../Common/HelperInputFields";
import { AssetData } from "../Assets/AssetTypes";
import { AssetClass, AssetData, AssetType } from "../Assets/AssetTypes";
import loadable from "@loadable/component";
import { LocationOnOutlined } from "@material-ui/icons";
import { navigate } from "raviger";
import QrReader from "react-qr-reader";
import { parseQueryParams } from "../../Utils/primitives";
import SelectMenu from "../Common/components/SelectMenu";
const Loading = loadable(() => import("../Common/Loading"));

const initError: any = {
name: "",
asset_type: "",
asset_class: "",
description: "",
is_working: "",
serial_number: "",
Expand Down Expand Up @@ -82,18 +84,19 @@ const AssetCreate = (props: AssetProps) => {
const { facilityId, assetId } = props;

const [state, dispatch] = useReducer(asset_create_reducer, initialState);
const [name, setName] = useState<string>("");
const [asset_type, setAssetType] = useState<string>("");
const [not_working_reason, setNotWorkingReason] = useState<string>("");
const [description, setDescription] = useState<string>("");
const [is_working, setIsWorking] = useState<string>("0");
const [serial_number, setSerialNumber] = useState<string>("");
const [warranty_details, setWarrantyDetails] = useState<string>("");
const [vendor_name, setVendorName] = useState<string>("");
const [support_name, setSupportName] = useState<string>("");
const [support_phone, setSupportPhone] = useState<string>("");
const [support_email, setSupportEmail] = useState<string>("");
const [location, setLocation] = useState<string>("");
const [name, setName] = useState("");
const [asset_type, setAssetType] = useState<AssetType>();
const [asset_class, setAssetClass] = useState<AssetClass>();
const [not_working_reason, setNotWorkingReason] = useState("");
const [description, setDescription] = useState("");
const [is_working, setIsWorking] = useState("0");
const [serial_number, setSerialNumber] = useState("");
const [warranty_details, setWarrantyDetails] = useState("");
const [vendor_name, setVendorName] = useState("");
const [support_name, setSupportName] = useState("");
const [support_phone, setSupportPhone] = useState("");
const [support_email, setSupportEmail] = useState("");
const [location, setLocation] = useState("");
const [isLoading, setIsLoading] = useState(false);
const dispatchAction: any = useDispatch();
const [locations, setLocations] = useState([]);
Expand Down Expand Up @@ -129,6 +132,7 @@ const AssetCreate = (props: AssetProps) => {
setDescription(asset.description);
setLocation(asset.location_object.id);
setAssetType(asset.asset_type);
setAssetClass(asset.asset_class);
setIsWorking(String(asset.is_working));
setNotWorkingReason(asset.not_working_reason);
setSerialNumber(asset.serial_number);
Expand Down Expand Up @@ -165,7 +169,7 @@ const AssetCreate = (props: AssetProps) => {
}
return;
case "asset_type":
if (asset_type !== "INTERNAL" && asset_type !== "EXTERNAL") {
if (!asset_type) {
errors[field] = "Field is required";
invalidForm = true;
}
Expand Down Expand Up @@ -208,6 +212,7 @@ const AssetCreate = (props: AssetProps) => {
const data = {
name: name,
asset_type: asset_type,
asset_class: asset_class,
description: description,
is_working: is_working,
not_working_reason: is_working === "true" ? "" : not_working_reason,
Expand Down Expand Up @@ -354,34 +359,41 @@ const AssetCreate = (props: AssetProps) => {
<InputLabel htmlFor="asset-type" id="name=label" required>
Asset Type
</InputLabel>
<SelectField
id="asset-type"
fullWidth
name="asset_type"
placeholder=""
variant="outlined"
margin="dense"
options={[
{
id: "",
name: "Select",
},
{
id: "EXTERNAL",
name: "EXTERNAL",
},
{
id: "INTERNAL",
name: "INTERNAL",
},
]}
optionValue="name"
value={asset_type}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setAssetType(e.target.value)
}
errors={state.errors.asset_type}
/>
<div className="my-2">
<SelectMenu
options={[
{
title: "Internal",
description: "Asset is inside the facility premises.",
value: "INTERNAL",
},
{
title: "External",
description: "Asset is outside the facility premises.",
value: "EXTERNAL",
},
]}
selected={asset_type}
onSelect={setAssetType}
/>
</div>
<ErrorHelperText error={state.errors.asset_type} />
</div>
<div>
<InputLabel htmlFor="asset-class" id="name=label">
Asset Class
</InputLabel>
<div className="my-2">
<SelectMenu
options={[
{ title: "Select", value: undefined },
{ title: "ONVIF Camera", value: "ONVIF" },
{ title: "HL7 Vitals Monitor", value: "HL7MONITOR" },
]}
selected={asset_class}
onSelect={setAssetClass}
/>
</div>
</div>
<div>
<InputLabel htmlFor="location" id="name=label" required>
Expand Down