now creating dewars, pucks and samples from spreadsheet and replacing dewars if a dewar with the same name exists

This commit is contained in:
GotthardG 2024-11-12 17:03:38 +01:00
parent 86883133a7
commit 8f7c90bab0
5 changed files with 327 additions and 134 deletions

View File

@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, String, Date, ForeignKey from sqlalchemy import Column, Integer, String, Date, ForeignKey, JSON
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from app.database import Base from app.database import Base
from app.calculations import calculate_number_of_pucks, calculate_number_of_samples from app.calculations import calculate_number_of_pucks, calculate_number_of_samples
@ -107,6 +107,7 @@ class Sample(Base):
id = Column(Integer, primary_key=True, index=True, autoincrement=True) id = Column(Integer, primary_key=True, index=True, autoincrement=True)
sample_name = Column(String, index=True) # Matches `sample_name` in data creation sample_name = Column(String, index=True) # Matches `sample_name` in data creation
position = Column(Integer) # Matches `position` in data creation script position = Column(Integer) # Matches `position` in data creation script
data_collection_parameters = Column(JSON, nullable=True)
# Foreign keys and relationships # Foreign keys and relationships
puck_id = Column(Integer, ForeignKey('pucks.id')) puck_id = Column(Integer, ForeignKey('pucks.id'))

View File

@ -1,9 +1,12 @@
from fastapi import APIRouter, HTTPException, status, Depends from fastapi import APIRouter, HTTPException, status, Depends
from sqlalchemy.orm import Session, joinedload from sqlalchemy.orm import Session, joinedload
from typing import List from typing import List
import uuid import logging
from sqlalchemy.exc import SQLAlchemyError
from pydantic import ValidationError
from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate from app.schemas import Dewar as DewarSchema, DewarCreate, DewarUpdate
from app.models import Dewar as DewarModel, Puck as PuckModel from app.models import Dewar as DewarModel, Puck as PuckModel, \
Sample as SampleModel # Assuming SampleModel is defined in models
from app.dependencies import get_db from app.dependencies import get_db
router = APIRouter() router = APIRouter()
@ -17,24 +20,55 @@ async def get_dewars(db: Session = Depends(get_db)):
@router.post("/", response_model=DewarSchema, status_code=status.HTTP_201_CREATED) @router.post("/", response_model=DewarSchema, status_code=status.HTTP_201_CREATED)
async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> DewarSchema: async def create_dewar(dewar: DewarCreate, db: Session = Depends(get_db)) -> DewarSchema:
db_dewar = DewarModel( try:
dewar_name=dewar.dewar_name, db_dewar = DewarModel(
tracking_number=dewar.tracking_number, dewar_name=dewar.dewar_name,
status=dewar.status, tracking_number=dewar.tracking_number,
ready_date=dewar.ready_date, status=dewar.status,
shipping_date=dewar.shipping_date, ready_date=dewar.ready_date,
arrival_date=dewar.arrival_date, shipping_date=dewar.shipping_date,
returning_date=dewar.returning_date, arrival_date=dewar.arrival_date,
qrcode=dewar.qrcode, returning_date=dewar.returning_date,
contact_person_id=dewar.contact_person_id, qrcode=dewar.qrcode,
return_address_id=dewar.return_address_id contact_person_id=dewar.contact_person_id,
) return_address_id=dewar.return_address_id
)
db.add(db_dewar) db.add(db_dewar)
db.commit() db.commit()
db.refresh(db_dewar) db.refresh(db_dewar)
return db_dewar for puck_data in dewar.pucks:
puck = PuckModel(
dewar_id=db_dewar.id,
puck_name=puck_data.puck_name,
puck_type=puck_data.puck_type,
puck_location_in_dewar=puck_data.puck_location_in_dewar,
)
db.add(puck)
db.commit()
db.refresh(puck)
for sample_data in puck_data.samples:
sample = SampleModel(
puck_id=puck.id,
sample_name=sample_data.sample_name,
position=sample_data.position,
# Ensure only valid attributes are set
data_collection_parameters=sample_data.data_collection_parameters,
)
db.add(sample)
db.commit()
db.refresh(sample)
return db_dewar
except SQLAlchemyError as e:
logging.error(f"Database error occurred: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
except ValidationError as e:
logging.error(f"Validation error occurred: {e}")
raise HTTPException(status_code=400, detail="Validation error")
@router.get("/{dewar_id}", response_model=DewarSchema) @router.get("/{dewar_id}", response_model=DewarSchema)

View File

@ -9,7 +9,7 @@ from sqlalchemy.exc import SQLAlchemyError
from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, \ from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, \
Proposal as ProposalModel, Dewar as DewarModel, Puck as PuckModel, Sample as SampleModel Proposal as ProposalModel, Dewar as DewarModel, Puck as PuckModel, Sample as SampleModel
from app.schemas import ShipmentCreate, UpdateShipmentComments, Shipment as ShipmentSchema, DewarUpdate, \ from app.schemas import ShipmentCreate, UpdateShipmentComments, Shipment as ShipmentSchema, DewarUpdate, \
ContactPerson as ContactPersonSchema, Sample as SampleSchema, DewarCreate, PuckCreate, SampleCreate ContactPerson as ContactPersonSchema, Sample as SampleSchema, DewarCreate, PuckCreate, SampleCreate, DewarSchema
from app.database import get_db from app.database import get_db
from app.crud import get_shipments, get_shipment_by_id from app.crud import get_shipments, get_shipment_by_id
@ -38,6 +38,19 @@ async def fetch_shipments(id: Optional[int] = Query(None), db: Session = Depends
logging.info(f"Shipment ID: {shipment.id}, Shipment Name: {shipment.shipment_name}") logging.info(f"Shipment ID: {shipment.id}, Shipment Name: {shipment.shipment_name}")
return shipments return shipments
@router.get("/{shipment_id}/dewars", response_model=List[DewarSchema])
async def get_dewars_by_shipment_id(shipment_id: int, db: Session = Depends(get_db)):
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found")
dewars = db.query(DewarModel).filter(DewarModel.shipment_id == shipment_id).all()
if not dewars:
raise HTTPException(status_code=404, detail="No dewars found for this shipment")
return dewars
@router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED) @router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)): async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)):
@ -220,58 +233,55 @@ async def update_shipment_comments(shipment_id: int, comments_data: UpdateShipme
@router.post("/{shipment_id}/add_dewar_puck_sample", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED) @router.post("/{shipment_id}/add_dewar_puck_sample", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
async def add_dewar_puck_sample_to_shipment( def add_dewar_puck_sample_to_shipment(shipment_id: int, payload: DewarCreate, db: Session = Depends(get_db)):
shipment_id: int,
payload: DewarCreate,
db: Session = Depends(get_db)
):
shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.id == shipment_id).first()
if not shipment: if not shipment:
logging.error(f"Shipment not found with ID: {shipment_id}")
raise HTTPException(status_code=404, detail="Shipment not found") raise HTTPException(status_code=404, detail="Shipment not found")
try: try:
dewar = DewarModel( for dewar_data in payload.dewars:
shipment_id=shipment_id, dewar = db.query(DewarModel).filter(DewarModel.dewar_name == dewar_data.dewar_name).first()
dewar_name=payload.dewar_name, if dewar:
tracking_number=payload.tracking_number, # Update existing dewar
status=payload.status, dewar.tracking_number = dewar_data.tracking_number
contact_person_id=payload.contact_person_id, dewar.status = dewar_data.status
return_address_id=payload.return_address_id,
)
db.add(dewar)
db.commit()
db.refresh(dewar)
for puck_data in payload.pucks:
puck = PuckModel(
dewar_id=dewar.id,
puck_name=puck_data.puck_name,
puck_type=puck_data.puck_type,
puck_location_in_dewar=puck_data.puck_location_in_dewar,
)
db.add(puck)
db.commit()
db.refresh(puck)
for sample_data in puck_data.samples:
sample = SampleModel(
puck_id=puck.id,
sample_name=sample_data.sample_name,
position=sample_data.position,
data_collection_parameters=sample_data.data_collection_parameters,
)
db.add(sample)
db.commit() db.commit()
db.refresh(sample) else:
dewar = DewarModel(
shipment_id=shipment_id,
dewar_name=dewar_data.dewar_name,
tracking_number=dewar_data.tracking_number,
status=dewar_data.status,
)
db.add(dewar)
db.commit()
db.refresh(dewar)
for puck_data in dewar_data.pucks:
puck = PuckModel(
dewar_id=dewar.id,
puck_name=puck_data.puck_name,
puck_type=puck_data.puck_type,
puck_location_in_dewar=puck_data.puck_location_in_dewar,
)
db.add(puck)
db.commit()
db.refresh(puck)
for sample_data in puck_data.samples:
sample = SampleModel(
puck_id=puck.id,
sample_name=sample_data.sample_name,
position=sample_data.position,
)
db.add(sample)
db.commit()
db.refresh(sample)
db.refresh(shipment) db.refresh(shipment)
except SQLAlchemyError as e: except SQLAlchemyError as e:
logging.error(f"Database error occurred: {e}") raise HTTPException(status_code=500, detail=f"Database error: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
except ValidationError as e: except ValidationError as e:
logging.error(f"Validation error occurred: {e}") raise HTTPException(status_code=400, detail=f"Validation error: {e}")
raise HTTPException(status_code=400, detail="Validation error")
logging.info(f"Successfully added dewar, puck, and sample for shipment ID: {shipment_id}") return shipment
return shipment

View File

@ -101,7 +101,8 @@ class Sample(BaseModel):
class SampleCreate(BaseModel): class SampleCreate(BaseModel):
sample_name: str = Field(..., alias="crystalname") sample_name: str = Field(..., alias="crystalname")
position: int = Field(..., alias="positioninpuck") position: int = Field(..., alias="positioninpuck")
data_collection_parameters: DataCollectionParameters data_collection_parameters: Optional[DataCollectionParameters] = None
results: Optional[Results] = None
class Config: class Config:
populate_by_name = True populate_by_name = True
@ -115,8 +116,11 @@ class PuckBase(BaseModel):
puck_location_in_dewar: int puck_location_in_dewar: int
class PuckCreate(PuckBase): class PuckCreate(BaseModel):
pass puck_name: str
puck_type: str
puck_location_in_dewar: int
samples: List[SampleCreate] = []
class PuckUpdate(BaseModel): class PuckUpdate(BaseModel):
@ -152,6 +156,7 @@ class DewarBase(BaseModel):
qrcode: str qrcode: str
contact_person_id: Optional[int] contact_person_id: Optional[int]
return_address_id: Optional[int] return_address_id: Optional[int]
pucks: List[PuckCreate] = []
class DewarCreate(DewarBase): class DewarCreate(DewarBase):
@ -181,6 +186,17 @@ class DewarUpdate(BaseModel):
contact_person_id: Optional[int] = None contact_person_id: Optional[int] = None
address_id: Optional[int] = None address_id: Optional[int] = None
class DewarSchema(BaseModel):
id: int
dewar_name: str
tracking_number: str
status: str
contact_person_id: int
return_address_id: int
class Config:
from_attributes = True
# Proposal schemas # Proposal schemas
class Proposal(BaseModel): class Proposal(BaseModel):

View File

@ -11,7 +11,12 @@ import {
TextField, TextField,
Typography, Typography,
Button, Button,
Box Box,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle
} from '@mui/material'; } from '@mui/material';
import { SpreadsheetService, ShipmentsService, DewarsService, ApiError } from '../../openapi'; import { SpreadsheetService, ShipmentsService, DewarsService, ApiError } from '../../openapi';
import * as ExcelJS from 'exceljs'; import * as ExcelJS from 'exceljs';
@ -30,6 +35,9 @@ const SpreadsheetTable = ({
const [editingCell, setEditingCell] = useState({}); const [editingCell, setEditingCell] = useState({});
const [nonEditableCells, setNonEditableCells] = useState(new Set()); const [nonEditableCells, setNonEditableCells] = useState(new Set());
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [showUpdateDialog, setShowUpdateDialog] = useState(false);
const [dewarsToReplace, setDewarsToReplace] = useState([]);
const [dewarsToCreate, setDewarsToCreate] = useState(new Map());
const initialNewDewarState = { const initialNewDewarState = {
number_of_pucks: 0, number_of_pucks: 0,
@ -44,7 +52,7 @@ const SpreadsheetTable = ({
dewar_name: '', dewar_name: '',
tracking_number: 'UNKNOWN', tracking_number: 'UNKNOWN',
status: 'In preparation', status: 'In preparation',
pucks: [] pucks: [] // Ensure 'pucks' array exists
}; };
const [newDewar, setNewDewar] = useState(initialNewDewarState); const [newDewar, setNewDewar] = useState(initialNewDewarState);
@ -141,36 +149,80 @@ const SpreadsheetTable = ({
'dewarname': 0, 'dewarname': 0,
'puckname': 1, 'puckname': 1,
'pucktype': 2, 'pucktype': 2,
// Add other fields as needed 'crystalname': 3,
'positioninpuck': 4,
'priority': 5,
'comments': 6,
'directory': 7,
'proteinname': 8,
'oscillation': 9,
'aperture': 10,
'exposure': 11,
'totalrange': 12,
'transmission': 13,
'dose': 14,
'targetresolution': 15,
'datacollectiontype': 16,
'processingpipeline': 17,
'spacegroupnumber': 18,
'cellparameters': 19,
'rescutkey': 20,
'rescutvalue': 21,
'userresolution': 22,
'pdbid': 23,
'autoprocfull': 24,
'procfull': 25,
'adpenabled': 26,
'noano': 27,
'ffcscampaign': 28,
'trustedhigh': 29,
'autoprocextraparams': 30,
'chiphiangles': 31
}; };
const createDewarsFromSheet = async (data, contactPerson, returnAddress) => { const checkIfDewarExists = async (dewarName) => {
if (!selectedShipment) return null;
try {
const shipDewars = await ShipmentsService.getDewarsByShipmentIdShipmentsShipmentIdDewarsGet(selectedShipment.id);
return shipDewars.find((d) => d.dewar_name === dewarName);
} catch (error) {
console.error('Failed to fetch existing dewars:', error);
return null;
}
};
const createOrUpdateDewarsFromSheet = async (data, contactPerson, returnAddress) => {
if (!contactPerson?.id || !returnAddress?.id) { if (!contactPerson?.id || !returnAddress?.id) {
console.error('contact_person_id or return_address_id is missing'); console.error('contact_person_id or return_address_id is missing');
return null; return null;
} }
const dewars = new Map(); const dewars = new Map();
const puckPositionMap = new Map();
const dewarsToReplace = [];
const dewarNameIdx = fieldToCol['dewarname']; const dewarNameIdx = fieldToCol['dewarname'];
const puckNameIdx = fieldToCol['puckname']; const puckNameIdx = fieldToCol['puckname'];
const puckTypeIdx = fieldToCol['pucktype']; const puckTypeIdx = fieldToCol['pucktype'];
const sampleNameIdx = fieldToCol['crystalname'];
const samplePositionIdx = fieldToCol['positioninpuck'];
let puckPositionInDewar = 1; for (let rowIndex = 0; rowIndex < data.length; rowIndex++) {
const row = data[rowIndex];
for (const row of data) {
if (!row.data) { if (!row.data) {
console.error(`Row data is missing`); console.error('Row data is missing');
continue; continue;
} }
const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null; const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null;
const puckName = typeof row.data[puckNameIdx] === 'string' ? row.data[puckNameIdx].trim() : null; const puckName = row.data[puckNameIdx] !== undefined && row.data[puckNameIdx] !== null ? String(row.data[puckNameIdx]).trim() : null;
const puckType = typeof row.data[puckTypeIdx] === 'string' ? row.data[puckTypeIdx] : 'Unipuck'; const puckType = typeof row.data[puckTypeIdx] === 'string' ? row.data[puckTypeIdx] : 'Unipuck';
const sampleName = typeof row.data[sampleNameIdx] === 'string' ? row.data[sampleNameIdx].trim() : null;
const samplePosition = row.data[samplePositionIdx] !== undefined && row.data[samplePositionIdx] !== null ? Number(row.data[samplePositionIdx]) : null;
console.log(`Processing Dewar: ${dewarName}, Puck: ${puckName}, Type: ${puckType}`); if (dewarName && puckName) {
if (dewarName) {
let dewar; let dewar;
if (!dewars.has(dewarName)) { if (!dewars.has(dewarName)) {
dewar = { dewar = {
@ -181,44 +233,116 @@ const SpreadsheetTable = ({
pucks: [] pucks: []
}; };
dewars.set(dewarName, dewar); dewars.set(dewarName, dewar);
puckPositionInDewar = 1; puckPositionMap.set(dewarName, new Map());
console.log(`Created new dewar: ${dewarName}`);
// Check if the dewar exists in the shipment
const existingDewar = await checkIfDewarExists(dewarName);
if (existingDewar) {
dewarsToReplace.push(existingDewar);
}
} else { } else {
dewar = dewars.get(dewarName); dewar = dewars.get(dewarName);
puckPositionInDewar++;
console.log(`Found existing dewar: ${dewarName}`);
} }
const puck = { let puckPositions = puckPositionMap.get(dewarName);
puck_name: puckName || 'test', // Fixed puck name if (!puckPositions.has(puckName)) {
puck_type: puckType || 'Unipuck', // Fixed puck type puckPositions.set(puckName, puckPositions.size + 1);
puck_position_in_dewar: puckPositionInDewar }
}; const puckPosition = puckPositions.get(puckName);
dewar.pucks.push(puck);
console.log(`Added puck: ${JSON.stringify(puck)}`); let puck = dewar.pucks.find(p => p.puck_name === puckName);
if (!puck) {
puck = {
puck_name: puckName,
puck_type: puckType,
puck_location_in_dewar: puckPosition,
samples: []
};
dewar.pucks.push(puck);
}
const sample = {
sample_name: sampleName,
position: samplePosition,
results: null // Placeholder for results field
};
if (isNaN(sample.position)) {
console.error(`Invalid sample position for sample ${sample.sample_name} in puck ${puckName}`);
} else {
puck.samples.push(sample);
}
} else { } else {
console.error('Dewar name is missing in the row'); if (!dewarName) {
console.error(`Dewar name is missing in row ${rowIndex}`);
}
if (!puckName) {
console.error(`Puck name is missing in row ${rowIndex}`);
}
} }
} }
const dewarsArray = Array.from(dewars.values()); const dewarsArray = Array.from(dewars.values());
// Save dewars array for later use in handleConfirmUpdate
setDewarsToCreate(dewars);
if (dewarsArray.length > 0 && dewarsToReplace.length > 0) {
setDewarsToReplace(dewarsToReplace);
setShowUpdateDialog(true);
} else {
await handleDewarCreation(dewarsArray);
}
};
const handleConfirmUpdate = async () => {
if (dewarsToReplace.length === 0) return;
try {
for (const dewar of dewarsToReplace) {
await DewarsService.deleteDewarDewarsDewarIdDelete(dewar.id);
}
const dewarsArray = Array.from(dewarsToCreate.values());
await handleDewarCreation(dewarsArray);
console.log('Dewars replaced successfully');
} catch (error) {
console.error('Error replacing dewar', error);
if (error instanceof ApiError && error.body) {
console.error('Validation errors:', error.body.detail);
} else {
console.error('Unexpected error:', error);
}
}
setShowUpdateDialog(false);
setDewarsToReplace([]);
setDewarsToCreate(new Map());
};
const handleCancelUpdate = () => {
setShowUpdateDialog(false);
setDewarsToReplace([]);
setDewarsToCreate(new Map());
};
const handleDewarCreation = async (dewarsArray) => {
for (const dewar of dewarsArray) { for (const dewar of dewarsArray) {
try { try {
// Call to create the dewar if (!dewar.pucks || dewar.pucks.length === 0) {
const createdDewar = await DewarsService.createDewarDewarsPost(dewar); console.error(`Dewar ${dewar.dewar_name} does not have any pucks.`);
console.log(`Created dewar: ${createdDewar.id}`); continue;
}
const createdDewar = await DewarsService.createDewarDewarsPost(dewar);
// Add dewar to the shipment if created successfully
if (createdDewar && selectedShipment) { if (createdDewar && selectedShipment) {
await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost( await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(
selectedShipment.id, selectedShipment.id,
createdDewar.id createdDewar.id
); );
console.log(`Added dewar to shipment: ${createdDewar.id}`); console.log(`Dewar ${createdDewar.dewar_name} with ID ${createdDewar.id} created and added to the shipment.`);
} }
} catch (error) { } catch (error) {
console.error(`Error adding dewar`, error); console.error('Error adding dewar', error);
if (error instanceof ApiError && error.body) { if (error instanceof ApiError && error.body) {
console.error('Validation errors:', error.body.detail); console.error('Validation errors:', error.body.detail);
} else { } else {
@ -226,8 +350,6 @@ const SpreadsheetTable = ({
} }
} }
} }
return dewarsArray;
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
@ -241,18 +363,12 @@ const SpreadsheetTable = ({
setIsSubmitting(true); setIsSubmitting(true);
console.log('All data is valid. Proceeding with submission...'); console.log('All data is valid. Proceeding with submission...');
const processedDewars = await createDewarsFromSheet( await createOrUpdateDewarsFromSheet(
raw_data, raw_data,
selectedShipment?.contact_person, selectedShipment?.contact_person,
selectedShipment?.return_address selectedShipment?.return_address
); );
if (processedDewars && processedDewars.length > 0) {
console.log('Dewars processed successfully.');
} else {
console.error('No valid dewars were created.');
}
setIsSubmitting(false); setIsSubmitting(false);
} else { } else {
console.log('There are validation errors in the dataset. Please correct them before submission.'); console.log('There are validation errors in the dataset. Please correct them before submission.');
@ -270,22 +386,12 @@ const SpreadsheetTable = ({
}); });
}); });
const buffer = await workbook.xlsx.writeBuffer(); workbook.xlsx.writeBuffer().then((buffer) => {
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
saveAs(blob, 'corrected_spreadsheet.xlsx');
saveAs(blob, 'corrected_data.xlsx'); });
}; };
useEffect(() => {
console.log('Raw data:', raw_data);
console.log('Errors:', localErrors);
console.log('Headers:', headers);
}, [raw_data, localErrors, headers]);
if (!raw_data || !headers) {
return <div>Loading...</div>;
}
return ( return (
<TableContainer component={Paper}> <TableContainer component={Paper}>
<Table> <Table>
@ -323,21 +429,25 @@ const SpreadsheetTable = ({
return ( return (
<TableCell key={colIndex} align="center"> <TableCell key={colIndex} align="center">
<Tooltip title={errorMessage || ""} arrow disableHoverListener={!isInvalid}> <Tooltip title={errorMessage || ""} arrow disableHoverListener={!isInvalid}>
<TextField {isInvalid ? (
value={editingValue !== undefined ? editingValue : cellValue} <TextField
onChange={(e) => setEditingCell({ ...editingCell, [`${rowIndex}-${colIndex}`]: e.target.value })} value={editingValue !== undefined ? editingValue : cellValue}
onKeyDown={(e) => { onChange={(e) => setEditingCell({ ...editingCell, [`${rowIndex}-${colIndex}`]: e.target.value })}
if (e.key === "Enter") { onKeyDown={(e) => {
handleCellEdit(rowIndex, colIndex); if (e.key === "Enter") {
} handleCellEdit(rowIndex, colIndex);
}} }
onBlur={() => handleCellBlur(rowIndex, colIndex)} }}
error={isInvalid} onBlur={() => handleCellBlur(rowIndex, colIndex)}
fullWidth error={isInvalid}
variant="outlined" fullWidth
size="small" variant="outlined"
disabled={isReadonly} size="small"
/> disabled={isReadonly}
/>
) : (
cellValue
)}
</Tooltip> </Tooltip>
</TableCell> </TableCell>
); );
@ -346,6 +456,28 @@ const SpreadsheetTable = ({
))} ))}
</TableBody> </TableBody>
</Table> </Table>
<Dialog
open={showUpdateDialog}
onClose={handleCancelUpdate}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Replace Dewars</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
The following dewars already exist: {dewarsToReplace.map(dewar => dewar.dewar_name).join(', ')}. Would you like to replace them?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleCancelUpdate} color="primary">
Cancel
</Button>
<Button onClick={handleConfirmUpdate} color="primary" autoFocus>
Replace
</Button>
</DialogActions>
</Dialog>
</TableContainer> </TableContainer>
); );
}; };