actually revert this until its NOT broken (#23557)

This commit is contained in:
Contrabang
2023-12-13 20:21:25 -06:00
committed by GitHub
parent 28163dc81d
commit e623109668
13 changed files with 523 additions and 559 deletions
-116
View File
@@ -44,15 +44,12 @@ Make sure to add new items to this list if you document new components.
- [`Table`](#table)
- [`Table.Row`](#tablerow)
- [`Table.Cell`](#tablecell)
- [`Table.Sortable`](#tablesortable)
- [`Tabs`](#tabs)
- [`Tabs.Tab`](#tabstab)
- [`Tooltip`](#tooltip)
- [`tgui/layouts`](#tguilayouts)
- [`Window`](#window)
- [`Window.Content`](#windowcontent)
- [`tgui/interfaces/common`](#tguiinterfacescommon)
- [`RecordsTable`](#recordstable)
## General Concepts
@@ -847,103 +844,6 @@ A straight forward mapping to `<td>` element.
- `collapsing: boolean` - Collapses table cell to the smallest possible size,
and stops any text inside from wrapping.
### `Table.Sortable`
A managed sortable table.
**Props:**
- See inherited props: [Table](#table)
- `columns: Column[]` - A list of data fields to be
used. The order in which columns are specified is the order in which they
appear in the UI.
- `columnDefaults: UnnamedColumn` - Default values for all columns.
`columnDefaults.datum.children` can be used to specify the default children,
but will get overriden by children defined at each column.
`columnDefaults.datum.props` and `columnDefaults.header.props` can be used to
specify additional props for the datum and header respectively.
- `data: object[]` - The data to put into the table.
- `datumID: object => object` - A function which takes in a datum and returns
an id.
- `filter: object[] => object[]` - A function applied to the data before
sorting the table. The `createSearch` function can be applied here.
- `headerRowProps: object` - Props to apply to the header row.
- `datumRowProps: object | object => object` - Props to apply to the data
rows. If a function is specified instead, the data associated with that row
is passed into it.
- `...rest` - The rest of the props are applied on the table.
- `children: Component[]` - Not supported. Do not use.
The column types is defined as such:
```ts
interface UnnamedColumn {
// ? signifies optional
datum?: {
// Pass the children directly or pass a function which takes the value of
// the cell and returns the props. Setting this property overrides the
// default children.
children?: React.ReactNode | ((value: object) => React.ReactNode);
// Pass the additional props directly or pass a function which takes the
// value of the cell and returns the additional props.
props?: CellProps | ((value: object) => CellProps);
};
header?: {
// Additional props for the header cell.
props?: HeaderProps;
};
}
// Inherits properties from UnnamedColumn
type Column = UnnamedColumn & {
id: string;
name: string;
};
```
The following is an example of how to use `Table.Sortable`.
```jsx
const data = [
{ 'account_number': 6224001, 'name': 'Command Account', 'suspended': 'Active', 'money': 7000, },
{ 'account_number': 3099002, 'name': 'Security Account', 'suspended': 'Active', 'money': 14000, },
{ 'account_number': 8652003, 'name': 'Science Account', 'suspended': 'Active', 'money': 7000, },
{ 'account_number': 8422004, 'name': 'Service Account', 'suspended': 'Active', 'money': 7000, },
{ 'account_number': 9853005, 'name': 'Supply Account', 'suspended': 'Active', 'money': 7000, },
{ 'account_number': 1866006, 'name': 'Engineering Account', 'suspended': 'Active', 'money': 7250, },
{ 'account_number': 3811007, 'name': 'Medical Account', 'suspended': 'Active', 'money': 7000, },
{ 'account_number': 3945008, 'name': 'Assistant Account', 'suspended': 'Active', 'money': 4500, },
]
<Table.Sortable
columns={[
{ id: "name", name: "Department Name", },
{ id: "account_number", name: "Account Number", },
{ id: "suspended", name: "Account Status", },
{ id: "money", name: "Account Balance", },
]}
data={data}
datumID={(datum) => datum.account_number}
datumRowProps={(datum) => ({
className: `AccountsUplinkTerminal__listRow--${datum.suspended}`,
})}
datumCellChildren={{
name: (value) => <><Icon name="wallet" /> {value}</>,
}}
/>
```
In the above example, the `columns` prop defines the fields in the data that
are used. `columns.id` is the key or the name of the property on the data,
while `columns.name` is what the user sees on the UI. `datumID` selects which
field in the data to use as the key for render caching. `datumID` can return
anything, but it must be unique.
`datumRowProps` applies a class to a row if the account associated with that
row is suspended, and `datumCellChildren` prepends a wallet to the contents
of the `name` column.
### `Tabs`
Tabs make it easy to explore and switch between different views.
@@ -1068,19 +968,3 @@ Can be scrollable.
- `className: string` - Applies a CSS class to the element.
- `scrollable: boolean` - Shows or hides the scrollbar.
- `children: any` - Main content of your window.
## `tgui/interfaces/common`
## `RecordsTable`
An extension to [`Table.Sortable`](#tablesortable) which provides a search
function and slots for buttons.
**Props:**
- See inherited props: [`Table.Sortable`](#tablesortable)
- `leftButtons: Component` - Optional buttons to add left of the search box.
- `rightButtons: Component` - Optional buttons to add right of the search box.
- `searchPlaceholder: string` - The default text in the search box.
Use a `<Fragment>`(or `<>`) to specify multiple buttons.
+1 -156
View File
@@ -1,8 +1,5 @@
import { classes, pureComponentHooks } from 'common/react';
import { computeBoxClassName, computeBoxProps } from './Box';
import { Component } from 'inferno';
import { Button } from './Button';
import { Icon } from './Icon';
import { Box, computeBoxClassName, computeBoxProps } from './Box';
export const Table = (props) => {
const { className, collapsing, children, ...rest } = props;
@@ -56,159 +53,7 @@ export const TableCell = (props) => {
);
};
const resolveFunctionalProp = (props, ...data) =>
props ? (props instanceof Function ? props(...data) : props) : undefined;
class HoverableIcon extends Component {
constructor() {
super();
this.state = {
hovering: false,
};
this.handleMouseOver = (e) => {
this.setState({ hovering: true });
};
this.handleMouseOut = (e) => {
this.setState({ hovering: false });
};
}
render() {
const { hoverIcon, name, ...rest } = this.props;
const { hovering } = this.state;
return (
<Icon
name={hovering ? hoverIcon : name}
{...rest}
onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
/>
);
}
}
class SortableTable extends Component {
constructor(props) {
super();
this.state = {
// Allow null
sortId: props.sortId === undefined ? props.columns[0].id : props.sortId,
sortOrder: props.sortOrder ?? 1,
};
}
render() {
const {
className,
columnDefaults,
columns,
data,
datumID,
filter,
headerRowProps,
datumRowProps,
...rest
} = this.props;
const { sortId, sortOrder } = this.state;
const columnHeaders = columns.map(
({ id, name, header: { props } = {} }) => {
const {
header: { props: defaultProps },
} = columnDefaults;
return (
<Table.Cell key={id}>
<Button
color={sortId !== id && 'transparent'}
width="100%"
onClick={() => {
if (sortId === id) {
this.setState({
sortOrder: !sortOrder,
});
} else {
this.setState({
sortId: id,
sortOrder: true,
});
}
}}
{...defaultProps}
{...props}
>
{name}
{sortId === id && (
<HoverableIcon
name={sortOrder ? 'sort-up' : 'sort-down'}
hoverIcon="times"
position="absolute"
right={0}
top="50%"
style={{
transform: 'translate(0, -50%)',
}}
onClick={(e) => {
this.setState({
sortId: null,
});
e.preventDefault();
}}
/>
)}
</Button>
</Table.Cell>
);
}
);
const dataRows = (filter ? filter(data) : data)
.sort((a, b) => {
if (sortId) {
const i = sortOrder ? 1 : -1;
return a[sortId].toString().localeCompare(b[sortId].toString()) * i;
} else {
return 0;
}
})
.map((datum) => {
let cells = columns.map(
({ id, name, datum: { props, children } = {} }) => {
const {
datum: { children: defaultChildren, props: defaultProps },
} = columnDefaults;
return (
<Table.Cell
key={id}
{...(resolveFunctionalProp(defaultProps, datum[id]) ?? [])}
{...(resolveFunctionalProp(props, datum[id]) ?? [])}
>
{resolveFunctionalProp(children, datum[id]) ??
resolveFunctionalProp(defaultChildren, datum[id]) ??
datum[id]}
</Table.Cell>
);
}
);
return (
<Table.Row
key={datumID(datum)}
{...(resolveFunctionalProp(datumRowProps, datum) ?? [])}
>
{cells}
</Table.Row>
);
});
return (
<Table className={classes(['SortableTable', className])} {...rest}>
<Table.Row bold {...headerRowProps}>
{columnHeaders}
</Table.Row>
{dataRows}
</Table>
);
}
}
TableCell.defaultHooks = pureComponentHooks;
Table.Row = TableRow;
Table.Cell = TableCell;
Table.Sortable = SortableTable;
@@ -1,7 +1,9 @@
import { createSearch } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from '../backend';
import {
Button,
Flex,
Icon,
Input,
LabeledList,
@@ -9,10 +11,11 @@ import {
Table,
Tabs,
} from '../components';
import { FlexItem } from '../components/Flex';
import { TableCell } from '../components/Table';
import { Window } from '../layouts';
import { LoginInfo } from './common/LoginInfo';
import { LoginScreen } from './common/LoginScreen';
import { RecordsTable } from './common/RecordsTable';
export const AccountsUplinkTerminal = (properties, context) => {
const { act, data } = useBackend(context);
@@ -39,7 +42,7 @@ export const AccountsUplinkTerminal = (properties, context) => {
return (
<Window resizable>
<Window.Content scrollable className="Layout__content--flexColumn">
<Window.Content scrollable>
<LoginInfo />
<AccountsUplinkTerminalNavigation />
{body}
@@ -78,95 +81,159 @@ const AccountsUplinkTerminalContent = (props, context) => {
}
};
const AccountsRecordList = (props, context) => {
const AccountsRecordList = (properties, context) => {
const { act, data } = useBackend(context);
const { accounts } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const [sortId, _setSortId] = useLocalState(context, 'sortId', 'owner_name');
const [sortOrder, _setSortOrder] = useLocalState(context, 'sortOrder', true);
return (
<RecordsTable
columns={[
{
id: 'owner_name',
name: 'Account Holder',
datum: {
children: (value) => (
<>
<Icon name="user" /> {value}
</>
),
},
},
{
id: 'account_number',
name: 'Account Number',
datum: { children: (value) => <>#{value}</> },
},
{ id: 'suspended', name: 'Account Status' },
{ id: 'money', name: 'Account Balance' },
]}
data={accounts}
datumID={(datum) => datum.account_number}
leftButtons={
<Button
content="New Account"
icon="plus"
onClick={() => act('create_new_account')}
/>
}
searchPlaceholder="Search by account holder, number, status"
datumRowProps={(datum) => ({
className: `AccountsUplinkTerminal__listRow--${datum.suspended}`,
onClick: () =>
act('view_account_detail', {
account_num: datum.account_number,
}),
})}
/>
<Flex direction="column" height="100%">
<AccountsActions />
<Flex.Item flexGrow="1" mt="0.5rem">
<Section height="100%">
<Table className="AccountsUplinkTerminal__list">
<Table.Row bold>
<SortButton id="owner_name">Account Holder</SortButton>
<SortButton id="account_number">Account Number</SortButton>
<SortButton id="suspended">Account Status</SortButton>
<SortButton id="money">Account Balance</SortButton>
</Table.Row>
{accounts
.filter(
createSearch(searchText, (account) => {
return (
account.owner_name +
'|' +
account.account_number +
'|' +
account.suspended +
'|' +
account.money
);
})
)
.sort((a, b) => {
const i = sortOrder ? 1 : -1;
return a[sortId].localeCompare(b[sortId]) * i;
})
.map((account) => (
<Table.Row
key={account.account_number}
className={
'AccountsUplinkTerminal__listRow--' + account.suspended
}
onClick={() =>
act('view_account_detail', {
account_num: account.account_number,
})
}
>
<Table.Cell>
<Icon name="user" /> {account.owner_name}
</Table.Cell>
<Table.Cell>#{account.account_number}</Table.Cell>
<Table.Cell>{account.suspended}</Table.Cell>
<Table.Cell>{account.money}</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
</Flex.Item>
</Flex>
);
};
const DepartmentAccountsList = (props, context) => {
const DepartmentAccountsList = (properties, context) => {
const { act, data } = useBackend(context);
const { department_accounts } = data;
return (
<RecordsTable
columns={[
{
id: 'name',
name: 'Department Name',
datum: {
children: (value) => (
<>
<Icon name="wallet" /> {value}
</>
),
},
},
{
id: 'account_number',
name: 'Account Number',
datum: { children: (value) => <>#{value}</> },
},
{ id: 'suspended', name: 'Account Status' },
{ id: 'money', name: 'Account Balance' },
]}
data={department_accounts}
datumID={(datum) => datum.account_number}
leftButtons={
<Flex direction="column" height="100%">
<AccountsActions />
<Flex.Item flexGrow="1" mt="0.5rem">
<Section height="100%">
<Table className="AccountsUplinkTerminal__list">
<Table.Row bold>
<TableCell>Department Name</TableCell>
<TableCell>Account Number</TableCell>
<TableCell>Account Status</TableCell>
<TableCell>Account Balance</TableCell>
</Table.Row>
{department_accounts.map((account) => (
<Table.Row
key={account.account_number}
className={
'AccountsUplinkTerminal__listRow--' + account.suspended
}
onClick={() =>
act('view_account_detail', {
account_num: account.account_number,
})
}
>
<Table.Cell>
<Icon name="wallet" /> {account.name}
</Table.Cell>
<Table.Cell>#{account.account_number}</Table.Cell>
<Table.Cell>{account.suspended}</Table.Cell>
<Table.Cell>{account.money}</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
</Flex.Item>
</Flex>
);
};
const SortButton = (properties, context) => {
const [sortId, setSortId] = useLocalState(context, 'sortId', 'name');
const [sortOrder, setSortOrder] = useLocalState(context, 'sortOrder', true);
const { id, children } = properties;
return (
<Table.Cell>
<Button
color={sortId !== id && 'transparent'}
width="100%"
onClick={() => {
if (sortId === id) {
setSortOrder(!sortOrder);
} else {
setSortId(id);
setSortOrder(true);
}
}}
>
{children}
{sortId === id && (
<Icon name={sortOrder ? 'sort-up' : 'sort-down'} ml="0.25rem;" />
)}
</Button>
</Table.Cell>
);
};
const AccountsActions = (properties, context) => {
const { act, data } = useBackend(context);
const { is_printing } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
return (
<Flex>
<FlexItem>
<Button
content="New Account"
icon="plus"
onClick={() => act('create_new_account')}
/>
}
searchPlaceholder="Search by department name, account number, status, and balance"
datumRowProps={(datum) => ({
className: `AccountsUplinkTerminal__listRow--${datum.suspended}`,
onClick: () =>
act('view_account_detail', {
account_num: datum.account_number,
}),
})}
/>
</FlexItem>
<FlexItem grow="1" ml="0.5rem">
<Input
placeholder="Search by account holder, number, status"
width="100%"
onInput={(e, value) => setSearchText(value)}
/>
</FlexItem>
</Flex>
);
};
+186 -69
View File
@@ -1,9 +1,11 @@
import { createSearch } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { useBackend, useLocalState } from '../backend';
import {
Box,
Button,
Icon,
Input,
LabeledList,
Section,
Tabs,
@@ -15,11 +17,11 @@ import {
modalOpen,
modalRegisterBodyOverride,
} from '../interfaces/common/ComplexModal';
import { FlexItem } from '../components/Flex';
import { Window } from '../layouts';
import { LoginInfo } from './common/LoginInfo';
import { LoginScreen } from './common/LoginScreen';
import { TemporaryNotice } from './common/TemporaryNotice';
import { RecordsTable } from './common/RecordsTable';
const severities = {
'Minor': 'lightgray',
@@ -114,43 +116,80 @@ export const MedicalRecords = (_properties, context) => {
);
};
const MedicalRecordsList = (props, context) => {
const MedicalRecordsList = (_properties, context) => {
const { act, data } = useBackend(context);
const { records } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const [sortId, _setSortId] = useLocalState(context, 'sortId', 'name');
const [sortOrder, _setSortOrder] = useLocalState(context, 'sortOrder', true);
return (
<RecordsTable
columns={[
{
id: 'name',
name: 'Name',
datum: {
children: (value) => (
<>
<Icon name="user" /> {value}
</>
),
},
},
{ id: 'id', name: 'ID' },
{ id: 'rank', name: 'Assignment' },
{ id: 'p_stat', name: 'Patient Status' },
{ id: 'm_stat', name: 'Mental Status' },
]}
data={records}
datumID={(datum) => datum.ref}
leftButtons={
<Button
content="Manage Records"
icon="wrench"
onClick={() => act('screen', { screen: 3 })}
/>
}
searchPlaceholder="Search by Name, ID, Physical Status, or Mental Status"
datumRowProps={(datum) => ({
className: `MedicalRecords__listRow--${medStatusStyles[datum.p_stat]}`,
onClick: () => act('view_record', { view_record: datum.ref }),
})}
/>
<Flex direction="column" height="100%">
<Flex>
<FlexItem>
<Button
content="Manage Records"
icon="wrench"
ml="0.25rem"
onClick={() => act('screen', { screen: 3 })}
/>
</FlexItem>
<FlexItem grow="1" ml="0.5rem">
<Input
placeholder="Search by Name, ID, Physical Status, or Mental Status"
width="100%"
onInput={(e, value) => setSearchText(value)}
/>
</FlexItem>
</Flex>
<Section flexGrow="1" mt="0.5rem">
<Table className="MedicalRecords__list">
<Table.Row bold>
<SortButton id="name">Name</SortButton>
<SortButton id="id">ID</SortButton>
<SortButton id="rank">Assignment</SortButton>
<SortButton id="p_stat">Patient Status</SortButton>
<SortButton id="m_stat">Mental Status</SortButton>
</Table.Row>
{records
.filter(
createSearch(searchText, (record) => {
return (
record.name +
'|' +
record.id +
'|' +
record.rank +
'|' +
record.p_stat +
'|' +
record.m_stat
);
})
)
.sort((a, b) => {
const i = sortOrder ? 1 : -1;
return a[sortId].localeCompare(b[sortId]) * i;
})
.map((record) => (
<Table.Row
key={record.id}
className={
'MedicalRecords__listRow--' + medStatusStyles[record.p_stat]
}
onClick={() => act('view_record', { view_record: record.ref })}
>
<Table.Cell>
<Icon name="user" /> {record.name}
</Table.Cell>
<Table.Cell>{record.id}</Table.Cell>
<Table.Cell>{record.rank}</Table.Cell>
<Table.Cell>{record.p_stat}</Table.Cell>
<Table.Cell>{record.m_stat}</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
</Flex>
);
};
@@ -334,42 +373,62 @@ const MedicalRecordsViewMedical = (_properties, context) => {
);
};
const MedicalRecordsViruses = (props, context) => {
const MedicalRecordsViruses = (_properties, context) => {
const { act, data } = useBackend(context);
const { virus } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const [sortId2, _setSortId2] = useLocalState(context, 'sortId2', 'name');
const [sortOrder2, _setSortOrder2] = useLocalState(
context,
'sortOrder2',
true
);
return (
<RecordsTable
columns={[
{
id: 'name',
name: 'Name',
datum: {
children: (value) => (
<>
<Icon name="virus" /> {value}
</>
),
},
},
{ id: 'max_stages', name: 'Max Stages' },
{
id: 'severity',
name: 'Severity',
datum: {
props: (value) => ({
color: severities[value],
}),
},
},
]}
data={virus}
datumID={(datum) => datum.id}
searchPlaceholder="Search by Name, Max Stages, or Severity"
datumRowProps={(datum) => ({
className: `MedicalRecords__listVirus--${datum.severity}`,
onClick: () => act('vir', { vir: datum.D }),
})}
/>
<Flex direction="column" height="100%">
<Flex>
<FlexItem grow="1" ml="0.5rem">
<Input
placeholder="Search by Name, Max Stages, or Severity"
width="100%"
onInput={(e, value) => setSearchText(value)}
/>
</FlexItem>
</Flex>
<Section flexGrow="1" mt="0.5rem">
<Table className="MedicalRecords__list">
<Table.Row bold>
<SortButton2 id="name">Name</SortButton2>
<SortButton2 id="max_stages">Max Stages</SortButton2>
<SortButton2 id="severity">Severity</SortButton2>
</Table.Row>
{virus
.filter(
createSearch(searchText, (vir) => {
return vir.name + '|' + vir.max_stages + '|' + vir.severity;
})
)
.sort((a, b) => {
const i = sortOrder2 ? 1 : -1;
return a[sortId2].localeCompare(b[sortId2]) * i;
})
.map((vir) => (
<Table.Row
key={vir.id}
className={'MedicalRecords__listVirus--' + vir.severity}
onClick={() => act('vir', { vir: vir.D })}
>
<Table.Cell>
<Icon name="virus" /> {vir.name}
</Table.Cell>
<Table.Cell>{vir.max_stages}</Table.Cell>
<Table.Cell color={severities[vir.severity]}>
{vir.severity}
</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
</Flex>
);
};
@@ -423,6 +482,64 @@ const MedicalRecordsMedbots = (_properties, context) => {
);
};
const SortButton = (properties, context) => {
const [sortId, setSortId] = useLocalState(context, 'sortId', 'name');
const [sortOrder, setSortOrder] = useLocalState(context, 'sortOrder', true);
const { id, children } = properties;
return (
<Table.Cell>
<Button
color={sortId !== id && 'transparent'}
width="100%"
onClick={() => {
if (sortId === id) {
setSortOrder(!sortOrder);
} else {
setSortId(id);
setSortOrder(true);
}
}}
>
{children}
{sortId === id && (
<Icon name={sortOrder ? 'sort-up' : 'sort-down'} ml="0.25rem;" />
)}
</Button>
</Table.Cell>
);
};
const SortButton2 = (properties, context) => {
const [sortId2, setSortId2] = useLocalState(context, 'sortId2', 'name');
const [sortOrder2, setSortOrder2] = useLocalState(
context,
'sortOrder2',
true
);
const { id, children } = properties;
return (
<Table.Cell>
<Button
color={sortId2 !== id && 'transparent'}
width="100%"
onClick={() => {
if (sortId2 === id) {
setSortOrder2(!sortOrder2);
} else {
setSortId2(id);
setSortOrder2(true);
}
}}
>
{children}
{sortId2 === id && (
<Icon name={sortOrder2 ? 'sort-up' : 'sort-down'} ml="0.25rem;" />
)}
</Button>
</Table.Cell>
);
};
const MedicalRecordsNavigation = (_properties, context) => {
const { act, data } = useBackend(context);
const { screen, general } = data;
+134 -52
View File
@@ -1,13 +1,23 @@
import { decodeHtmlEntities } from 'common/string';
import { createSearch, decodeHtmlEntities } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Icon, LabeledList, Section, Tabs } from '../components';
import { useBackend, useLocalState } from '../backend';
import {
Box,
Button,
Flex,
Icon,
Input,
LabeledList,
Section,
Table,
Tabs,
} from '../components';
import { FlexItem } from '../components/Flex';
import { Window } from '../layouts';
import { ComplexModal, modalOpen } from './common/ComplexModal';
import { LoginInfo } from './common/LoginInfo';
import { LoginScreen } from './common/LoginScreen';
import { TemporaryNotice } from './common/TemporaryNotice';
import { RecordsTable } from './common/RecordsTable';
const statusStyles = {
'*Execute*': 'execute',
@@ -85,56 +95,128 @@ const SecurityRecordsNavigation = (properties, context) => {
);
};
const SecurityRecordsPageList = (props, context) => {
const SecurityRecordsPageList = (properties, context) => {
const { act, data } = useBackend(context);
const { isPrinting, records } = data;
const { records } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
const [sortId, _setSortId] = useLocalState(context, 'sortId', 'name');
const [sortOrder, _setSortOrder] = useLocalState(context, 'sortOrder', true);
return (
<RecordsTable
columns={[
{
id: 'name',
name: 'Name',
datum: {
children: (value) => (
<>
<Icon name="user" /> {value}
</>
),
},
},
{ id: 'id', name: 'ID' },
{ id: 'rank', name: 'Assignment' },
{ id: 'fingerprint', name: 'Fingerprint' },
{ id: 'status', name: 'Criminal Status' },
]}
data={records}
datumID={(datum) => datum.id}
leftButtons={
<>
<Button
content="New Record"
icon="plus"
onClick={() => act('new_general')}
/>
<Button
disabled={isPrinting}
icon={isPrinting ? 'spinner' : 'print'}
iconSpin={!!isPrinting}
content="Print Cell Log"
onClick={() => modalOpen(context, 'print_cell_log')}
/>
</>
}
searchPlaceholder="Search by Name, ID, Assignment, Fingerprint, Status"
datumRowProps={(datum) => ({
className: `SecurityRecords__listRow--${statusStyles[datum.status]}`,
onClick: () =>
act('view', {
uid_gen: datum.uid_gen,
uid_sec: datum.uid_sec,
}),
})}
/>
<Flex direction="column" height="100%">
<SecurityRecordsActions />
<Section flexGrow="1" mt="0.5rem">
<Table className="SecurityRecords__list">
<Table.Row bold>
<SortButton id="name">Name</SortButton>
<SortButton id="id">ID</SortButton>
<SortButton id="rank">Assignment</SortButton>
<SortButton id="fingerprint">Fingerprint</SortButton>
<SortButton id="status">Criminal Status</SortButton>
</Table.Row>
{records
.filter(
createSearch(searchText, (record) => {
return (
record.name +
'|' +
record.id +
'|' +
record.rank +
'|' +
record.fingerprint +
'|' +
record.status
);
})
)
.sort((a, b) => {
const i = sortOrder ? 1 : -1;
return a[sortId].localeCompare(b[sortId]) * i;
})
.map((record) => (
<Table.Row
key={record.id}
className={
'SecurityRecords__listRow--' + statusStyles[record.status]
}
onClick={() =>
act('view', {
uid_gen: record.uid_gen,
uid_sec: record.uid_sec,
})
}
>
<Table.Cell>
<Icon name="user" /> {record.name}
</Table.Cell>
<Table.Cell>{record.id}</Table.Cell>
<Table.Cell>{record.rank}</Table.Cell>
<Table.Cell>{record.fingerprint}</Table.Cell>
<Table.Cell>{record.status}</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
</Flex>
);
};
const SortButton = (properties, context) => {
const [sortId, setSortId] = useLocalState(context, 'sortId', 'name');
const [sortOrder, setSortOrder] = useLocalState(context, 'sortOrder', true);
const { id, children } = properties;
return (
<Table.Cell>
<Button
color={sortId !== id && 'transparent'}
width="100%"
onClick={() => {
if (sortId === id) {
setSortOrder(!sortOrder);
} else {
setSortId(id);
setSortOrder(true);
}
}}
>
{children}
{sortId === id && (
<Icon name={sortOrder ? 'sort-up' : 'sort-down'} ml="0.25rem;" />
)}
</Button>
</Table.Cell>
);
};
const SecurityRecordsActions = (properties, context) => {
const { act, data } = useBackend(context);
const { isPrinting } = data;
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
return (
<Flex>
<FlexItem>
<Button
content="New Record"
icon="plus"
onClick={() => act('new_general')}
/>
<Button
disabled={isPrinting}
icon={isPrinting ? 'spinner' : 'print'}
iconSpin={!!isPrinting}
content="Print Cell Log"
ml="0.25rem"
onClick={() => modalOpen(context, 'print_cell_log')}
/>
</FlexItem>
<FlexItem grow="1" ml="0.5rem">
<Input
placeholder="Search by Name, ID, Assignment, Fingerprint, Status"
width="100%"
onInput={(e, value) => setSearchText(value)}
/>
</FlexItem>
</Flex>
);
};
@@ -1,55 +0,0 @@
import { Component } from 'inferno';
import { Flex, Input, Section, Table } from '../../components';
import { FlexItem } from '../../components/Flex';
import { createSearch } from 'common/string';
import { classes } from 'common/react';
export class RecordsTable extends Component {
constructor() {
super();
this.state = {
searchText: '',
};
}
render() {
const {
className,
columns,
leftButtons,
rightButtons,
searchPlaceholder,
...rest
} = this.props;
const { searchText } = this.state;
return (
<Flex direction="column" height="100%">
<Flex className="RecordsTable_Toolbar">
{leftButtons}
<FlexItem grow="1">
<Input
placeholder={searchPlaceholder}
width="100%"
onInput={(e, value) => this.setState({ searchText: value })}
/>
</FlexItem>
{rightButtons}
</Flex>
<Section flexGrow="1" mt="0.5rem">
<Table.Sortable
className={classes(['RecordsTable', className])}
columns={columns}
filter={(data) =>
data.filter(
createSearch(searchText, (datum) =>
columns.map((c) => datum[c.id]).join('|')
)
)
}
{...rest}
/>
</Section>
</Flex>
);
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,6 +2,24 @@
@use '../colors.scss';
@use '../functions.scss' as *;
.AccountsUplinkTerminal__list {
tr > td {
text-align: center;
}
tr:not(:first-child) {
height: 24px;
line-height: 24px;
cursor: pointer;
transition: background-color 50ms;
&:hover,
&:focus {
background-color: rgba(base.$color-bg, 1);
}
}
}
.AccountsUplinkTerminal__listRow--SUSPENDED {
background-color: colors.bg(#890e26);
}
@@ -2,6 +2,24 @@
@use '../colors.scss';
@use '../functions.scss' as *;
.MedicalRecords__list {
tr > td {
text-align: center;
}
tr:not(:first-child) {
height: 24px;
line-height: 24px;
cursor: pointer;
transition: background-color 50ms;
&:hover,
&:focus {
background-color: rgba(base.$color-bg, 1);
}
}
}
.MedicalRecords__listRow--deceased {
background-color: colors.bg(#890e26);
}
@@ -2,6 +2,24 @@
@use '../colors.scss';
@use '../functions.scss' as *;
.SecurityRecords__list {
tr > td {
text-align: center;
}
tr:not(:first-child) {
height: 24px;
line-height: 24px;
cursor: pointer;
transition: background-color 50ms;
&:hover,
&:focus {
background-color: rgba(base.$color-bg, 1);
}
}
}
.SecurityRecords__listRow--arrest {
background-color: colors.bg(#890e26);
}
@@ -1,29 +0,0 @@
@use '../../base.scss';
@use '../../colors.scss';
@use '../../functions.scss' as *;
.RecordsTable {
tr > td {
text-align: center;
}
tr:not(:first-child) {
height: 24px;
line-height: 24px;
cursor: pointer;
transition: background-color 50ms;
&:hover,
&:focus {
background-color: rgba(base.$color-bg, 1);
}
}
}
.RecordsTable_Toolbar {
> {
*:not(:first-child) {
margin-left: 0.25rem;
}
}
}
-1
View File
@@ -39,7 +39,6 @@
@include meta.load-css('./components/Tooltip.scss');
// Interfaces
@include meta.load-css('./interfaces/common/RecordsTable.scss');
@include meta.load-css('./interfaces/AccountsUplinkTerminal.scss');
@include meta.load-css('./interfaces/BrigCells.scss');
@include meta.load-css('./interfaces/CameraConsole.scss');