Skip to content

Commit

Permalink
Merge pull request #329 from RyanNerd/paperless
Browse files Browse the repository at this point in the history
⛲ feat Document Management (upload/download files)
  • Loading branch information
RyanNerd authored Mar 6, 2022
2 parents c11b567 + bc0539f commit b1c15e7
Show file tree
Hide file tree
Showing 26 changed files with 1,121 additions and 121 deletions.
20 changes: 11 additions & 9 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,22 +136,24 @@ module.exports = {
'error',
{
allowList: {
props: true,
CustomMenuProps: true,
dropdownProps: true,
IDropdownProps: true,
IProps: true,
IModalProps: true,
TProps: true,
ITitleProps: true,
IProps: true,
ITableProps: true,
tableProps: true,
yesButtonProps: true,
noButtonProps: true,
ITitleProps: true,
TProps: true,
defaultNoButtonProps: true,
defaultYesButtonProps: true,
doc: true,
dropdownProps: true,
idx: true,
n: true,
ref: true
noButtonProps: true,
props: true,
ref: true,
tableProps: true,
yesButtonProps: true
}
}
],
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rxchart",
"version": "0.12.0",
"version": "0.13.0",
"private": false,
"dependencies": {
"bootstrap": "^4.6.1",
Expand Down Expand Up @@ -44,6 +44,7 @@
"@types/react-dom": "^17.0.11",
"@typescript-eslint/eslint-plugin": "^5.1.0",
"@typescript-eslint/parser": "^5.1.0",
"@types/wicg-file-system-access": "latest",
"eslint": "^7.11.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-jsdoc": "^36.1.1",
Expand Down
1 change: 1 addition & 0 deletions src/components/Pages/ClientPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ const ClientPage = (props: IProps): JSX.Element | null => {
await setActiveClient({
...activeClient,
clientInfo: clientLoad.clientInfo,
fileList: clientLoad.fileList,
drugLogList: clientLoad.drugLogList,
medicineList: clientLoad.medicineList,
pillboxList: clientLoad.pillboxList,
Expand Down
100 changes: 100 additions & 0 deletions src/components/Pages/Grids/FileGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import Button from 'react-bootstrap/Button';
import Table, {TableProps} from 'react-bootstrap/Table';
import React from 'reactn';
import {FileRecord} from 'types/RecordTypes';
import {randomString} from 'utility/common';

interface IProps extends TableProps {
[key: string]: unknown;
onDownload: (fileRecord: FileRecord) => void;
onDelete: (fileRecord: FileRecord) => void;
onEdit: (fileRecord: FileRecord) => void;
fileList: FileRecord[];
}

interface ITableProps extends TableProps {
onDownload: unknown;
onDelete: unknown;
fileList: unknown;
onEdit: unknown;
}

/**
* FileGrid
* @param {IProps} props The props for this component
*/
const FileGrid = (props: IProps): JSX.Element | null => {
const {fileList, onDownload, onDelete, onEdit} = props;

// No render if there isn't anything to render
if (!fileList || fileList.length === 0) return null;

/**
* Child component for the table for each DocumentRecord object.
* @param {FileRecord} file The Document row object
*/
const FileRow = (file: FileRecord): JSX.Element | null => {
const domId = file.Id ? file.Id : randomString();

return (
<tr key={`file-grid-row-${domId}`} id={`file-grid-row-${domId}`}>
<td style={{verticalAlign: 'middle'}}>{file.FileName}</td>
<td style={{verticalAlign: 'middle'}}>{file.Description}</td>
<td style={{verticalAlign: 'middle'}}>{file.MediaType}</td>
<td style={{verticalAlign: 'middle'}}>{file.Size}</td>
<td style={{textAlign: 'center', verticalAlign: 'middle'}}>
<Button id={`file-grid-edit-btn-${domId}`} onClick={() => onEdit(file)} size="sm" variant="info">
Edit
</Button>
</td>
<td style={{textAlign: 'center', verticalAlign: 'middle'}}>
<Button
id={`file-grid-download-btn-${domId}`}
onClick={() => onDownload(file)}
size="sm"
variant="info"
>
Download
</Button>
</td>
<td style={{textAlign: 'center', verticalAlign: 'middle'}}>
<Button
id={`file-grid-delete-btn-${domId}`}
onClick={() => onDelete(file)}
size="sm"
variant="outline-danger"
>
<span role="img" aria-label="delete">
🗑️
</span>
</Button>
</td>
</tr>
);
};

const tableProps = {...props} as ITableProps;
delete tableProps.onDelete;
delete tableProps.onDownload;
delete tableProps.onEdit;
delete tableProps.fileList;

return (
<Table style={{wordWrap: 'break-word'}} {...tableProps} bordered hover size="sm" striped>
<thead>
<tr>
<th>Filename</th>
<th>Description</th>
<th>Type</th>
<th>Size</th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>{fileList.map((p) => FileRow(p))}</tbody>
</Table>
);
};

export default FileGrid;
35 changes: 10 additions & 25 deletions src/components/Pages/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ClientPage from 'components/Pages/ClientPage';
import SettingsPage from 'components/Pages/SettingsPage';
import ManagementPage from 'components/Pages/ManagementPage';
import RxPage from 'components/Pages/RxPage';
import {ReactNode} from 'react';
import Tab from 'react-bootstrap/Tab';
import Tabs from 'react-bootstrap/Tabs';
Expand All @@ -8,9 +9,6 @@ import React, {useEffect, useGlobal, useMemo} from 'reactn';
import {IPreferences} from 'reactn/default';
import DiagnosticPage from './DiagnosticPage';
import LoginPage from './LoginPage';
import ManageDrugPage from './ManageDrugPage';
import ManageOtcPage from './ManageOtcPage';
import MedicinePage from './MedicinePage';

interface ITitleProps {
activeKey: string;
Expand Down Expand Up @@ -49,10 +47,10 @@ const LandingPage = (props: IProps) => {

// Observer to show / hide tabs based on if logged in and if a client has been selected
useEffect(() => {
['resident', 'medicine', 'manage', 'manage-otc'].map((tab) => {
['resident', 'medicine', 'management'].map((tab) => {
const element = document.getElementById('landing-page-tabs-tab-' + tab);
if (element) {
if (tab === 'resident' || tab === 'manage-otc') element.style.display = apiKey ? 'block' : 'none';
if (tab === 'resident' || tab === 'management') element.style.display = apiKey ? 'block' : 'none';
else element.style.display = apiKey && activeClient ? 'block' : 'none';
}
});
Expand All @@ -62,13 +60,9 @@ const LandingPage = (props: IProps) => {
* Memoized pages to reduce number of re-renders
*/
const medicinePage = useMemo(() => {
return <MedicinePage activeTabKey={activeTabKey} preferences={preferences} />;
return <RxPage activeTabKey={activeTabKey} preferences={preferences} />;
}, [activeTabKey, preferences]);

const manageDrugPage = useMemo(() => {
return <ManageDrugPage activeTabKey={activeTabKey} />;
}, [activeTabKey]);

const clientPage = useMemo(() => {
return <ClientPage activeTabKey={activeTabKey} clientSelected={() => setActiveTabKey('medicine')} />;
}, [activeTabKey, setActiveTabKey]);
Expand Down Expand Up @@ -110,33 +104,24 @@ const LandingPage = (props: IProps) => {
<Tab disabled={!apiKey} eventKey="resident" title={<Title activeKey="resident">Clients</Title>}>
<Tab.Content style={{marginLeft: 0}}>{clientPage}</Tab.Content>
</Tab>

<Tab disabled={!apiKey || !activeClient} eventKey="medicine" title={<Title activeKey="medicine">Rx</Title>}>
{activeClient && activeTabKey === 'medicine' && (
<Tab.Content style={{marginLeft: 0}}>{medicinePage}</Tab.Content>
)}
</Tab>
<Tab
disabled={!apiKey || !activeClient}
eventKey="manage"
title={<Title activeKey="manage">Manage Rx</Title>}
>
<Tab.Content>{manageDrugPage}</Tab.Content>
</Tab>
<Tab disabled={!apiKey} eventKey="manage-otc" title={<Title activeKey="manage-otc">Manage OTC</Title>}>

<Tab disabled={!apiKey} eventKey="management" title={<Title activeKey={'management'}>Management</Title>}>
<Tab.Content>
<ManageOtcPage activeTabKey={activeTabKey} />
<ManagementPage />
</Tab.Content>
</Tab>

<Tab disabled={!errorDetails} eventKey="error" title={<Title activeKey="error">Diagnostics</Title>}>
<Tab.Content>
<DiagnosticPage error={errorDetails} dismissErrorAlert={() => window.location.reload()} />
</Tab.Content>
</Tab>
<Tab eventKey="settings" title={<Title activeKey={'settings'}>Preferences</Title>}>
<Tab.Content>
<SettingsPage />
</Tab.Content>
</Tab>
</Tabs>
);
};
Expand Down
8 changes: 4 additions & 4 deletions src/components/Pages/ListGroups/OtcListGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import OtcListGroupGrid from 'components/Pages/Grids/OtcListGroupGrid';
import DisabledSpinner from 'components/Pages/ListGroups/DisabledSpinner';
import {TAB_KEY} from 'components/Pages/MedicinePage';
import {RX_TAB_KEY} from 'components/Pages/RxPage';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import FormGroup from 'react-bootstrap/FormGroup';
Expand All @@ -14,7 +14,7 @@ import ShadowBox from '../Buttons/ShadowBox';

interface IProps {
activeOtc: MedicineRecord | null;
activeRxTab: TAB_KEY;
activeRxTab: RX_TAB_KEY;
disabled?: boolean;
drugLogList: DrugLogRecord[];
editOtcMedicine: (m: MedicineRecord) => void;
Expand Down Expand Up @@ -47,10 +47,10 @@ const OtcListGroup = (props: IProps): JSX.Element | null => {
const lastTakenVariant = lastTaken && lastTaken >= 8 ? 'primary' : getLastTakenVariant(lastTaken);
const searchReference = useRef<HTMLInputElement>(null);

const [activeRxTab, setActiveRxTab] = useState<TAB_KEY>(props.activeRxTab);
const [activeRxTab, setActiveRxTab] = useState<RX_TAB_KEY>(props.activeRxTab);
useEffect(() => {
setActiveRxTab(props.activeRxTab);
if (props.activeRxTab === TAB_KEY.OTC) searchReference?.current?.focus();
if (props.activeRxTab === RX_TAB_KEY.OTC) searchReference?.current?.focus();
}, [activeRxTab, props.activeRxTab]);

// Filter the otcList by the search textbox value
Expand Down
64 changes: 64 additions & 0 deletions src/components/Pages/ManagementPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import ManageOtc from 'components/Pages/ManagementTabs/ManageOtc';
import Settings from 'components/Pages/ManagementTabs/Settings';
import Tab from 'react-bootstrap/Tab';
import Tabs from 'react-bootstrap/Tabs';
import ToggleButton from 'react-bootstrap/ToggleButton';
import React, {useState} from 'reactn';

const ManagementPage = () => {
const [activeManagementKey, setActiveManagementKey] = useState('otc');
return (
<Tabs
activeKey={activeManagementKey}
defaultActiveKey="otc"
onSelect={(s) => setActiveManagementKey(s || 'otc')}
>
<Tab
eventKey="otc"
title={
<ToggleButton
checked={activeManagementKey === 'otc'}
id="management-radio-btn-otc"
key="management-radio-btn-otc"
name="radio-management"
onChange={() => setActiveManagementKey('otc')}
size="sm"
type="radio"
value="otc"
variant="outline-success"
>
<span className="ml-2">Manage OTC</span>
</ToggleButton>
}
>
<Tab.Content>
<ManageOtc activeManagementKey={activeManagementKey} />
</Tab.Content>
</Tab>
<Tab
eventKey="settings"
title={
<ToggleButton
checked={activeManagementKey === 'settings'}
id="management-radio-btn-settings"
key="management-radio-btn-settings"
name="radio-management"
onChange={() => setActiveManagementKey('settings')}
size="sm"
type="radio"
value="settings"
variant="outline-success"
>
<span className="ml-2">Settings</span>
</ToggleButton>
}
>
<Tab.Content>
<Settings />
</Tab.Content>
</Tab>
</Tabs>
);
};

export default ManagementPage;
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import Form from 'react-bootstrap/Form';
import Row from 'react-bootstrap/Row';
import React, {useEffect, useGlobal, useRef, useState} from 'reactn';
import {DrugLogRecord, MedicineRecord, newMedicineRecord} from 'types/RecordTypes';
import MedicineEdit from './Modals/MedicineEdit';
import MedicineEdit from 'components/Pages/Modals/MedicineEdit';

interface IProps {
activeTabKey: string;
activeManagementKey: string;
}

/**
* ManageOtcPage - UI for Displaying, editing and adding OTC drugs
* ManageOtc - UI for Displaying, editing and adding OTC drugs
* @param {IProps} props The props for the component
*/
const ManageOtcPage = (props: IProps): JSX.Element | null => {
const ManageOtc = (props: IProps): JSX.Element | null => {
const [allowDelete, setAllowDelete] = useState(false);
const [medicineInfo, setMedicineInfo] = useState<MedicineRecord | null>(null);
const [mm] = useGlobal('medicineManager');
Expand All @@ -27,7 +27,7 @@ const ManageOtcPage = (props: IProps): JSX.Element | null => {
const [searchText, setSearchText] = useState('');
const [showDeleteMedicine, setShowDeleteMedicine] = useState(0);
const [showMedicineEdit, setShowMedicineEdit] = useState(false);
const activeTabKey = props.activeTabKey;
const activeTabKey = props.activeManagementKey;

const focusReference = useRef<HTMLInputElement>(null);
useEffect(() => {
Expand All @@ -52,7 +52,7 @@ const ManageOtcPage = (props: IProps): JSX.Element | null => {
}, [otcList, searchText]);

// If this tab isn't active then don't render
if (activeTabKey !== 'manage-otc') return null;
if (activeTabKey !== 'otc') return null;

/**
* Given a MedicineRecord object Update or Insert the record and rehydrate the global otcList
Expand Down Expand Up @@ -167,4 +167,4 @@ const ManageOtcPage = (props: IProps): JSX.Element | null => {
);
};

export default ManageOtcPage;
export default ManageOtc;
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, {useEffect, useGlobal} from 'reactn';
import 'styles/neumorphism/settings.css';
import {setStickyState} from 'utility/common';

const SettingsPage = () => {
const Settings = () => {
const [preferences, setPreferences] = useGlobal('preferences');
useEffect(() => {
if (preferences) {
Expand Down Expand Up @@ -50,4 +50,4 @@ const SettingsPage = () => {
);
};

export default SettingsPage;
export default Settings;
Loading

0 comments on commit b1c15e7

Please sign in to comment.