Skip to content

Instantly share code, notes, and snippets.

@Sharan-ram
Created May 24, 2019 12:43
Show Gist options
  • Save Sharan-ram/6642fb7634a7bea6a7909dd593ab4590 to your computer and use it in GitHub Desktop.
Save Sharan-ram/6642fb7634a7bea6a7909dd593ab4590 to your computer and use it in GitHub Desktop.
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { get } from 'lodash';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import Card from '@material-ui/core/Card';
import CardHeader from '@material-ui/core/CardHeader';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Tab from '@material-ui/core/Tab';
import Tabs from '@material-ui/core/Tabs';
import TextField from '@material-ui/core/TextField';
import withStyles from '@material-ui/core/styles/withStyles';
import OfflineIcon from 'images/ic-offline-mini.svg';
import CardReader from 'components/Capabilities/CardReader';
import ContactSensor from 'components/Capabilities/ContactSensor';
import Relay from 'components/Capabilities/Relay';
import RequestToExit from 'components/Capabilities/RequestToExit';
import PhoneReader from 'components/Capabilities/PhoneReader';
import { validate, Action } from 'components/Capabilities/utils';
import {
AutoComplete,
copyTextToClipboard,
ButtonText,
CardTitle,
Divider,
FormErrors,
HelpText,
SecondaryText,
ListPlaceholder,
Pager,
useForm
} from 'components/UtilsNext';
import {
relayActions,
lockActions,
beaconActions,
cardReaderActions,
contactSensorActions,
requestToExitActions,
gatewayActions
} from 'appredux/actions/api';
import { showMessage } from 'appredux/actions/ui';
import {
card,
cardContent,
cardContentForm,
cardActions
} from 'themes/default';
const commonControllerCapabalityStyles = {
listPlaceholder: {
padding: '0px 4px 20px 4px'
},
helpText: {
padding: '13px 0px 13px 14px'
}
};
const updateGatewayStyles = {
avatar: {
width: 24,
height: 24
},
card,
cardContentForm,
list: {
padding: 0
},
gutters: {
paddingLeft: 0
},
listItemText: {
padding: 0
},
cardActions,
copy: {
alignSelf: 'center'
},
deviceId: {
display: 'flex'
}
};
function _UpdateGateway({ classes, gateway, form, children }) {
const copy = () => {
copyTextToClipboard(gateway.deviceId);
};
const renderDeviceId = () => {
const deviceIdProps = {
...form.inputProps('deviceId'),
disabled: true,
fullWidth: true,
margin: 'normal'
};
return (
<div className={classes.deviceId}>
<TextField {...deviceIdProps} />
<Button className={classes.copy} onClick={copy}>
<ButtonText>Copy</ButtonText>
</Button>
</div>
);
};
const nameProps = {
...form.inputProps('name'),
onChange: form.onInputChange,
fullWidth: true,
margin: 'normal'
};
const submitProps = {
disabled: !form.isSubmittable(),
onClick: form.onSubmit,
color: 'primary',
variant: 'contained'
};
const listItemTextProps = {
className: classes.listItemText,
secondary: (
<SecondaryText>
This device is offline.Click here to get help on how to bring it back
online.
</SecondaryText>
)
};
return (
<Card className={classes.card}>
<CardHeader title={<CardTitle>{gateway.name}</CardTitle>} />
<Divider />
<CardContent className={classes.cardContentForm}>
<TextField {...nameProps} />
{!gateway.online && (
<List className={classes.list}>
<ListItem classes={{ gutters: classes.gutters }}>
<ListItemIcon>
<Avatar className={classes.avatar} src={OfflineIcon} />
</ListItemIcon>
<ListItemText {...listItemTextProps} />
</ListItem>
</List>
)}
{renderDeviceId()}
{children && children()}
</CardContent>
<FormErrors form={form} except={['name']} />
<Divider />
<CardActions className={classes.cardActions}>
<Button {...submitProps}>
<ButtonText>Save</ButtonText>
</Button>
</CardActions>
</Card>
);
}
const UpdateGateway = withStyles(updateGatewayStyles)(_UpdateGateway);
const cardStyles = {
card,
cardContent
};
function _CardReaders({ form, cardReaders, locks, updateCardReader }) {
const listPlaceholderProps = {
list: cardReaders,
placeholder: 'This device has no card readers.'
};
return (
<ListPlaceholder {...listPlaceholderProps}>
{cardReaders =>
cardReaders.map(cardReader => {
const cardReaderProps = {
form,
key: `cardReader-${cardReader.id}`,
cardReader,
locks,
updateCardReader
};
return <CardReader {...cardReaderProps} />;
})
}
</ListPlaceholder>
);
}
const CardReaders = withStyles(cardStyles)(_CardReaders);
function _PhoneReaders({ form, beacons, locks, updateBeacon }) {
const listPlaceholderProps = {
list: beacons,
placeholder: 'This device has no phone readers.'
};
return (
<ListPlaceholder {...listPlaceholderProps}>
{beacons =>
beacons.map(beacon => {
const phoneReaderProps = {
form,
key: `cardReader-${beacon.id}`,
beacon,
locks,
updateBeacon
};
return <PhoneReader {...phoneReaderProps} />;
})
}
</ListPlaceholder>
);
}
const PhoneReaders = withStyles(cardStyles)(_PhoneReaders);
function _Relays({
classes,
relays,
getRelays,
pagination,
setPage,
locks,
updateRelay
}) {
const listPlaceholderProps = {
className: classes.listPlaceholder,
list: relays,
placeholder: 'This device has no relays.'
};
return (
<>
<CardContent>
<HelpText className={classes.helpText}>
Relays represent the functionality of the device to trigger door
release devices.
</HelpText>
<ListPlaceholder {...listPlaceholderProps}>
{relays =>
relays.map(relay => {
const relayProps = {
key: `relay-${relay.id}`,
relay,
locks,
updateRelay
};
return <Relay {...relayProps} />;
})
}
</ListPlaceholder>
</CardContent>
<Divider />
<Pager
pagination={pagination}
setPage={page => setPage(page, getRelays)}
/>
</>
);
}
const Relays = withStyles(commonControllerCapabalityStyles)(_Relays);
function _ContactSensors({
classes,
contactSensors,
pagination,
getContactSensors,
setPage,
locks,
updateContactSensor
}) {
const listPlaceholderProps = {
className: classes.listPlaceholder,
list: contactSensors,
placeholder: 'This device has no contact sensors.'
};
return (
<>
<CardContent>
<HelpText className={classes.helpText}>
Contact sensors represent the functionality of your device to detect
when doors and windows are opened.
</HelpText>
<ListPlaceholder {...listPlaceholderProps}>
{contactSensors =>
contactSensors.map(contactSensor => {
const contactSensorProps = {
key: `contactSensor-${contactSensor.id}`,
contactSensor,
locks,
updateContactSensor
};
return <ContactSensor {...contactSensorProps} />;
})
}
</ListPlaceholder>
</CardContent>
<Divider />
<Pager
pagination={pagination}
setPage={page => setPage(page, getContactSensors)}
/>
</>
);
}
const ContactSensors = withStyles(commonControllerCapabalityStyles)(
_ContactSensors
);
function _RequestToExits({
classes,
requestToExits,
getRequestToExits,
pagination,
setPage,
locks,
updateRequestToExit
}) {
const listPlaceholderProps = {
className: classes.listPlaceholder,
list: requestToExits,
placeholder: 'This device has no request to exits.'
};
return (
<>
<CardContent>
<HelpText className={classes.helpText}>
Requests to Exit represent the functionality of the device to receive
request to exit signals, e.g. from buttons or motion sensors. In most
use cases this will lead to the door being unlocked.
</HelpText>
<ListPlaceholder {...listPlaceholderProps}>
{requestToExits =>
requestToExits.map(requestToExit => {
const requestToExitProps = {
key: `requestToExit-${requestToExit.id}`,
requestToExit,
locks,
updateRequestToExit
};
return <RequestToExit {...requestToExitProps} />;
})
}
</ListPlaceholder>
</CardContent>
<Divider />
<Pager
pagination={pagination}
setPage={page => setPage(page, getRequestToExits)}
/>
</>
);
}
const RequestToExits = withStyles(commonControllerCapabalityStyles)(
_RequestToExits
);
function _Reader({
form,
locks,
cardReaders,
beacons,
gatewayId,
getBeacons,
getCardReaders,
updateBeacon,
updateCardReader
}) {
useEffect(() => {
getCardReaders({ query: { gatewayId, limit: 10 } });
}, [gatewayId, getCardReaders]);
useEffect(() => {
getBeacons({ query: { gatewayId, limit: 10 } });
}, [gatewayId, getBeacons]);
if (!cardReaders || !beacons) return null;
const cardReadersProps = {
form,
cardReaders: cardReaders.data,
updateCardReader,
locks
};
const phoneReadersProps = {
form,
beacons: beacons.data,
updateBeacon,
locks
};
return (
<>
<Divider />
<CardReaders {...cardReadersProps} />
<Divider />
<PhoneReaders {...phoneReadersProps} />
</>
);
}
const Reader = connect(
state => ({
cardReaders: get(state, 'api.cardReaders.collection'),
beacons: get(state, 'api.beacons.collection')
}),
{
getBeacons: beaconActions.retrieve,
getCardReaders: cardReaderActions.retrieve,
updateBeacon: beaconActions.update,
updateCardReader: cardReaderActions.update
}
)(_Reader);
const controllerStyles = {
tab: {
height: 60
},
card
};
function _Controller({
classes,
contactSensors,
relays,
requestToExits,
gatewayId,
locks,
getRelays,
getContactSensors,
getRequestToExits,
updateRelay,
updateContactSensor,
updateRequestToExit
}) {
const [value, setValue] = useState(0);
useEffect(() => {
getRelays({ query: { gatewayId, limit: 10 } });
}, [gatewayId, getRelays]);
useEffect(() => {
getContactSensors({ query: { gatewayId, limit: 10 } });
}, [gatewayId, getContactSensors]);
useEffect(() => {
getRequestToExits({ query: { gatewayId, limit: 10 } });
}, [gatewayId, getRequestToExits]);
const setPage = (page, action) => {
if (page > 1) {
action({
query: {
gatewayId,
limit: 10,
offset: (page - 1) * 10
}
});
} else {
action({ query: { gatewayId, limit: 10 } });
}
};
const contactSensorsProps = {
contactSensors: contactSensors.data,
updateContactSensor,
locks,
pagination: contactSensors.pagination,
getContactSensors,
setPage
};
const relaysProps = {
relays: relays.data,
updateRelay,
locks,
pagination: relays.pagination,
getRelays,
setPage
};
const requestToExitsProps = {
requestToExits: requestToExits.data,
updateRequestToExit,
locks,
pagination: requestToExits.pagination,
getRequestToExits,
setPage
};
const tabsProps = {
centered: true,
indicatorColor: 'primary',
onChange: (_, value) => setValue(value),
value
};
const titles = ['CONTACT SENSORS', 'RELAYS', 'REQUEST TO EXITS'];
const tabs = titles.map((title, index) => (
<Tab
key={title}
label={<ButtonText>{title}</ButtonText>}
className={classes.tab}
value={index}
/>
));
return (
<Card className={classes.card}>
<Tabs {...tabsProps}>{tabs}</Tabs>
<Divider />
{value === 0 && <ContactSensors {...contactSensorsProps} />}
{value === 1 && <Relays {...relaysProps} />}
{value === 2 && <RequestToExits {...requestToExitsProps} />}
</Card>
);
}
const Controller = (() => {
const C = connect(
state => ({
contactSensors: get(state, 'api.contactSensors.collection', {}),
relays: get(state, 'api.relays.collection', {}),
requestToExits: get(state, 'api.requestToExits.collection', {})
}),
{
getRelays: relayActions.retrieve,
getContactSensors: contactSensorActions.retrieve,
getRequestToExits: requestToExitActions.retrieve,
updateRelay: relayActions.update,
updateContactSensor: contactSensorActions.update,
updateRequestToExit: requestToExitActions.update
}
)(_Controller);
return withStyles(controllerStyles)(C);
})();
function _ShowPage({
match: {
params: { gatewayId, placeId }
},
gateway,
locks,
getGateway,
// updateGateway,
getLocks
}) {
useEffect(() => {
getGateway({ params: { id: gatewayId } });
}, [gatewayId, getGateway]);
useEffect(() => {
getLocks({ query: { placeId, limit: 250 } });
}, [getLocks, placeId]);
if (!gateway.model) return null;
const onSubmit = values => ({
PARAMS: { id: gateway.id },
data: {
gateway: {
name: values.name
}
}
});
const onSuccess = () => {
showMessage('Device updated successfully.');
getGateway({ params: { id: gatewayId } });
};
// const formProps = {
// name: 'updateGateways',
// defaultValues: {
// name: gateway.name || '',
// deviceId: gateway.deviceId
// },
// onSubmit,
// validate
// };
const form = useForm({
defaults: {
name: gateway.name || '',
deviceId: gateway.deviceId
},
onSubmit,
onSuccess,
validate,
method: 'PUT',
path: `/gateways/${gateway.id}`
});
const lockNameProps = {
clear: true,
dataSource: locks,
dataSourceConfig: { value: 'id', text: 'name' },
// defaultSelectedItem: defaultLock,
TextFieldProps: {
...form.errorProps('lock'),
margin: 'normal',
placeholder: 'Search Lock',
label: 'Lock',
fullWidth: true
},
onChange: lock => {
form.onInputChange('lock', lock);
}
};
const hardwareType = gateway.model.includes('reader')
? 'reader'
: 'controller';
return (
<>
<form onSubmit={form.handleSubmit}>
<UpdateGateway form={form} gateway={gateway}>
{() =>
(hardwareType === 'reader' ? (
<>
<AutoComplete {...lockNameProps} />
<HelpText>
Start typing the name of the lock you wish to assign to this
reader.
</HelpText>
<Action
label="Action on Access"
SelectProps={{ ...form.selectProps('actionOnAccess') }}
/>
<Reader gatewayId={gatewayId} locks={locks} form={form} />
</>
) : null)
}
</UpdateGateway>
</form>
{hardwareType !== 'reader' && (
<Controller gatewayId={gatewayId} locks={locks} />
)}
</>
);
}
const ShowPage = connect(
state => ({
gateway: get(state, 'api.gateways.member', {}),
locks: get(state, 'api.locks.collection.data', [])
}),
{
getGateway: gatewayActions.retrieve,
updateGateway: gatewayActions.update,
getLocks: lockActions.retrieve
}
)(_ShowPage);
export default ShowPage;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment