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

SIMSBIOHUB-223 #1096

Closed
wants to merge 10 commits into from
5 changes: 4 additions & 1 deletion app/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from 'components/security/RouteGuards';
import { SYSTEM_ROLE } from 'constants/roles';
import { CodesContextProvider } from 'contexts/codesContext';
import { ObservationsContextProvider } from 'contexts/observationsContext';
import AdminUsersRouter from 'features/admin/AdminUsersRouter';
import FundingSourcesRouter from 'features/funding-sources/FundingSourcesRouter';
import ProjectsRouter from 'features/projects/ProjectsRouter';
Expand Down Expand Up @@ -117,7 +118,9 @@ const AppRouter: React.FC = () => {
</RouteWithTitle>

<RouteWithTitle title="prototype" path="/prototype">
<PrototypePage />
<ObservationsContextProvider>
<PrototypePage />
</ObservationsContextProvider>
</RouteWithTitle>

<Route path="/login">
Expand Down
128 changes: 128 additions & 0 deletions app/src/contexts/observationsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { GridRowModes, GridRowModesModel } from '@mui/x-data-grid';
import useDataLoader from 'hooks/useDataLoader';
import { createContext, Dispatch, PropsWithChildren, SetStateAction, useEffect, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';

export interface IObservationRecord {
observation_id: number;
speciesName: string;
samplingSite: string;
samplingMethod: string;
samplingPeriod: string;
count?: number;
date?: string;
time?: string;
lat?: number;
long?: number;
}

export interface IObservationTableRow extends Omit<IObservationRecord, 'observation_id'> {
id: string;
observation_id: number | null;
_isModified: boolean;
}

export const fetchObservationDemoRows = async (): Promise<IObservationRecord[]> => {
await setTimeout(() => {}, 100 * (Math.random() + 1));
return [

Check warning on line 27 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L25-L27

Added lines #L25 - L27 were not covered by tests
{
observation_id: 1,
speciesName: 'Moose (Alces Americanus)',
samplingSite: 'Site 1',
samplingMethod: 'Method 1',
samplingPeriod: '',

Check warning on line 33 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Delete `,`
},
{
observation_id: 2,
speciesName: 'Moose (Alces Americanus)',
samplingSite: 'Site 1',
samplingMethod: 'Method 1',
samplingPeriod: '',

Check warning on line 40 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Delete `,`
},
{
observation_id: 3,
speciesName: 'Moose (Alces Americanus)',
samplingSite: 'Site 1',
samplingMethod: 'Method 1',
samplingPeriod: '',

Check warning on line 47 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Delete `,`
}
]

Check warning on line 49 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Insert `;`
}

Check warning on line 50 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Insert `;`

/**
* Context object that stores information about survey observations
*
* @export
* @interface IObservationsContext
*/
export type IObservationsContext = {
createNewRecord: () => void;
_rows: IObservationTableRow[]

Check warning on line 60 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Insert `;`
_rowModesModel: GridRowModesModel;
_setRows: Dispatch<SetStateAction<IObservationTableRow[]>> // (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void;

Check warning on line 62 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Insert `;`
_setRowModesModel: Dispatch<SetStateAction<GridRowModesModel>>

Check warning on line 63 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Insert `;`
}

Check warning on line 64 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Insert `;`

export const ObservationsContext = createContext<IObservationsContext>({

Check warning on line 66 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L66

Added line #L66 was not covered by tests
_rows: [],
_rowModesModel: {},
_setRows: () => {},
_setRowModesModel: () => {},
createNewRecord: () => {},

Check warning on line 71 in app/src/contexts/observationsContext.tsx

View workflow job for this annotation

GitHub Actions / Running Linter and Formatter

Delete `,⏎`

});

export const ObservationsContextProvider = (props: PropsWithChildren<Record<never, any>>) => {
const [_rows, _setRows] = useState<IObservationTableRow[]>([]);
const [_rowModesModel, _setRowModesModel] = useState<GridRowModesModel>({});

Check warning on line 77 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L75-L77

Added lines #L75 - L77 were not covered by tests

const observationsDataLoader = useDataLoader(fetchObservationDemoRows);
observationsDataLoader.load()

Check warning on line 80 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L79-L80

Added lines #L79 - L80 were not covered by tests

useEffect(() => {

Check warning on line 82 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L82

Added line #L82 was not covered by tests
if (observationsDataLoader.data) {
const rows: IObservationTableRow[] = observationsDataLoader.data.map((row) => ({

Check warning on line 84 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L84

Added line #L84 was not covered by tests
...row,
id: String(row.observation_id),
_isModified: false
}));
_setRows(rows);

Check warning on line 89 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L89

Added line #L89 was not covered by tests
}
}, [observationsDataLoader.data])

const createNewRecord = () => {
const id = uuidv4();

Check warning on line 94 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L93-L94

Added lines #L93 - L94 were not covered by tests

_setRows((oldRows) => [

Check warning on line 96 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L96

Added line #L96 was not covered by tests
...oldRows,
{
id,
_isModified: true,
observation_id: null,
speciesName: '',
samplingSite: '',
samplingMethod: '',
samplingPeriod: '',
}
]);

_setRowModesModel((oldModel) => ({

Check warning on line 109 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L109

Added line #L109 was not covered by tests
...oldModel,
[id]: { mode: GridRowModes.Edit, fieldToFocus: 'speciesName' },
}));
};

const observationsContext: IObservationsContext = {

Check warning on line 115 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L115

Added line #L115 was not covered by tests
createNewRecord,
_rows,
_rowModesModel,
_setRows,
_setRowModesModel
};

return (

Check warning on line 123 in app/src/contexts/observationsContext.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/contexts/observationsContext.tsx#L123

Added line #L123 was not covered by tests
<ObservationsContext.Provider value={observationsContext}>
{props.children}
</ObservationsContext.Provider>
);
};
200 changes: 200 additions & 0 deletions app/src/pages/prototype/ObservationsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { mdiDotsVertical } from "@mdi/js";
import Icon from "@mdi/react";
import { Theme } from "@mui/material";
import IconButton from '@mui/material/IconButton';
import { makeStyles } from "@mui/styles";
import { DataGrid, GridColDef, GridEventListener } from '@mui/x-data-grid';
import { IObservationTableRow, ObservationsContext } from "contexts/observationsContext";
import { useContext } from "react";
// import { useEffect, useState } from "react";
import { pluralize as p } from "utils/Utils";

export type IObservationsTableProps = Record<never, any>;

const useStyles = makeStyles((theme: Theme) => ({

Check warning on line 14 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L14

Added line #L14 was not covered by tests
modifiedRow: {} // { background: 'rgba(65, 168, 3, 0.16)' }
}));

export const observationColumns: GridColDef<IObservationTableRow>[] = [

Check warning on line 18 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L18

Added line #L18 was not covered by tests
{
field: 'speciesName',
headerName: 'Species',
editable: true,
flex: 1,
minWidth: 250,
disableColumnMenu: true
},
{
field: 'samplingSite',
headerName: 'Sampling Site',
editable: true,
type: 'singleSelect',
valueOptions: ['Site 1', 'Site 2', 'Site 3', 'Site 4'],
flex: 1,
minWidth: 200,
disableColumnMenu: true
},
{
field: 'samplingMethod',
headerName: 'Sampling Method',
editable: true,
type: 'singleSelect',
valueOptions: ['Method 1', 'Method 2', 'Method 3', 'Method 4'],
flex: 1,
minWidth: 200,
disableColumnMenu: true
},
{
field: 'samplingPeriod',
headerName: 'Sampling Period',
editable: true,
type: 'singleSelect',
valueOptions: ['Period 1', 'Period 2', 'Period 3', 'Period 4', 'Undefined'],
flex: 1,
minWidth: 200,
disableColumnMenu: true
},
{
field: 'count',
headerName: 'Count',
editable: true,
type: 'number',
minWidth: 100,
disableColumnMenu: true,
},
{
field: 'date',
headerName: 'Date',
editable: true,
type: 'date',
minWidth: 150,
disableColumnMenu: true,
},
{
field: 'time',
headerName: 'Time',
editable: true,
type: 'time',
width: 150,
disableColumnMenu: true
},
{
field: 'lat',
headerName: 'Lat',
type: 'number',
editable: true,
width: 150,
disableColumnMenu: true
},
{
field: 'long',
headerName: 'Long',
type: 'number',
editable: true,
width: 150,
disableColumnMenu: true
},
{
field: 'actions',
headerName: '',
type: 'actions',
width: 80,
disableColumnMenu: true,
resizable: false,
cellClassName: 'test',
getActions: () => [

Check warning on line 105 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L105

Added line #L105 was not covered by tests
<IconButton>
<Icon path={mdiDotsVertical} size={1} />
</IconButton>
],
}
];

const ObservationsTable = (props: IObservationsTableProps) => {
const classes = useStyles();

Check warning on line 114 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L113-L114

Added lines #L113 - L114 were not covered by tests

const { _rows, _setRows, _setRowModesModel, _rowModesModel } = useContext(ObservationsContext);
console.log('rowModesModel:', _rowModesModel);

Check warning on line 117 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L116-L117

Added lines #L116 - L117 were not covered by tests

const handleRowEditStart: GridEventListener<'rowEditStart'> = (params, event) => {
console.log({ params })

Check warning on line 120 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L119-L120

Added lines #L119 - L120 were not covered by tests
}

const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
event.defaultMuiPrevented = true;

Check warning on line 124 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L123-L124

Added lines #L123 - L124 were not covered by tests
//if (params.reason === GridRowEditStopReasons.rowFocusOut) {
// }
};

const handleProcessRowUpdate = (newRow: IObservationTableRow) => {
const updatedRow: IObservationTableRow = { ...newRow, _isModified: true };

Check warning on line 130 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L129-L130

Added lines #L129 - L130 were not covered by tests

_setRows(_rows.map((row) => (row.id === newRow.id ? updatedRow : row)));
return updatedRow;

Check warning on line 133 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L133

Added line #L133 was not covered by tests
};

const numModified = _rows.filter((row) => row._isModified).length;

Check warning on line 136 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L136

Added line #L136 was not covered by tests

return (

Check warning on line 138 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L138

Added line #L138 was not covered by tests
<DataGrid
editMode="row"
onRowEditStart={handleRowEditStart}
onRowEditStop={handleRowEditStop}
processRowUpdate={handleProcessRowUpdate}
columns={observationColumns}
rows={_rows}
rowModesModel={_rowModesModel}
disableRowSelectionOnClick
onRowModesModelChange={_setRowModesModel}
localeText={{
noRowsLabel: "No Records",
footerRowSelected: (numSelected: number) => {
return [

Check warning on line 152 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L152

Added line #L152 was not covered by tests
numSelected > 0 && `${numSelected} ${p(numSelected, 'row')} selected`,
numModified > 0 && `${numModified} unsaved ${p(numModified, 'row')}`
].filter(Boolean).join(', ')
}
}}
getRowClassName={(params) => {
if (params.row._isModified || _rowModesModel) {
return classes.modifiedRow;

Check warning on line 160 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L160

Added line #L160 was not covered by tests
}

return '';

Check warning on line 163 in app/src/pages/prototype/ObservationsTable.tsx

View check run for this annotation

Codecov / codecov/patch

app/src/pages/prototype/ObservationsTable.tsx#L163

Added line #L163 was not covered by tests
}}
sx={{
background: '#fff',
border: 'none',
'& .MuiDataGrid-pinnedColumns, .MuiDataGrid-pinnedColumnHeaders': {
background: '#fff'
},
'& .MuiDataGrid-columnHeaderTitle': {
fontWeight: 700,
textTransform: 'uppercase',
color: '#999'
},
'& .test': {
position: 'sticky',
right: 0,
top: 0,
borderLeft: '1px solid #ccc',
background: '#fff'
},
'& .MuiDataGrid-columnHeaders': {
position: 'relative'
},
'& .MuiDataGrid-columnHeaders:after': {
content: "''",
position: 'absolute',
right: 0,
width: '79px',
height: '80px',
borderLeft: '1px solid #ccc',
background: '#fff'
}
}}
/>
)
}

export default ObservationsTable;
Loading
Loading