Skip to content

Instantly share code, notes, and snippets.

@fatso83
Last active August 15, 2021 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fatso83/6a399a9b720297d5c20386af68a38484 to your computer and use it in GitHub Desktop.
Save fatso83/6a399a9b720297d5c20386af68a38484 to your computer and use it in GitHub Desktop.
Pay bananas, get monkeys 🤯
/*
This is a typical example of a codebase I inherited 😭
I mean, where do I even start ...
- direct DOM manipulation
- toggling display: none instead of not rendering, ...
- unclear data paths and constant heavy lookups
Oh, are you surprised that this stinking pile of shit is full of bugs? Me neither.
#kilowott #ninestack
*/
import React from 'react';
import { Workouts, App } from 'app-interfaces';
import {
useStateValue,
metricConverter,
isInt,
} from '../../bin/config/AppContext';
import { makeStyles } from '@material-ui/core/styles';
import { Button } from '@material-ui/core';
import { ExerciseDetailsComponent as ExerciseDetails } from '../Exercises';
import Table from './Table';
import { useTranslation } from 'react-i18next';
import uuid from 'uuid/v4';
import { CatalogClass } from '../../bin/controllers';
const useStyles = makeStyles((theme) => ({
tablList: {
'& .step-indicator': {
width:"60%",
textAlign: 'center',
marginTop: 15,
// marginLeft: '31%',
right:"90px",
// position:"sticky",
position:"absolute",
[theme.breakpoints.down('md')]: {
marginTop: 15,
width:"100%",
marginLeft: '0%',
right:'unset'
},
},
'& .action-group': {
marginTop: -30,
[theme.breakpoints.down('md')]: {
marginTop: -35,
},
'& .MuiButton-root': {
height: 40,
width: 95,
textTransform: 'capitalize',
color: '#ffffff',
fontSize: 14,
fontWeight: 600,
'&.prev': {
float: 'left',
backgroundColor: '#333333',
},
'&.next': {
float: 'right',
backgroundColor: 'var(--text-primary)',
},
},
},
},
drawerTab:{
// display:"flex",
// justifyContent:"space-between",
width:"25vw",
"& .tabDiv2":{
width:"44vw",
marginTop:"1rem"
}
}
}));
export default function StepFormComponent(props: {
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
supersetList?: Workouts.IExercise[] | null;
addExercise?: Workouts.IExercise[] | null;
superset?: Workouts.IExerciseSuperSet | null;
children?: JSX.Element | JSX.Element[];
}) {
const { metric } = useStateValue() as { metric: App.State['metric'] };
const initialTab = 0;
const [superset, setSuperset] = React.useState(props.superset);
const [addExercise, setAddExercise] = React.useState(props.addExercise);
const [supersetList, setSupersetList] = React.useState(props.supersetList);
const [currentTab, dispatch] = React.useReducer(reduceCurrentTab, initialTab);
function handleChange(form: HTMLFormElement) {
const formData = new FormData(form);
const newExerciseList = formData.get('exercises');
if (newExerciseList && typeof newExerciseList === 'string') {
if (supersetList) return setSupersetList(JSON.parse(newExerciseList));
if (addExercise) return setAddExercise(JSON.parse(newExerciseList));
if (superset)
return setSuperset({
id: superset && superset.id ? superset.id : uuid(),
exercises: JSON.parse(newExerciseList),
});
}
}
function reduceCurrentTab(
tab: number,
action: { type: string; payload?: any }
) {
const form = document.getElementById('step-form') as HTMLFormElement | null;
if (!form) return tab;
switch (action.type) {
case 'reset':
handleChange(form);
break;
case 'invalid':
return tab;
case 'prev':
if (tab === initialTab) return initialTab;
return tab - 1;
case 'next':
if (tab === initialTab) {
handleChange(form);
return tab + 1;
}
return tab + 1;
case 'submit':
handleChange(form);
form.dispatchEvent(new Event('submit'));
break;
default:
throw new Error();
}
return initialTab;
}
// Handle Prev, Next / Submit button action:
const handleAction = (e: React.MouseEvent) => {
const action = e.currentTarget.id;
const form = document.getElementById('step-form') as HTMLFormElement | null;
const tabs = [...document.getElementsByClassName('tab')] as
| HTMLElement[]
| null;
if (!action || !form || !tabs || !tabs[currentTab]) return;
const formData = new FormData(form);
const exercises = formData.get('exercises');
if (typeof exercises === 'string' && JSON.parse(exercises).length === 0)
return dispatch({ type: 'reset' });
// Hide current tab:
tabs[currentTab].style.display = 'none';
switch (action) {
case 'prevBtn':
if (currentTab === 1) {
const stepsIndicator=document.getElementById("setMarginTop")
if(stepsIndicator && window.innerWidth>700){
stepsIndicator.style.top="unset"
}
}
return dispatch({ type: 'prev' });
case 'nextBtn':
// If current tab is first tab then move to next tab:
if (currentTab === 0) {
return dispatch({ type: 'next' });
}
// ...else format and update data input field:
else {
// Get current exercise id:
const exercise_id =
(supersetList &&
supersetList[currentTab - 1] &&
supersetList[currentTab - 1].exercise_id) ||
(addExercise &&
addExercise[currentTab - 1] &&
addExercise[currentTab - 1].exercise_id) ||
(superset &&
superset.exercises[
currentTab === 0 ? currentTab : currentTab - 1
] &&
superset.exercises[currentTab === 0 ? currentTab : currentTab - 1]
.exercise_id);
// Collect input fields:
const tabInputs = superset
? ([
...tabs[
currentTab === 0 ? currentTab : currentTab - 1
].querySelectorAll('input.form-control'),
] as HTMLInputElement[] | null)
: ([...tabs[currentTab].querySelectorAll('input.form-control')] as
| HTMLInputElement[]
| null);
// Get target input field:
const dataInput = document.getElementById(
'data'
) as HTMLInputElement | null;
if (!exercise_id || !dataInput || !tabInputs) return;
const initialValues = {
name: tabInputs[0].name,
value: Number(tabInputs[0].value),
};
const newEntry = {
id: uuid(),
exercise_id,
score_count: 0,
score_type: initialValues.name,
score_cols: [initialValues.name],
score_vals: [
{
type: initialValues.name,
value: [
metricConverter(
initialValues.value,
`${initialValues.name}O`,
metric
),
],
},
],
result_vals: [],
notes_sets: [],
exercise_notes: null,
};
const reduceExerciseItem = (
accum: Workouts.IExercise,
tabInput: HTMLInputElement
) => {
// if input name is same as score type, update score count and score vals:
if (tabInput.name === accum.score_type) {
if (isInt(tabInput.name) && tabInput.valueAsNumber % 1 !== 0) {
if (!tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid');
} else {
if (tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid');
}
accum.score_vals[0].value[accum.score_count] = metricConverter(
Number(tabInput.value),
`${tabInput.name}O`,
metric
);
accum.score_count = accum.score_count + 1;
return accum;
}
// else if input name is exercise_notes update exercise_notes:
else if (tabInput.name === 'exercise_notes') {
accum.exercise_notes = tabInput.value;
return accum;
}
// else if input name is notes_sets, update notes_sets:
else if (tabInput.name === 'notes_sets') {
const targetData = tabInput.parentElement as HTMLTableDataCellElement | null;
if (!targetData) return accum;
const setIndex = targetData.cellIndex - 1;
accum.notes_sets[setIndex] = tabInput.value;
return accum;
}
// else if input name exists in score cols update score values:
else if (accum.score_cols.find((el) => el === tabInput.name)) {
if (isInt(tabInput.name) && !tabInput.valueAsNumber) {
if (tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid');
} else if (
isInt(tabInput.name) &&
tabInput.valueAsNumber % 1 !== 0
) {
if (!tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid');
} else {
if (tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid');
}
const targetData = tabInput.parentElement as HTMLTableDataCellElement | null;
if (!targetData) return accum;
const setIndex = targetData.cellIndex - 1;
accum.score_vals[
accum.score_cols.findIndex((el) => el === tabInput.name)
].value[setIndex] = metricConverter(
Number(tabInput.value),
`${tabInput.name}O`,
metric
);
return accum;
}
// else input name doesn't exist, add score col and score val:
else {
if (isInt(tabInput.name) && !tabInput.valueAsNumber) {
if (tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid');
} else if (
isInt(tabInput.name) &&
tabInput.valueAsNumber % 1 !== 0
) {
if (!tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid', true);
} else {
if (tabInput.classList.contains('is-invalid'))
tabInput.classList.toggle('is-invalid', false);
}
accum.score_cols.push(tabInput.name);
accum.score_vals.push({
type: tabInput.name,
value: [
metricConverter(
Number(tabInput.value),
`${tabInput.name}O`,
metric
),
],
});
return accum;
}
};
const currentExerciseList = JSON.parse(
dataInput.value
) as Workouts.IExercise[];
const newExerciseItem = [...tabInputs].reduce(
reduceExerciseItem,
newEntry
);
Object.assign(
currentExerciseList[currentTab === 0 ? currentTab : currentTab - 1],
newExerciseItem
);
// Update data input value:
dataInput.value = JSON.stringify(currentExerciseList);
// Check metric values
if (
[...tabInputs].find((el) => el.classList.contains('is-invalid'))
) {
tabs[currentTab].style.display = 'block';
// tabs[currentTab][0].style.width = 'unset';
// tabs[currentTab][0].style.paddingRight = 0;
// tabs[currentTab][1].style.width = 'unset';
// tabs[currentTab][1].style.marginTop = 0;
// return dispatch({ type: 'invalid' });
}
// If on the last tab then submit form:
if (
(tabs.length > 1 && currentTab === tabs.length - 1) ||
(currentTab === 0 && superset && superset.exercises.length === 1)
) {
return dispatch({ type: 'submit', payload: dataInput.value });
}
// ... else move to next tab:
return dispatch({ type: 'next' });
}
default:
return dispatch({ type: 'reset' });
}
};
// Handle current tab
const showTab = React.useCallback(() => {
const tabs = document.getElementsByClassName(
'tab'
) as HTMLCollectionOf<HTMLElement> | null;
const prevBtn = document.getElementById(
'prevBtn'
) as HTMLButtonElement | null;
const nextBtn = document.getElementById(
'nextBtn'
) as HTMLButtonElement | null;
if (!tabs || !tabs[currentTab] || !prevBtn || !nextBtn) return;
const temp= document.getElementById("superset")
// const temp2= document.getElementById("tablist-container")
// console.log(temp2?.children[1].className)
// const oldClass=temp2?.children[1].className
if(currentTab){
if(temp){
// temp2?.children[1].setAttribute("class", `${oldClass} scroller`)
window.innerWidth>700 && temp.children[2].setAttribute("style", "width: 87vw")
// console.log(temp)
}
}
else{
if(temp){
window.innerWidth>700 && temp.children[2].setAttribute("style", "width: 512px")
// temp2?.children[1].setAttribute("class", `tab`)
}
}
// Handle step indicator:
const fixStepIndicator = (n: number) => {
// Remove class from all steps:
const steps = document.getElementsByClassName(
'step'
) as HTMLCollectionOf<HTMLSpanElement> | null;
if (!steps || !steps[n]) return;
[...steps].forEach((v, i) => {
steps[i].className = steps[i].className.replace(' active', '');
});
// If on first tab remove finish class:
if (n === 0)
steps[n].className = steps[n].className.replace(' finish', '');
// ... else add finish class to previous step:
if (n !== 0) steps[n - 1].className += ' finish';
// ... add active class to current step:
steps[n].className = steps[n].className.replace(' finish', '');
steps[n].className += ' active';
};
// Display the current tab:
tabs[currentTab].style.display = 'block';
let currentTabTabs = tabs[currentTab].children as HTMLCollectionOf<HTMLElement>;
currentTabTabs.length > 1 && (currentTabTabs[0].style.width = 'unset');
currentTabTabs.length > 1 && (currentTabTabs[0].style.paddingRight = "0px");
currentTabTabs.length > 1 && (currentTabTabs[1].style.width = 'unset');
currentTabTabs.length > 1 && (currentTabTabs[1].style.marginTop = "0px");
// Fix previous button:
if (currentTab === 0) {
prevBtn.style.display = 'none';
} else {
prevBtn.style.display = 'inline';
}
// Fix next button:
if (currentTab === tabs.length - 1) {
// console.log(props.superset && props.superset.exercises.length);
if (currentTab === 0 && superset && superset.exercises.length === 1)
nextBtn.innerHTML = 'Submit';
else if (currentTab === 0) nextBtn.innerHTML = 'Next';
else nextBtn.innerHTML = 'Submit';
} else {
nextBtn.innerHTML = 'Next';
}
// Fix step indicators:
fixStepIndicator(currentTab);
}, [currentTab, superset]);
// Effects
React.useEffect(showTab, [currentTab]);
return (
<form id='step-form' className='step-form' onSubmit={props.onSubmit}>
{/* Tabs section */}
{props.children}
<TabsList
currentTab={currentTab}
superset={superset}
addExercise={addExercise}
supersetList={supersetList}
handleAction={handleAction}
/>
</form>
);
}
type TabListProps = {
currentTab: number;
supersetList?: Workouts.IExercise[] | null;
addExercise?: Workouts.IExercise[] | null;
superset?: Workouts.IExerciseSuperSet | null;
handleAction: (e: React.MouseEvent) => false | void;
children?: any;
};
const TabsList: React.FC<TabListProps> = ({
currentTab,
supersetList,
addExercise,
superset,
handleAction,
}) => {
const classes = useStyles();
const { t } = useTranslation();
if (supersetList) {
return (
<div id='tablist-container' className={classes.tablList} style={{width:window.innerWidth>700 ?"48%" :"100%"}}>
{supersetList.map((exercise, exerciseIndex) => (
<TabComponent
key={exerciseIndex}
exerciseIndex={exerciseIndex}
currentTab={currentTab}
exercise={exercise}
/>
))}
{/* Step indicator */}
<div className='step-indicator' id="setMarginTop">
{currentTab ? <><span key={0} className='step' />
{supersetList.map((exercise, exerciseIndex) => (
<span key={exerciseIndex + 1} className='step' />
))}
</>
:
<div style={{marginTop:"40px"}}/>
}
{/* Action buttons Previous, Next/Submit */}
<div className='action-group'
// style={{width:window.innerWidth>700 ?"60%": "unset", marginLeft:window.innerWidth>700 ?"31%" :"0px"}}
>
<Button
variant='contained'
size='small'
id='prevBtn'
type='button'
className='prev'
onClick={handleAction}
>
{t("Previous")}
</Button>
<Button
variant='contained'
size='small'
id='nextBtn'
type='button'
className='next'
onClick={handleAction}
>
{t("Next")}
</Button>
</div>
</div>
</div>
);
} else if (superset) {
return (
<div id='tablist-container' className={classes.tablList} style={{width:window.innerWidth>700 ?"48%" :"100%"}}>
{superset.exercises.map((exercise, exerciseIndex) => {
return (
<TabComponent
key={exerciseIndex}
currentTab={currentTab + 1}
exercise={exercise}
exerciseIndex={exerciseIndex}
/>
);
})}
{/* Step indicator */}
<div className='step-indicator' id="setMarginTop">
{/* <span key={0} className='step' /> */}
{currentTab ?
<>{superset.exercises.map((exercise, exerciseIndex) => (
<span key={exerciseIndex} className='step' />
))}
</>
:
<div style={{marginTop:"40px"}}/>
}
{/* Action buttons Previous, Next/Submit */}
<div className='action-group'
// style={{width:window.innerWidth>700 ?"60%" :"unset", marginLeft:window.innerWidth>700 ?"31%":"0px"}}
>
<Button
variant='contained'
size='small'
id='prevBtn'
type='button'
className='prev'
onClick={handleAction}
>
{t("Previous")}
</Button>
<Button
variant='contained'
size='small'
id='nextBtn'
type='button'
className='next'
onClick={handleAction}
>
{t("Next")}
</Button>
</div>
</div>
</div>
);
} else if (addExercise) {
return (
<div id='tablist-container' className={classes.tablList} style={{width:window.innerWidth>700 ?"48%" :"100%"}}>
{addExercise.map((exercise, exerciseIndex) => (
<TabComponent
key={exerciseIndex}
exerciseIndex={exerciseIndex}
currentTab={currentTab}
exercise={exercise}
/>
))}
{/* Step indicator */}
<div className='step-indicator' id="setMarginTop">
{currentTab ? <><span key={0} className='step' />
{addExercise.map((exercise, exerciseIndex) => (
<span key={exerciseIndex + 1} className='step' />
))} </>
:
<div style={{marginTop:"40px"}}/>
}
{/* Action buttons Previous, Next/Submit */}
<div className='action-group'>
<Button
variant='contained'
size='small'
id='prevBtn'
type='button'
className='prev'
onClick={handleAction}
>
{t("Previous")}
</Button>
<Button
variant='contained'
size='small'
id='nextBtn'
type='button'
className='next'
onClick={handleAction}
>
{t("Next")}
</Button>
</div>
</div>
</div>
);
}
return <div id='tablist-container'></div>;
};
type TabProps = {
currentTab?: number;
exerciseIndex: number;
exercise: Workouts.IExercise;
mystyle?: boolean | undefined
};
export const TabComponent: React.FC<TabProps> = ({
currentTab,
exerciseIndex,
exercise,
mystyle
}) => {
const { catalog } = useStateValue() as { catalog: CatalogClass };
const {t} = useTranslation();
const classes = useStyles();
if (!catalog) return <></>;
const exerciseItem =
catalog.MyExercises.find((el) => el.id === exercise.exercise_id) ||
catalog.Exercises.find((el) => el.id === exercise.exercise_id);
if (!exerciseItem)
return (
<div className='tab' style={{width:"25vw"}}>
<h5 className='section-heading'>{t("Exercise not found")}</h5>
</div>
);
return (
window.innerWidth > 700 ?
<div className={`tab ${mystyle && classes.drawerTab}`} style={{width:"25vw"}}>
{/* <div className='tabDiv1'> */}
<h5 className='section-heading' style={{ color: '#333333',fontWeight:"bold", marginLeft: "0.5%" }}>
{exerciseItem.name}
</h5>
{/* </div>
<div className='tabDiv2'> */}
<ExerciseDetails match={{ params: exercise }} mystyle={true} >
<Table
exercise={exercise}
index={exerciseIndex + 1}
currentTab={currentTab}
exerciseItem={exerciseItem}
/>
</ExerciseDetails>
{/* </div> */}
</div> :
<div className={`tab ${mystyle && classes.drawerTab}`} style={{width:"25vw"}}>
<h5 className='section-heading' style={{ color: '#333333',fontWeight:"bold", marginLeft: "1.5%" }}>
{exerciseItem.name}
</h5>
<ExerciseDetails match={{ params: exercise }} mystyle={true}>
<Table
exercise={exercise}
index={exerciseIndex + 1}
currentTab={currentTab}
exerciseItem={exerciseItem}
/>
</ExerciseDetails>
</div>
);
};
import React from 'react';
import Popup from 'reactjs-popup';
import {
ExerciseTypes,
WorkoutScoreType,
CardioScoreType,
CardioScoreCloumns,
StrengthScoreColumns,
WorkoutScoreColumns,
ScoreType,
} from '../../bin/models/StaticTypes';
import {
useStateValue,
metricConverter,
metricUnit,
isInt,
} from '../../bin/config/AppContext';
import { App, Workouts, Exercises } from 'app-interfaces';
import { makeStyles } from '@material-ui/core/styles';
import {
Table,
TableHead,
TableBody,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Button,
} from '@material-ui/core';
import { Clear } from '@material-ui/icons';
import { useTranslation } from 'react-i18next';
import { ConfirmModal as ConfirmCol } from '../Modals';
import Icons from '../Utilities/Icons';
const useStyles = makeStyles((theme) => ({
addSetBtn:{
backgroundColor:"#7ed320",
borderColor:"#7ed320"
},
tableToppy: { width: "10px",
borderTop: "3px solid var(--text-primary)",
borderLeft: "3px solid #5692F4",
borderBottom: "3px solid #5692F4" },
tableContainer: {
margin: '12px 0',
maxWidth: '100%',
maxHeight: 560,
overflow: 'auto',
position: 'relative',
backgroundImage: `url(${process.env.PUBLIC_URL}/table.png)`,
backgroundSize: "248px",
'& .MuiTable-root': {
'& tr': {
height: 38,
'& th, & td, ': {
minWidth: 130,
border: '1px solid #ffffff',
padding: "12px 20px",
fontSize: 14,
fontWeight: 500,
color: '#ffffff',
},
'& th:first-child': {
width: 124,
},
},
'& .MuiTableHead-root': {
backgroundColor: 'var(--text-primary)',
},
'& .MuiTableBody-root': {
backgroundColor: '#5692F4',
'& tr': {
'& td': {
paddingTop: 5,
paddingBottom: 2,
verticalAlign: 'top',
'& .form-label, & .form-control': {
marginTop: "0px",
fontSize: 14,
fontWeight: 500,
textAlign: 'left',
textTransform: 'capitalize',
'& span': {
color: '#ffffff',
display: 'block',
textTransform: 'lowercase',
},
},
'& .form-label': {
color: '#ffffff',
},
'& .form-control': {
display: 'block',
width: '100%',
height: 'calc(1.5em + .75em + 2px)',
padding: '.375rem .75rem',
color: '#495057',
backgroundColor: '#ffffff',
backgroundClip: 'padding-box',
border: '1px solid #ced4da',
borderRadius: '.25rem',
transition:
'border-color .15s ease-in-out,box-shadow .15s ease-in-out',
},
},
},
},
},
},
dialog: {
'& .MuiDialogTitle-root': {
height: 75,
backgroundColor: 'var(--text-primary)',
color: '#ffffff',
fontSize: 20,
fontWeight: 500,
padding: '25px 15px',
'& svg': {
float: 'right',
marginRight: 15,
marginTop: -2,
},
'& .session-options': {
float: 'right',
fontSize: 14,
fontWeight: 400,
'& li': {
fontSize: 16,
fontWeight: 'bold',
padding: '5px 0',
'& svg': {
height: 24,
width: 24,
marginRight: 14,
},
},
},
},
'& .MuiDialogContent-root': {
'& textarea': {
marginTop: 15,
padding: 15,
borderRadius: 3,
width: '100%',
height: 83,
border: '1px solid #dcdcdc',
backgroundColor: 'transparent',
fontSize: 16,
},
},
'& .MuiDialogActions-root': {
paddingLeft: 20,
paddingRight: 20,
width: '100%',
'& button': {
height: 50,
backgroundColor: 'var(--text-primary)',
color: '#ffffff',
fontSize: theme.spacing(2),
textTransform: 'capitalize',
},
},
},
}));
type TableProps = {
// tabDiv2:Boolean;
exercise: Workouts.IExercise;
exerciseItem: Exercises.IExercise;
currentTab?: number;
index: number;
};
export default function TableComponent(props: TableProps) {
const classes = useStyles();
const { t } = useTranslation();
const { exercise, exerciseItem, currentTab, index } = props;
const { ic_del, ic_info } = Icons;
const { catalog, metric, Methods } = useStateValue() as {
catalog: App.State['catalog'];
metric: App.State['metric'];
Methods: App.Methods;
};
const isSchedule = !/workouts/.test(window.location.href);
const [labelState, setLabelState]: any = React.useState([]);
const [tableHead, setTableHead] = React.useState<JSX.Element>();
const [tableBody, setTableBody] = React.useState<JSX.Element[]>();
const [resultRow, setResultRow] = React.useState<JSX.Element[]>();
const [targetCol, setTargetCol] = React.useState<number | null>(null);
const [targetSetNotes, setTargetSetNotes] = React.useState<number | null>(
null
);
const [showConfirmCol, setShowConfirmCol] = React.useState(false);
const [showSetNotes, setShowSetNotes] = React.useState(false);
const [generateTableCount, setGenerateTableCount] = React.useState(0);
React.useEffect(()=>{
setTimeout(function(){
const x=document.getElementById(
`table-container-${index}`)?.clientHeight
const y=document.getElementById("setTop")
const z=document.getElementById(
`table-container-${index}`)?.clientWidth
const stepsIndicator=document.getElementById("setMarginTop")
// console.log('tablewidth', z)
if(y && x && window.innerWidth>700){
y.style.top=`${x+130}px`
}
if(stepsIndicator && x && window.innerWidth>700){
stepsIndicator.style.top=`${x+150}px`
if(z){
stepsIndicator.style.width=`${z}px`
}
}
},10)
})
// Show confirm rem Col
const showConfirmColModal = React.useCallback(() => {
if (!targetCol) return setShowConfirmCol(false);
return setShowConfirmCol(true);
}, [targetCol]);
// Show set note
const showSetNoteModal = React.useCallback(() => {
if (!targetSetNotes) return setShowSetNotes(false);
return setShowSetNotes(true);
}, [targetSetNotes]);
// Handle rem set click
const handleRemCol = (e: MouseEvent) => {
const currentTarget = e.currentTarget as HTMLSpanElement | null;
if (currentTarget) {
const tableHeadCell = currentTarget.parentElement as HTMLTableHeaderCellElement | null;
return tableHeadCell && setTargetCol(tableHeadCell.cellIndex);
}
};
// Handle set notes click
const handleSetNotes = (e: MouseEvent) => {
const currentTarget = e.currentTarget as HTMLSpanElement | null;
if (currentTarget) {
const tableHeadCell = currentTarget.parentElement as HTMLTableHeaderCellElement | null;
return tableHeadCell && setTargetSetNotes(tableHeadCell.cellIndex);
}
};
// Save set notes
const saveSetNotes = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
e.stopPropagation();
const formData = new FormData(e.currentTarget);
const newNotes = formData.get('notes') as string;
const targetTable = document.getElementById(
`set-table-${index}`
) as HTMLTableElement | null;
if (!targetTable || !targetSetNotes) return;
const tableBody = targetTable.children[1] as HTMLTableElement; // <tbody/>
const tableBodyRows = tableBody.children as HTMLCollectionOf<HTMLTableRowElement>; // <tr/>
const tableSetNotes = tableBodyRows[0].children[targetSetNotes]
.children[1] as HTMLInputElement;
tableSetNotes.setAttribute('value', newNotes);
if (!exercise.notes_sets) Object.assign(exercise, { notes_sets: [] });
exercise.notes_sets[targetSetNotes - 1] = newNotes;
return setTargetSetNotes(null);
};
// Confirm remove column
const confirmRemCol = () => {
const targetTable = document.getElementById(
`set-table-${index}`
) as HTMLTableElement | null;
const dataInput = document.getElementById(
'data'
) as HTMLInputElement | null;
if (!targetTable || !targetCol || !dataInput) return;
const tableHead = targetTable.children[0] as HTMLTableElement; // <thead/>
const tableHeadRow = tableHead.children[0] as HTMLTableHeaderCellElement; // <th/>
const tableBody = targetTable.children[1] as HTMLTableElement; // <tbody/>
const tableBodyRows = tableBody.children as HTMLCollectionOf<HTMLTableRowElement>; // <tr/>
// Remove the head cell.
tableHeadRow.children[targetCol].remove();
// Remove coresponding column cells.
[...tableBodyRows].forEach((v) => v.children[targetCol].remove());
// Get current exercise
const currentExercise = exercise;
// Decrement score count.
currentExercise.score_count && currentExercise.score_count--;
// Splice score values.
currentExercise.score_vals &&
currentExercise.score_vals.map((val) =>
val.value.splice(targetCol - 1, 1)
);
// Update data input value
const currentDataInput = JSON.parse(
dataInput.value
) as Workouts.IExercise[];
if (currentTab) {
currentDataInput[currentTab - 1] = currentExercise;
dataInput.value = JSON.stringify(currentDataInput);
} else {
dataInput.value = JSON.stringify([currentExercise]);
}
if (targetCol >= 1) {
for (let i = targetCol; i < tableHeadRow.children.length; i++) {
tableHeadRow.children[
i
].innerHTML = `<td>${i}
<span class='info-icon' onClick='${handleSetNotes}'>
<img
src='${ic_info}'
alt='${t('notes')}'
title='${t(`Set ${i + 1} Notes`)}'
/>
</span>
<span class='del-icon' id='dlt${i + 1}'><img src='${ic_del}'/></span></td>`;
tableHeadRow.children[i].children[0].addEventListener("click", (e: any) => handleSetNotes(e));
tableHeadRow.children[i].children[1].addEventListener("click", (e: any) => handleRemCol(e));
}
}
return setTargetCol(null);
};
// Generate Metrics
const generateMetrics = React.useCallback(
(name: string) => metricUnit(name, metric),
[metric]
);
// Generate HeadRow
const generateHeadRow = React.useCallback(() => {
const handleRemCol = (e: React.MouseEvent<HTMLSpanElement>) => {
if (e.currentTarget) {
const targetRow = e.currentTarget
.parentElement as HTMLTableHeaderCellElement | null;
return targetRow && setTargetCol(targetRow.cellIndex);
}
};
const handleSetNotes = (e: React.MouseEvent<HTMLSpanElement>) => {
if (e.currentTarget) {
const tableHeadCell = e.currentTarget
.parentElement as HTMLTableHeaderCellElement | null;
return tableHeadCell && setTargetSetNotes(tableHeadCell.cellIndex);
}
};
const setHeads = () => {
const scoreCount = exercise.score_count;
let heads: JSX.Element[] = scoreCount
? []
: [
<th key={0}>
{1}
<span className='info-icon' onClick={handleSetNotes}>
<img src={ic_info} alt={t('notes')} title={t('Set 1 Notes')} />
</span>
</th>,
];
if (scoreCount) {
for (let i = 0; i < scoreCount; i++) {
heads.push(
<th key={i + 1}>
{i + 1}
{i >= 0 ? (
<>
<span className='info-icon' onClick={handleSetNotes}>
<img
src={ic_info}
alt={t('notes')}
title={t(`Set ${i + 1} Notes`)}
/>
</span>
<span
className='del-icon'
onClick={handleRemCol}
>
<img
src={ic_del}
alt={t('delete')}
title={t(`Remove Set ${i + 1}`)}
/>
</span>
</>
) : (
<span
className='info-icon'
onClick={() => setTargetSetNotes(i + 1)}
>
<img src={ic_info} alt={t('notes')} title={t(`Set ${i + 1} Notes`)} />
</span>
)}
</th>
);
}
}
return heads;
};
if (!tableHead) {
return setTableHead(
<tr>
<th>{t("Sets")}</th>
{setHeads()}
</tr>
);
}
}, [tableHead, exercise, ic_del, ic_info, t]);
// Generate BodyRow
const generateBodyRow = React.useCallback(() => {
// toggle option select
const toggleOptions = (e: React.ChangeEvent<HTMLInputElement>) => {
e.preventDefault();
const currentTargetValue = e.currentTarget.value;
const currentTargetDataCell = e.currentTarget
.parentElement as HTMLTableDataCellElement | null;
const currentTargetRow =
currentTargetDataCell &&
(currentTargetDataCell.parentElement as HTMLTableRowElement | null);
const selectDataCell =
currentTargetRow &&
(currentTargetRow.firstElementChild as HTMLTableDataCellElement | null);
const selectInput =
selectDataCell &&
(selectDataCell.firstElementChild as HTMLSelectElement | null);
if (!selectInput)
return Methods.handleUnauthorized({
error_code: 504,
error_message: t('Input not found'),
title: t('App Error!'),
});
if (currentTargetValue === '')
return selectInput.removeAttribute('disabled');
if (selectInput.disabled) return;
return selectInput.setAttribute('disabled', 'true');
};
// set name attribute for corresponding row inputs
const handleOptions = (e: React.ChangeEvent<HTMLSelectElement>) => {
e.preventDefault();
const name = e.currentTarget.value;
const metric = e.currentTarget
.nextElementSibling as HTMLSpanElement | null;
const currentIndex = parseInt(e.currentTarget.id);
const targetTable = document.getElementById(`set-table-${currentIndex}`);
if (e.currentTarget.value === '' || !targetTable) return;
const targetTableBody = targetTable.children[1].children;
if (!targetTableBody[0] || !metric) return;
const targetTableBodyInputs = [
...targetTableBody[0].querySelectorAll('input.form-control'),
] as HTMLInputElement[];
targetTableBodyInputs.forEach((v, i) => {
if (targetTableBodyInputs[i].name !== 'notes_sets') {
targetTableBodyInputs[i].name = name;
targetTableBodyInputs[i].step = isInt(name) ? '1' : '0.01';
}
});
metric.innerText = generateMetrics(name);
if (isSchedule) {
const resultsRow = targetTable.children[1]
.lastElementChild as HTMLTableRowElement;
const resultMetric = resultsRow.getElementsByTagName('span')[0];
if (resultMetric) resultMetric.innerText = generateMetrics(name);
}
};
// Generate options list based on exercise type
const generateOptionSelect = () => {
let scoreColumnOptions;
let length;
const type = exerciseItem && exerciseItem.type;
if (!catalog || !type) return;
if (type.toString() === ExerciseTypes[1]) {
scoreColumnOptions = CardioScoreType;
length = Object.keys(CardioScoreType).length / 2;
} else {
scoreColumnOptions = WorkoutScoreType;
length = Object.keys(WorkoutScoreType).length / 2;
}
let options: JSX.Element[] = [];
for (let i = 0; i < length; i++) {
options = [
...options,
<option
key={i}
value={catalog.typesDisplay(scoreColumnOptions[i])}
style={{
textTransform: 'capitalize',
}}
>
{catalog.typesDisplay(scoreColumnOptions[i].toLowerCase())}
</option>,
];
}
return exercise.score_vals &&
exercise.score_vals.length > 0 &&
exercise.score_vals[0].value[0] &&
exercise.score_vals[0].value.find((el) => el === 0) ? (
<>
<select
disabled
id={`${index}`}
name='score_type'
className='form-control'
onChange={handleOptions}
defaultValue={exercise.score_type}
>
{options}
</select>
<span>{generateMetrics(exercise.score_type)}</span>
</>
) : (
<>
<select
id={`${index}`}
name='score_type'
className='form-control'
onChange={handleOptions}
defaultValue={exercise.score_type}
>
{options}
</select>
<span>{generateMetrics(exercise.score_type)}</span>
</>
);
};
// Generate Table Data cells
const setData = (name: string) => {
const scoreCount = exercise.score_count;
const colIndex =
exercise.score_cols &&
exercise.score_cols.findIndex((el) => el === name);
let dataCells: JSX.Element[] =
colIndex > -1
? [
<td key={0}>
<input
type='text'
className='form-control'
name={name}
autoComplete="off"
// min={0}
// step={isInt(name) ? 1 : 0.01}
defaultValue={
exercise.score_vals &&
exercise.score_vals.length > 0 &&
exercise.score_vals[colIndex].value[0]
? isInt(name)
? metricConverter(
exercise.score_vals[colIndex].value[0],
name,
metric
)
: metricConverter(
exercise.score_vals[colIndex].value[0],
name,
metric
).toFixed(2)
: undefined
}
onChange={toggleOptions}
onKeyUp={handleInputChange}
/>
{name === exercise.score_type && (
<input
type='text'
name='notes_sets'
className='form-control'
defaultValue={exercise.notes_sets[colIndex]}
style={{ display: 'none' }}
/>
)}
</td>,
]
: [
<td key={0}>
<input
type='text'
className='form-control'
name={name}
autoComplete="off"
// min={0}
// step={isInt(name) ? 1 : 0.01}
defaultValue={
exercise.score_vals &&
exercise.score_vals.length > 0 &&
exercise.score_vals[0].value[0]
? isInt(name)
? metricConverter(
exercise.score_vals[0].value[0],
name,
metric
)
: metricConverter(
exercise.score_vals[0].value[0],
name,
metric
).toFixed(2)
: undefined
}
onChange={toggleOptions}
onKeyUp={handleInputChange}
/>
<input
autoComplete="off"
type='text'
name='notes_sets'
className='form-control'
defaultValue={
exercise.notes_sets ? exercise.notes_sets[0] : undefined
}
style={{ display: 'none' }}
/>
</td>,
];
if (scoreCount >= 1) {
for (let i = 1; i < scoreCount; i++) {
dataCells = [
...dataCells,
<td key={i}>
<input
set-index={i}
type='text'
className='form-control'
name={name}
autoComplete="off"
// min={0}
// step={isInt(name) ? 1 : 0.01}
onKeyUp={handleInputChange}
defaultValue={
exercise.score_vals &&
exercise.score_vals.length > 0 &&
exercise.score_vals[colIndex].value[i]
? isInt(name)
? metricConverter(
exercise.score_vals[colIndex].value[i],
name,
metric
)
: metricConverter(
exercise.score_vals[colIndex].value[i],
name,
metric
).toFixed(2)
: undefined
}
/>
{name === exercise.score_type && (
<input
type='text'
name='notes_sets'
className='form-control'
defaultValue={exercise.notes_sets[i]}
style={{ display: 'none' }}
/>
)}
</td>,
];
}
}
return dataCells;
};
if (!tableBody) {
const exerciseType = exerciseItem.type;
const scoreType = exercise.score_type;
const scoreCols = exercise.score_cols;
// If no score type or no score cols then return template
if (!scoreType || !scoreCols) {
// If exercise type is STRENGTH or KETTLEBELLS add KG_WEIGHTS to defau;t template
if (
`${exerciseType}` === ExerciseTypes[4]
) {
return setTableBody([
<tr key={0}>
<td>{generateOptionSelect()}</td>
{setData('TIME')}
</tr>,
<tr key={1}>
<td>
<label className='form-label'>
{t("pause")}
<span
style={{ display: 'block', textTransform: 'lowercase' }}
>
{generateMetrics(ScoreType[3])}
</span>
</label>
</td>
{setData('PAUSE')}
</tr>,
<tr key={2}>
<td>
<label className='form-label'>
{t("Weights")}
<span
style={{ display: 'block', textTransform: 'lowercase' }}
>
{generateMetrics(ScoreType[4])}
</span>
</label>
</td>
{setData(ScoreType[4])}
</tr>,
]);
}
// else default template
return setTableBody([
<tr key={0}>
<td>{generateOptionSelect()}</td>
{setData('TIME')}
</tr>,
<tr key={1}>
<td>
<label className='form-label'>
{t("pause")}
<span>{generateMetrics(ScoreType[3])}</span>
</label>
</td>
{setData('PAUSE')}
</tr>,
]);
}
// ... else return existing set data
const exisitingSetData = scoreCols.map((col, i) => {
return i === 0 ? (
// if first column then set option return option selector & score type
<tr key={i}>
<td>{generateOptionSelect()}</td>
{setData(col)}
</tr>
) : (
// ...else return label and optional score keys
<tr key={i}>
<td>
<label className='form-label'>
{catalog.typesDisplay(col).toLowerCase()}
<span>{generateMetrics(col)}</span>
</label>
</td>
{setData(col)}
</tr>
);
});
return setTableBody(exisitingSetData);
}
}, [
t,
catalog,
index,
exercise,
exerciseItem,
tableBody,
metric,
generateMetrics,
Methods,
isSchedule,
]);
// Generate Results row
const generateResultsRow = React.useCallback(() => {
// Generate result columns
const generateResults = () => {
const results: JSX.Element[] = [
<td key={0}>
<label className='form-label'>
{t("Results")}
<span>{generateMetrics(exercise.score_type)}</span>
</label>
</td>,
];
if (!exercise.score_count) {
results.push(
<td key={1}>
<input
className='form-control'
name='result_vals'
type='text'
// min={0}
onKeyUp={handleInputChange}
// step={isInt(exercise.score_type) ? 0 : 0.01}
defaultValue={undefined}
/>
</td>
);
return setResultRow(results);
}
for (let i = 1; i <= exercise.score_count; i++) {
const value = metricConverter(
exercise.result_vals[i - 1],
exercise.score_type,
metric
);
results.push(
<td key={i}>
<input
className='form-control'
name='result_vals'
type='text'
// min={0}
onKeyUp={handleInputChange}
// step={isInt(exercise.score_type) ? 0 : 0.01}
defaultValue={value && value !== 0 ? value : undefined}
/>
</td>
);
}
return setResultRow(results);
};
if (!resultRow && exercise.result_vals && isSchedule)
return generateResults();
}, [exercise, resultRow, isSchedule, metric, generateMetrics, t]);
// Effects
React.useEffect(generateHeadRow, [currentTab]);
React.useEffect(generateBodyRow, [currentTab]);
React.useEffect(generateResultsRow, [exercise, resultRow, isSchedule]);
React.useEffect(showConfirmColModal, [targetCol]);
React.useEffect(showSetNoteModal, [targetSetNotes]);
if (!exerciseItem || !catalog) return <></>;
const exerciseType = exerciseItem.type;
// Generate select input & options:
const generateOptions = (close: () => void) => {
let scoreColumnOptions: any;
let length;
const type = exerciseType;
switch (`${type}`) {
case ExerciseTypes[1]:
scoreColumnOptions = CardioScoreCloumns;
length = Object.keys(CardioScoreCloumns).length / 2;
break;
case ExerciseTypes[4]:
scoreColumnOptions = StrengthScoreColumns;
length = Object.keys(StrengthScoreColumns).length / 2;
break;
default:
scoreColumnOptions = WorkoutScoreColumns;
length = Object.keys(WorkoutScoreColumns).length / 2;
break;
}
let options: JSX.Element[] = [
<li
key={0}
value={''}
style={{ cursor: 'pointer', textTransform: 'capitalize', color: "var(--gray)" }}
onClick={handleAddOptions}
>
{t("SELECT")}
</li>,
];
const targetTable = document.querySelector(`#set-table-${index}`);
const targetTableBody = targetTable && targetTable.children[1];
const targetFormLabels: any =
targetTableBody && targetTableBody.querySelectorAll('label.form-label');
for (let i = 0; i < length; i++) {
const isActive = [...targetFormLabels].find(
(el: HTMLLabelElement | any) =>
el.innerText
.replace(generateMetrics(scoreColumnOptions[i]), '')
.trim()
.toUpperCase() === catalog.typesDisplay(scoreColumnOptions[i])
);
options = [
...options,
<li
key={i + 1}
value={catalog.typesDisplay(scoreColumnOptions[i])}
style={{ cursor: 'pointer', textTransform: 'capitalize', textAlign: "left" }}
onClick={handleAddOptions}
>
<span className={isActive ? 'checkbox active' : 'checkbox'} style={{ marginRight: 8 }}></span>
{catalog.typesDisplay(scoreColumnOptions[i].toLowerCase())}
</li>,
];
}
options = [
...options,
<li
key={options.length}
style={{ cursor: 'pointer', textTransform: 'capitalize' }}
>
<button
type='button'
className='btn'
style={{ width: '100%', backgroundColor: "black", color: "white", padding: ".3rem", margin: "10px 0px 0" }}
onClick={() => {
handleDone();
close()
}}
>
{t("Done")}
</button>
</li>,
];
return <ul style={{ marginBottom: '0' }}>{options}</ul>;
};
// Check if add options is required:
const requireOptions = () => {
switch (exerciseType.toString()) {
case ExerciseTypes[1]:
return true;
default:
return false;
}
};
const handleInputChange = (e:any)=>{
let stVal = e.target.value
var regex = /^[0-9.,]+$/;
if(regex.test(e.key)){
if(e.key === '.' ){
let dotScore = 0;
for(let i = 0; i < e.target.value.length; i++ ){
if(e.target.value[i] === '.'){
dotScore = dotScore +1 ;
}
}
if(dotScore > 1){
stVal = stVal.slice(0,-1)
e.target.value = stVal
}
}
if(e.key === ','){
stVal = stVal.slice(0,-1) + "."
let dotScore = 0;
for(let i = 0; i < stVal.length; i++ ){
if(stVal[i] === '.'){
dotScore = dotScore +1 ;
}
}
if(dotScore > 1){
stVal = stVal.slice(0,-1)
return e.target.value = stVal
}
return e.target.value = stVal
}
}else{
stVal = stVal.slice(0,-1)
e.target.value = stVal
}
}
// Generate table heads after add set button click
const generateTableHeads = () => {
setGenerateTableCount(1 + generateTableCount);
const newTableHead = document.createElement('th');
const newTableSpan = document.createElement('span');
const newTableIcon = document.createElement('img');
const newTableBody = document.createElement('td');
const newTableBodyInput = document.createElement('input');
const newTableNoteSpan = document.createElement('span');
const newTableNoteIcon = document.createElement('img');
const newTableBodyNote = document.createElement('input');
newTableSpan.className = 'del-icon';
// @ts-ignore
newTableSpan.style.float = 'right';
newTableIcon.src = ic_del;
newTableBodyInput.className = 'form-control';
newTableBodyInput.type = 'text';
newTableBodyInput.autocomplete = 'off';
newTableBodyInput.id = `newGeneratedInput-${generateTableCount}`;
// newTableBodyInput.addEventListener('keyup', handleInputChange);
// newTableBodyInput.oninput = (e:any) => console.log(e);
// newTableBodyInput.min = '0';
console.log(newTableBodyInput , ' new element')
newTableNoteSpan.className = 'info-icon';
newTableNoteIcon.src = ic_info;
newTableBodyNote.style.display = 'none';
newTableBodyNote.className = 'form-control';
newTableBodyNote.type = 'text';
newTableBodyNote.name = 'notes_sets';
const targetTable = document.getElementById(
`set-table-${index}`
) as HTMLTableElement | null;
if (!targetTable) return;
const tableHead = targetTable.children[0]; // <thead/>
const tableBody = targetTable.children[1]; // <tbody/>
const tableHeadRow = tableHead.children[0]; // <th/>
const tableBodyRows = tableBody.children as HTMLCollectionOf<HTMLTableRowElement>; // <tr/>
const scoreRows = tableBodyRows.length; // Number of scores
newTableHead.innerHTML = `${tableHeadRow.children.length}`;
newTableSpan.addEventListener('click', handleRemCol);
newTableNoteSpan.addEventListener('click', handleSetNotes);
// Add icon img to span
newTableSpan.append(newTableIcon);
newTableNoteSpan.append(newTableNoteIcon);
// Add new span to th
newTableHead.append(newTableSpan, newTableNoteSpan);
// Add new th to table head
tableHeadRow.append(newTableHead);
// Generate inputs for each selected option
for (let i = 0; i < scoreRows; i++) {
const targetRow = tableBodyRows.item(i) as HTMLTableRowElement;
newTableBodyInput.value = '';
if (targetRow && targetRow.cells[targetRow.cells.length - 1]) {
const prevColInput = targetRow.cells[targetRow.cells.length - 1]
.firstElementChild as HTMLInputElement | null;
if (i === 0) {
const newSelectInput = targetRow.cells[0]
.firstElementChild as HTMLSelectElement;
// console.log('val', prevColInput?.id, prevColInput)
if (prevColInput && prevColInput?.id!=="1") {
newTableBodyInput.value = prevColInput.value;
}
else{
newTableBodyInput.value = ""
}
newTableBodyInput.name = newSelectInput.value;
// newTableBodyInput.step = isInt(newSelectInput.value) ? '1' : '0.01';
newTableBody.append(newTableBodyInput, newTableBodyNote);
} else if (/Results/.test(targetRow.innerText.trim())) {
newTableBodyInput.name = 'result_vals';
// newTableBodyInput.step = isInt(`${exercise.score_type}`)
// ? '1'
// : '0.01';
newTableBody.append(newTableBodyInput);
} else {
// console.log('val2', prevColInput?.id, prevColInput)
if (prevColInput?.value){
newTableBodyInput.value = prevColInput.value;}
else{
newTableBodyInput.value=""
}
newTableBodyInput.name = targetRow.innerText.includes("Weights") ? "KG_WEIGHTS" :
targetRow.innerText.split('(')[0]
.trim()
.toUpperCase();
// newTableBodyInput.step = isInt(newTableBodyInput.name) ? '1' : '0.01';
newTableBody.append(newTableBodyInput);
}
targetRow.append(newTableBody.cloneNode(true));
for (let j = 0; j < newTableBody.children.length; j++) {
const tableBodyNote = newTableBody.children[j].getAttribute('name');
if (!tableBodyNote) return;
if (
/notes_sets/.test(tableBodyNote) ||
/result_vals/.test(tableBodyNote)
)
newTableBody.removeChild(newTableBody.children[j]);
}
}
}
const tableContainer = document.getElementById(
`table-container-${index}`
) as HTMLDivElement | null;
tableContainer &&
tableContainer.scrollTo({
left: targetTable.scrollWidth,
behavior: 'smooth',
});
document.querySelector(`#newGeneratedInput-${generateTableCount}`)!.addEventListener('keyup', handleInputChange);
};
// Generate table rows
const generateTableRows = (label: string) => {
const targetTable = document.getElementById(
`set-table-${index}`
) as HTMLTableElement | null;
if (!targetTable) return;
const tableHeadRow = targetTable.children[0]
.children[0] as HTMLTableRowElement;
const setNumber = tableHeadRow.children.length;
const tableBody = targetTable.children[1] as HTMLTableRowElement;
// Check if entry already exists in the table:
const targetEntry = [...tableBody.children].find((row, i) => {
const td = row.firstElementChild as HTMLTableDataCellElement;
if (!td) return false;
if (i === 0) {
const selectInput = td.firstElementChild as HTMLSelectElement;
if (selectInput && selectInput.value === label) return true;
}
const labelElement = td.firstElementChild as HTMLLabelElement;
if (
labelElement.innerText
.replace(generateMetrics(label), '')
.toUpperCase()
.trim() === label
)
return true;
return false;
}) as HTMLTableRowElement | undefined;
// If entry exists & not first row then remove corresponding row:
if (targetEntry) {
if (targetEntry.rowIndex > 1) {
return targetEntry.remove();
}
// Prevent adding duplicate rows
return alert(`${label.toLowerCase()} ${t("already exists")}`);
}
// If entry doesn't exist then generate new table row:
const newTableBodyRow = document.createElement('tr');
const newTableBodyRowData1 = document.createElement('td');
const newTableBodyRowData2 = document.createElement('td');
const newTableBodyRowDataLabel = document.createElement('label');
const newTableBodyRowDataSpan = document.createElement('span');
const newTableBodyRowDataInput = document.createElement('input');
newTableBodyRowDataLabel.className = 'form-label';
newTableBodyRowDataLabel.innerText = label.toLowerCase();
Object.assign(newTableBodyRowDataLabel.style, {
textTransform: 'capitalize',
textAlign: 'left',
width: '100%',
});
newTableBodyRowDataSpan.innerText = generateMetrics(label);
Object.assign(newTableBodyRowDataSpan.style, {
display: 'block',
textAlign: 'left',
width: '100%',
textTransform: 'lowercase',
});
// newTableBodyRowDataLabel.addEventListener('click', handleRemRow);
newTableBodyRowDataInput.className = 'form-control';
newTableBodyRowDataInput.name = label;
newTableBodyRowDataInput.type = 'number';
newTableBodyRowDataInput.min = '0';
newTableBodyRowDataInput.step = isInt(label) ? '1' : '0.01';
newTableBodyRowDataInput.addEventListener('invalid', (e) =>
console.log(e.currentTarget)
);
// Append the span element to the new label
newTableBodyRowDataLabel.append(newTableBodyRowDataSpan);
// Append the label element to the new td.
newTableBodyRowData1.append(newTableBodyRowDataLabel);
// Append the input element to the new td.
newTableBodyRowData2.append(newTableBodyRowDataInput);
// Append the new td to the new tr.
newTableBodyRow.append(newTableBodyRowData1);
// Append new inputs according to set number.
for (let i = 1; i < setNumber; i++) {
const newTableRowData = newTableBodyRowData2.cloneNode(
true
) as HTMLTableDataCellElement;
const existingValue =
exercise.score_vals &&
exercise.score_vals.find((el) => el.type === label);
if (existingValue && newTableRowData.children[0])
newTableRowData.children[0].setAttribute(
'value',
JSON.stringify(existingValue.value[i - 1])
);
newTableBodyRow.append(newTableRowData);
}
// Append the new tr before results row
if (isSchedule) {
const resultRow = tableBody.lastChild;
tableBody.insertBefore(newTableBodyRow, resultRow);
// Append the new tr to the current table.
} else {
tableBody.append(newTableBodyRow);
}
};
// Handle add set click
const handleAddSet = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
generateTableHeads();
};
// Handle options selection
const handleAddOptions = (e: React.MouseEvent<HTMLLIElement>) => {
e.preventDefault();
const label = e.currentTarget.getAttribute('value');
if (label === 'all') {
// TODO add select all functionality
}
const targetCheckBox = e.currentTarget.firstElementChild;
if (targetCheckBox) targetCheckBox.classList.toggle('active');
if (!label || label === '') return;
let labelArray = [...labelState];
labelArray.push(label)
setLabelState(labelArray)
};
const handleDone = () => {
if (!labelState.length || labelState[0] === '') return;
labelState.forEach((v: string) => {
generateTableRows(v.toUpperCase());
})
setLabelState([])
}
return (
<>
<div className={`button-group`} style={{ padding: '5px 0px' }}>
{requireOptions() && (
<Popup
trigger={
<button
type='button'
style={{
position: 'relative',
float: 'left',
textTransform: "none",
userSelect: 'none',
marginTop: 0,
marginRight: "10px",
}}
className='btn btn-primary'
>
{t("Add option")}
</button>
}
arrow={true}
arrowStyle={{ left: "22%" }}
position={'bottom center'}
contentStyle={{
textAlign: 'left',
fontSize: 14,
maxWidth: '140px',
minWidth: '100px',
cursor: 'pointer',
borderRadius: '5px',
backgroundColor: 'var(--white)',
color: 'black',
}}
>
{generateOptions}
</Popup>
)}
<button
type='button'
style={{ float: 'left', textTransform: "none", marginTop: 0 }}
className={`btn btn-success ${classes.addSetBtn}`}
onClick={handleAddSet}
>
{t("Add set")}
</button>
</div>
<div id={`table-container-${index}`} className={classes.tableContainer}>
<Table id={`set-table-${index}`} className={`set-table ${classes.tableToppy}`}>
<TableHead>{tableHead}</TableHead>
<TableBody>
{tableBody}
{/* {console.log('resultRow', resultRow, tableHead)} */}
{isSchedule && resultRow &&
<tr>{resultRow}</tr>
// :
// <tr>
// <td><label className="form-label">Results<span>(mm.ss)</span></label></td>
// <td>
// <input
// className='form-control'
// name='result_vals'
// type='number'
// min={0}
// onKeyUp={handleInputChange}
// step={isInt(exercise.score_type) ? 0 : 0.01}
// defaultValue={undefined}
// />
// </td>
// </tr>
}
</TableBody>
</Table>
</div>
<ConfirmCol
show={showConfirmCol}
onSubmit={confirmRemCol}
onHide={() => setTargetCol(null)}
title={t('Remove Column')}
message={t('Are you sure you want to remove this column?')}
/>
<Dialog
fullWidth
maxWidth='sm'
open={showSetNotes}
onClose={() => setTargetSetNotes(null)}
className={classes.dialog}
>
<DialogTitle>
{t("Set")} {targetSetNotes} {t("Notes")}{' '}
<Clear onClick={() => setTargetSetNotes(null)} />
</DialogTitle>
<DialogContent>
<form id='set-note' onSubmit={saveSetNotes}>
<textarea
name='notes'
defaultValue={
targetSetNotes && exercise.notes_sets
? exercise.notes_sets[targetSetNotes - 1]
: undefined
}
placeholder={t(`Notes for set ${targetSetNotes}`)}
/>
</form>
</DialogContent>
<DialogActions>
<Button variant='contained' type='submit' form='set-note'>
{t("Save")}
</Button>
</DialogActions>
</Dialog>
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment