Skip to content

Commit

Permalink
re-incorporating row edit functionality into the view data interface.…
Browse files Browse the repository at this point in the history
… will continue to expand this to make it more intuitive
  • Loading branch information
siddheshraze committed Jan 24, 2025
1 parent 9f6df8c commit 3c7b488
Show file tree
Hide file tree
Showing 10 changed files with 10,367 additions and 10,279 deletions.
103 changes: 91 additions & 12 deletions frontend/app/api/fixeddata/[dataType]/[[...slugs]]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { format } from 'mysql2/promise';
import { NextRequest, NextResponse } from 'next/server';
import { AllTaxonomiesViewQueryConfig, handleDeleteForSlices, handleUpsertForSlices } from '@/components/processors/processorhelperfunctions';
import { HTTPResponses } from '@/config/macros';
import ConnectionManager from '@/config/connectionmanager'; // slugs SHOULD CONTAIN AT MINIMUM: schema, page, pageSize, plotID, plotCensusNumber, (optional) quadratID, (optional) speciesID
import ConnectionManager from '@/config/connectionmanager';
import { getUpdatedValues } from '@/config/utils'; // slugs SHOULD CONTAIN AT MINIMUM: schema, page, pageSize, plotID, plotCensusNumber, (optional) quadratID, (optional) speciesID

// slugs SHOULD CONTAIN AT MINIMUM: schema, page, pageSize, plotID, plotCensusNumber, (optional) quadratID, (optional) speciesID
export async function GET(
Expand Down Expand Up @@ -231,22 +232,100 @@ export async function PATCH(request: NextRequest, { params }: { params: { dataTy

// Handle non-view table updates
else {
const newRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const { [demappedGridID]: gridIDKey, ...remainingProperties } = newRowData;
if (params.dataType === 'measurementssummary') {
console.log('params datatype is ', params.dataType);
const updatedFields = getUpdatedValues(oldRow, newRow);
console.log('updated fields: ', updatedFields);
const { coreMeasurementID, quadratID, treeID, stemID, speciesID } = newRow;

// Construct the UPDATE query
const updateQuery = format(
`UPDATE ??
const fieldGroups = {
coremeasurements: ['measuredDBH', 'measuredHOM', 'measurementDate'],
quadrats: ['quadratName'],
trees: ['treeTag'],
stems: ['stemTag', 'stemLocalX', 'stemLocalY'],
species: ['speciesName', 'subspeciesName', 'speciesCode']
};

// Initialize a flag for changes
let changesFound = false;

// Helper function to handle updates
const handleUpdate = async (groupName: keyof typeof fieldGroups, tableName: string, idColumn: string, idValue: any) => {
console.log('updating: ', groupName);
const matchingFields = Object.keys(updatedFields).reduce(
(acc, key) => {
if (fieldGroups[groupName].includes(key)) {
acc[key] = updatedFields[key];
}
return acc;
},
{} as Partial<typeof updatedFields>
);
console.log(`matching fields for group name ${groupName}: `, matchingFields);

if (Object.keys(matchingFields).length > 0) {
changesFound = true;
if (groupName === 'stems') {
// need to correct for key matching
if (matchingFields.stemLocalX) {
matchingFields.localX = matchingFields.stemLocalX;
delete matchingFields.stemLocalX;
}
if (matchingFields.stemLocalY) {
matchingFields.localY = matchingFields.stemLocalY;
delete matchingFields.stemLocalY;
}
}
const demappedData = MapperFactory.getMapper<any, any>(groupName).demapData([matchingFields])[0];
console.log('demapped data: ', JSON.stringify(demappedData));
const query = format('UPDATE ?? SET ? WHERE ?? = ?', [`${schema}.${tableName}`, demappedData, idColumn, idValue]);
console.log('update query: ', query);
await connectionManager.executeQuery(query);
}
};

// Process each group
await handleUpdate('coremeasurements', 'coremeasurements', 'CoreMeasurementID', coreMeasurementID);
await handleUpdate('quadrats', 'quadrats', 'QuadratID', quadratID);
await handleUpdate('trees', 'trees', 'TreeID', treeID);
await handleUpdate('stems', 'stems', 'StemID', stemID);
await handleUpdate('species', 'species', 'SpeciesID', speciesID);

// Reset validation status and clear errors if changes were made
if (changesFound) {
console.log('changes were found. resetting validation/clearing cmverrors');
const resetValidationQuery = format('UPDATE ?? SET ?? = ? WHERE ?? = ?', [
`${schema}.coremeasurements`,
'IsValidated',
null,
'CoreMeasurementID',
coreMeasurementID
]);
console.log('reset validation query: ', resetValidationQuery);
const deleteErrorsQuery = `DELETE FROM ${schema}.cmverrors WHERE CoreMeasurementID = ${coreMeasurementID}`;
console.log('delete cmverrors query: ', deleteErrorsQuery);
await connectionManager.executeQuery(resetValidationQuery);
await connectionManager.executeQuery(deleteErrorsQuery);
}
} else {
// special handling need not apply to non-measurements tables
const newRowData = MapperFactory.getMapper<any, any>(params.dataType).demapData([newRow])[0];
const { [demappedGridID]: gridIDKey, ...remainingProperties } = newRowData;

// Construct the UPDATE query
const updateQuery = format(
`UPDATE ??
SET ?
WHERE ?? = ?`,
[`${schema}.${params.dataType}`, remainingProperties, demappedGridID, gridIDKey]
);
[`${schema}.${params.dataType}`, remainingProperties, demappedGridID, gridIDKey]
);

// Execute the UPDATE query
await connectionManager.executeQuery(updateQuery);
// Execute the UPDATE query
await connectionManager.executeQuery(updateQuery);

// For non-view tables, standardize the response format
updateIDs = { [params.dataType]: gridIDKey };
// For non-view tables, standardize the response format
updateIDs = { [params.dataType]: gridIDKey };
}
}
await connectionManager.commitTransaction(transactionID ?? '');
return NextResponse.json({ message: 'Update successful', updatedIDs: updateIDs }, { status: HTTPResponses.OK });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ export async function PATCH(request: NextRequest, { params }: { params: { dataTy

const connectionManager = ConnectionManager.getInstance();
const demappedGridID = gridID.charAt(0).toUpperCase() + gridID.substring(1);
const { newRow, oldRow } = await request.json();
const { newRow } = await request.json();
let updateIDs: { [key: string]: number } = {};
let transactionID: string | undefined = undefined;

Expand Down
190 changes: 94 additions & 96 deletions frontend/components/client/datagridcolumns.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HEADER_ALIGN, unitSelectionOptions } from '@/config/macros';
import { Box, FormHelperText, Input, Option, Select, Stack, Typography } from '@mui/joy';
import { GridColDef, useGridApiRef } from '@mui/x-data-grid';
import React, { useEffect, useState } from 'react';
import { HEADER_ALIGN } from '@/config/macros';
import { Box, Stack, Typography } from '@mui/joy';
import { GridColDef } from '@mui/x-data-grid';
import React from 'react';
import { AttributeStatusOptions } from '@/config/sqlrdsdefinitions/core';
import { standardizeGridColumns } from '@/components/client/clientmacros';
import { customNumericOperators } from '@/components/datagrids/filtrationsystem';
Expand Down Expand Up @@ -224,89 +224,89 @@ export const StemTaxonomiesViewGridColumns: GridColDef[] = standardizeGridColumn

// note --> originally attempted to use GridValueFormatterParams, but this isn't exported by MUI X DataGrid anymore. replaced with <any> for now.

const renderValueCell = (params: any, valueKey: string, unitKey: string) => {
const value = params.row[valueKey] ? Number(params.row[valueKey]).toFixed(2) : 'null';
const units = params.row[unitKey] ? (params.row[valueKey] !== null ? params.row[unitKey] : '') : '';

return (
<Box sx={{ display: 'flex', flexDirection: 'row', gap: '0.5em', alignItems: 'center' }}>
{value && <Typography level="body-sm">{value}</Typography>}
{units && <Typography level="body-sm">{units}</Typography>}
</Box>
);
};

const renderEditValueCell = (params: any, valueKey: string, unitKey: string, placeholder: string) => {
const apiRef = useGridApiRef();
const { id, row } = params;
const [error, setError] = useState(false);
const [value, setValue] = useState(row[valueKey]);

const handleValueChange = (event: any) => {
const inputValue = event.target.value;
const isValid = /^\d*\.?\d{0,2}$/.test(inputValue);
setError(!isValid);
if (isValid) {
setValue(inputValue);
}
};

const handleValueBlur = () => {
const truncatedValue = Number(value).toFixed(2);
apiRef.current.setEditCellValue({ id, field: valueKey, value: truncatedValue });
};

const handleUnitsChange = (_event: any, newValue: any) => {
if (newValue !== null) {
apiRef.current.setEditCellValue({ id, field: unitKey, value: newValue });
}
};

useEffect(() => {
setValue(row[valueKey]);
}, [row[valueKey]]);

return (
<Box sx={{ display: 'flex', flexDirection: 'row', gap: '0.5em', alignItems: 'center' }}>
<Stack direction="column">
<Input
value={value}
onChange={handleValueChange}
onBlur={handleValueBlur}
error={error}
placeholder={placeholder}
required
slotProps={{
input: {
'aria-invalid': error
}
}}
/>
{error && (
<FormHelperText>
<Typography color="danger">Only numbers with up to 2 decimal places accepted!</Typography>
</FormHelperText>
)}
</Stack>
<Select value={row[unitKey]} onChange={handleUnitsChange} placeholder={'Units'} required>
{unitSelectionOptions.map(option => (
<Option key={option} value={option}>
{option}
</Option>
))}
</Select>
</Box>
);
};

export const renderDBHCell = (params: any) => renderValueCell(params, 'measuredDBH', '');
export const renderEditDBHCell = (params: any) => renderEditValueCell(params, 'measuredDBH', '', 'Diameter at breast height (DBH)');
export const renderHOMCell = (params: any) => renderValueCell(params, 'measuredHOM', '');
export const renderEditHOMCell = (params: any) => renderEditValueCell(params, 'measuredHOM', '', 'Height of Measure (HOM)');
export const renderStemXCell = (params: any) => renderValueCell(params, 'localStemX', '');
export const renderEditStemXCell = (params: any) => renderEditValueCell(params, 'localStemX', '', 'Stem Local X Coordinates');
export const renderStemYCell = (params: any) => renderValueCell(params, 'localStemY', '');
export const renderEditStemYCell = (params: any) => renderEditValueCell(params, 'localStemY', '', 'Stem Local Y Coordinates');
// const renderValueCell = (params: any, valueKey: string, unitKey: string) => {
// const value = params.row[valueKey] ? Number(params.row[valueKey]).toFixed(2) : 'null';
// const units = params.row[unitKey] ? (params.row[valueKey] !== null ? params.row[unitKey] : '') : '';
//
// return (
// <Box sx={{ display: 'flex', flexDirection: 'row', gap: '0.5em', alignItems: 'center' }}>
// {value && <Typography level="body-sm">{value}</Typography>}
// {units && <Typography level="body-sm">{units}</Typography>}
// </Box>
// );
// };
//
// const renderEditValueCell = (params: any, valueKey: string, unitKey: string, placeholder: string) => {
// const apiRef = useGridApiContext();
// const { id, row } = params;
// const [error, setError] = useState(false);
// const [value, setValue] = useState(row[valueKey]);
//
// const handleValueChange = (event: any) => {
// const inputValue = event.target.value;
// const isValid = /^\d*\.?\d{0,2}$/.test(inputValue);
// setError(!isValid);
// if (isValid) {
// setValue(inputValue);
// }
// };
//
// const handleValueBlur = () => {
// const truncatedValue = Number(value).toFixed(2);
// apiRef.current.setEditCellValue({ id, field: valueKey, value: truncatedValue });
// };
//
// const handleUnitsChange = (_event: any, newValue: any) => {
// if (newValue !== null) {
// apiRef.current.setEditCellValue({ id, field: unitKey, value: newValue });
// }
// };
//
// useEffect(() => {
// setValue(row[valueKey]);
// }, [row[valueKey]]);
//
// return (
// <Box sx={{ display: 'flex', flexDirection: 'row', gap: '0.5em', alignItems: 'center' }}>
// <Stack direction="column">
// <Input
// value={value}
// onChange={handleValueChange}
// onBlur={handleValueBlur}
// error={error}
// placeholder={placeholder}
// required
// slotProps={{
// input: {
// 'aria-invalid': error
// }
// }}
// />
// {error && (
// <FormHelperText>
// <Typography color="danger">Only numbers with up to 2 decimal places accepted!</Typography>
// </FormHelperText>
// )}
// </Stack>
// <Select value={row[unitKey]} onChange={handleUnitsChange} placeholder={'Units'} required>
// {unitSelectionOptions.map(option => (
// <Option key={option} value={option}>
// {option}
// </Option>
// ))}
// </Select>
// </Box>
// );
// };
//
// export const renderDBHCell = (params: any) => renderValueCell(params, 'measuredDBH', '');
// export const renderEditDBHCell = (params: any) => renderEditValueCell(params, 'measuredDBH', '', 'Diameter at breast height (DBH)');
// export const renderHOMCell = (params: any) => renderValueCell(params, 'measuredHOM', '');
// export const renderEditHOMCell = (params: any) => renderEditValueCell(params, 'measuredHOM', '', 'Height of Measure (HOM)');
// export const renderStemXCell = (params: any) => renderValueCell(params, 'localStemX', '');
// export const renderEditStemXCell = (params: any) => renderEditValueCell(params, 'localStemX', '', 'Stem Local X Coordinates');
// export const renderStemYCell = (params: any) => renderValueCell(params, 'localStemY', '');
// export const renderEditStemYCell = (params: any) => renderEditValueCell(params, 'localStemY', '', 'Stem Local Y Coordinates');

export const MeasurementsSummaryViewGridColumns: GridColDef[] = standardizeGridColumns([
{
Expand Down Expand Up @@ -376,8 +376,6 @@ export const MeasurementsSummaryViewGridColumns: GridColDef[] = standardizeGridC
return Number(value).toFixed(2);
},
maxWidth: 100,
renderCell: renderStemXCell,
renderEditCell: renderEditStemXCell,
editable: true,
filterOperators: customNumericOperators
},
Expand All @@ -387,12 +385,10 @@ export const MeasurementsSummaryViewGridColumns: GridColDef[] = standardizeGridC
renderHeader: () => formatHeader('Y', 'Stem'),
flex: 0.7,
type: 'number',
maxWidth: 100,
valueFormatter: (value: any) => {
return Number(value).toFixed(2);
},
maxWidth: 100,
renderCell: renderStemYCell,
renderEditCell: renderEditStemYCell,
editable: true,
filterOperators: customNumericOperators
},
Expand All @@ -401,17 +397,19 @@ export const MeasurementsSummaryViewGridColumns: GridColDef[] = standardizeGridC
headerName: 'DBH',
flex: 0.5,
editable: true,
renderCell: renderDBHCell,
renderEditCell: renderEditDBHCell,
valueFormatter: (value: any) => {
return Number(value).toFixed(2);
},
filterOperators: customNumericOperators
},
{
field: 'measuredHOM',
headerName: 'HOM',
flex: 0.5,
editable: true,
renderCell: renderHOMCell,
renderEditCell: renderEditHOMCell,
valueFormatter: (value: any) => {
return Number(value).toFixed(2);
},
filterOperators: customNumericOperators
},
{
Expand Down
5 changes: 3 additions & 2 deletions frontend/components/datagrids/applications/msvdatagrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ const initialMeasurementsSummaryViewRDSRow: MeasurementsSummaryRDS = {
measuredHOM: 0,
isValidated: false,
description: '',
attributes: ''
attributes: '',
userDefinedFields: '',
errors: ''
};

export default function MeasurementsSummaryViewDataGrid() {
Expand Down Expand Up @@ -133,7 +135,6 @@ export default function MeasurementsSummaryViewDataGrid() {
formType={'measurements'}
/>
<MeasurementsCommons
locked={true}
gridType={'measurementssummary'}
gridColumns={MeasurementsSummaryViewGridColumns}
rows={rows}
Expand Down
Loading

0 comments on commit 3c7b488

Please sign in to comment.