Skip to content

Commit

Permalink
refactor: optimize TableEntry component and CSS adjustments
Browse files Browse the repository at this point in the history
This commit brings several optimizations and code structure improvements to the `TableEntry.jsx` component, alongside minor CSS adjustments to ensure consistent text color across light and dark themes.

### Optimizations and Adjustments:
- Ensured consistent use of semicolons and removed unused imports.
- Introduced `BINARY_KEYS`, `TRUE_KEYS`, and `FALSE_KEYS` constants for clearer handling of binary values within `TableEntry`.
- Simplified and consolidated event handler functions (`handleEditClick`, `handleDeleteClick`, `handleSaveClick`, `handleCancelClick`) for cleaner, more readable code.
- Utilized `useEffect` to set keys based on the current table name, refining the component's reactivity to prop changes.
- Streamlined conditional rendering within the Actions component to improve readability.
- Adjusted `select` CSS in `index.css` to ensure text color is explicitly set to black in light mode, addressing an inconsistency in theme styling.

### Code Cleanup:
- Removed unnecessary checks and verbose conditions, adopting more straightforward logic for state management and event handling.
- Simplified the mapping and rendering of `keys` by directly integrating conditional rendering checks into the JSX returned by `map`, improving performance and readability.
- Removed redundant code segments and consolidated duplicate logic for handling binary value conditions within form inputs.

These changes aim to enhance the maintainability of the codebase, improve the component's performance, and ensure a consistent user experience across different themes.
  • Loading branch information
IanSkelskey committed May 21, 2024
1 parent f12a634 commit 8a76782
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 134 deletions.
203 changes: 70 additions & 133 deletions src/components/TableEntry.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { useEffect, useState, forwardRef } from 'react';
import { useAtomValue } from 'jotai';
import { currentTableName } from '../utils/jotai'
import { currentTableName } from '../utils/jotai';
import { AnimatePresence, motion } from 'framer-motion';
import { tableRows } from '../utils/variants';
import { CheckIcon, DeleteIcon, EditIcon, XIcon } from '../assets/icons';
import { getKey, getKeys, getLabel, TABLE_LABELS } from '../const/tableLabels';
import { getKey, getKeys, getLabel } from '../const/tableLabels';
import { getSessionEntryCount, startEntryOperation } from '../utils/firestore';
import { Type, notify } from './Notifier';
import { FormField } from './FormFields';
import { isNumber } from '@syncfusion/ej2-react-spreadsheet';

const BINARY_KEYS = ['noCaptures', 'isAlive', 'dead'];
const TRUE_KEYS = ['Y', 'y', 'T', 't'];
const FALSE_KEYS = ['N', 'n', 'F', 'f'];

export const getValue = (entry, column) => {
if (!entry._document.data.value.mapValue.fields[getKey(column, name)]) {
return 'N/A';
}
return entry._document.data.value.mapValue.fields
[getKey(column, name)].stringValue;
const field = entry._document.data.value.mapValue.fields[getKey(column, name)];
return field ? field.stringValue : 'N/A';
}

export const TableEntry = forwardRef((props, ref) => {
Expand All @@ -28,62 +28,45 @@ export const TableEntry = forwardRef((props, ref) => {

const [entryUIState, setEntryUIState] = useState('viewing');
const [entryData, setEntryData] = useState(entrySnapshot.data());
const [keys, setKeys] = useState();
const [keys, setKeys] = useState([]);
const tableName = useAtomValue(currentTableName);
const [deleteMessage, setDeleteMessage] = useState('Are you sure you want to delete this row?')
const [deleteMessage, setDeleteMessage] = useState('Are you sure you want to delete this row?');

useEffect(() => {
setKeys(getKeys(tableName));
}, [tableName]);

const onEditClickedHandler = () => {
console.log('Edit clicked');
setEntryUIState('editing');
};
const handleEditClick = () => setEntryUIState('editing');

const onDeleteClickedHandler = async () => {
const handleDeleteClick = async () => {
setEntryUIState('deleting');
if (entrySnapshot.ref.parent.id.includes('Session')) {
const entryCount = await getSessionEntryCount(entrySnapshot);
setDeleteMessage(`Are you sure you want to delete this session and its ${entryCount} animal entries?`)
setDeleteMessage(`Are you sure you want to delete this session and its ${entryCount} animal entries?`);
}
};

const onSaveClickedHandler = () => {
entryUIState === 'editing' &&
startEntryOperation(
tableName.includes('Session') ?
'uploadSessionEdits'
:
'uploadEntryEdits',
{
entrySnapshot,
entryData,
setEntryUIState
}
).then(response => notify(...response));
entryUIState === 'deleting' &&
startEntryOperation(
tableName.includes('Session') ?
'deleteSession'
:
'deleteEntry',
{
entrySnapshot,
removeEntryFromUI,
setEntryUIState
}
).then(response => notify(...response));
const handleSaveClick = () => {
const operationType = entryUIState === 'editing'
? (tableName.includes('Session') ? 'uploadSessionEdits' : 'uploadEntryEdits')
: (tableName.includes('Session') ? 'deleteSession' : 'deleteEntry');

startEntryOperation(operationType, {
entrySnapshot,
entryData,
setEntryUIState,
removeEntryFromUI
}).then(response => notify(...response));
};

const onCancelClickedHandler = () => {
const handleCancelClick = () => {
setEntryData(entrySnapshot.data());
setEntryUIState('viewing');
};

useEffect(() => {
setKeys(getKeys(tableName));
}, [])

return (
<motion.tr className="relative hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-all duration-200 ease-in-out"
<motion.tr
className="relative hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-all duration-200 ease-in-out"
variants={tableRows}
initial='initial'
animate='visible'
Expand All @@ -92,90 +75,65 @@ export const TableEntry = forwardRef((props, ref) => {
ref={ref}
>
<Actions
onEditClickedHandler={onEditClickedHandler}
onCancelClickedHandler={onCancelClickedHandler}
onDeleteClickedHandler={onDeleteClickedHandler}
onSaveClickedHandler={onSaveClickedHandler}
onEditClick={handleEditClick}
onCancelClick={handleCancelClick}
onDeleteClick={handleDeleteClick}
onSaveClick={handleSaveClick}
entryUIState={entryUIState}
deleteMessage={deleteMessage}
/>
{keys && keys.map((key) => (
{keys.map(key => (
shownColumns.includes(getLabel(key)) && (
<EntryItem
key={key}
entrySnapshot={entrySnapshot}
entryUIState={entryUIState}
dbKey={key}
entryData={entryData}
setEntryData={setEntryData}
key={key}
/>)
/>
)
))}
</motion.tr>
);
});

const EntryItem = ({ entrySnapshot, dbKey, entryUIState, setEntryData, entryData }) => {
const [displayText, setDisplayText] = useState(entrySnapshot.data()[dbKey]);
const [editable, setEditable] = useState(true);

const BINARY_KEYS = ['noCaptures', 'isAlive', 'dead'];
const TRUE_KEYS = ['Y', 'y', 'T', 't'];
const FALSE_KEYS = ['N', 'n', 'F', 'f'];

const onChangeHandler = (e) => {
if (BINARY_KEYS.includes(dbKey)) {
if (TRUE_KEYS.includes(e.target.value.slice(-1))) {
setEntryData((prevEntryData) => ({
...prevEntryData,
[dbKey]: 'true',
}));
} else if (FALSE_KEYS.includes(e.target.value.slice(-1))) {
setEntryData((prevEntryData) => ({
...prevEntryData,
[dbKey]: 'false',
}));
}
} else {
setEntryData((prevEntryData) => ({
...prevEntryData,
[dbKey]: e.target.value,
}));
}
};

let disabled = false;
const handleChange = (e) => {
const value = e.target.value.slice(-1);
const isBinaryKey = BINARY_KEYS.includes(dbKey);
const isTrueKey = TRUE_KEYS.includes(value);
const isFalseKey = FALSE_KEYS.includes(value);

if (
entryUIState === 'viewing' ||
(entryUIState === 'editing' && !editable) ||
entryUIState === 'deleting'
) {
disabled = true;
}
setEntryData(prev => ({
...prev,
[dbKey]: isBinaryKey ? (isTrueKey ? 'true' : (isFalseKey ? 'false' : prev[dbKey])) : e.target.value,
}));
};

let size = 1;
if (entrySnapshot.data()[dbKey] !== undefined) {
size = String(entrySnapshot.data()[dbKey]).length;
}
const disabled = entryUIState === 'viewing' || (entryUIState === 'editing' && !editable) || entryUIState === 'deleting';
const size = entryData[dbKey] ? String(entryData[dbKey]).length : 1;

return (
<td key={dbKey} className="text-center border-b border-neutral-400 dark:border-neutral-600 p-1">
<td className="text-center border-b border-neutral-400 dark:border-neutral-600 p-1">
<input
disabled={disabled}
className="text-center"
value={entryData[dbKey] ?? 'N/A'}
onChange={(e) => onChangeHandler(e)}
onChange={handleChange}
size={size}
/>
</td>
);
};

const Actions = ({
onEditClickedHandler,
onDeleteClickedHandler,
onSaveClickedHandler,
onCancelClickedHandler,
onEditClick,
onDeleteClick,
onSaveClick,
onCancelClick,
entryUIState,
deleteMessage
}) => {
Expand All @@ -187,56 +145,35 @@ const Actions = ({
<motion.div
key='deleteMsg'
className="absolute text-lg left-8 -top-3 z-10 px-2 rounded-md drop-shadow-xl border-[1px] bg-red-800/10 backdrop-blur border-red-800 shadow-lg shadow-red-800/25 leading-tight"
initial={{
left: '-2rem',
opacity: 0,
}}
animate={{
left: '2rem',
opacity: 1,
}}
exit={{
left: '-20rem',
opacity: 0,
transition: {
opacity: {
duration: .25
},
}
}}
initial={{ left: '-2rem', opacity: 0 }}
animate={{ left: '2rem', opacity: 1 }}
exit={{ left: '-20rem', opacity: 0, transition: { opacity: { duration: 0.25 } } }}
>
{deleteMessage}
</motion.div>
)}
{entryUIState === 'viewing' &&
{entryUIState === 'viewing' && (
<>
<div
className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer"
onClick={() => onEditClickedHandler()}>
<div className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer" onClick={onEditClick}>
<EditIcon />
</div>
<div
className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer"
onClick={() => onDeleteClickedHandler()}>
<div className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer" onClick={onDeleteClick}>
<DeleteIcon />
</div>
</>
}
{(entryUIState === 'editing' || entryUIState === 'deleting') &&
)}
{(entryUIState === 'editing' || entryUIState === 'deleting') && (
<>
<div
className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer"
onClick={() => onSaveClickedHandler()}>
<div className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer" onClick={onSaveClick}>
<CheckIcon />
</div>
<div
className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer"
onClick={() => onCancelClickedHandler()}>
<div className="w-5 h-5 hover:scale-125 transition hover:cursor-pointer" onClick={onCancelClick}>
<XIcon />
</div>
</>}
</>
)}
</AnimatePresence>
</div>
</td>
)
}
);
};
2 changes: 1 addition & 1 deletion src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
}

select {
@apply cursor-pointer w-fit p-1.5 bg-neutral-200 dark:bg-neutral-800 dark:text-white rounded-md border-solid border-2 border-neutral-300 dark:border-neutral-500 focus:border-asu-gold h-10;
@apply cursor-pointer w-fit p-1.5 bg-neutral-200 dark:bg-neutral-800 text-black dark:text-white rounded-md border-solid border-2 border-neutral-300 dark:border-neutral-500 focus:border-asu-gold h-10;
}

input {
Expand Down

0 comments on commit 8a76782

Please sign in to comment.