From 3df4abba472414e0a438e78be794741ec8f24ddb Mon Sep 17 00:00:00 2001 From: AG Date: Wed, 10 Dec 2025 19:58:42 +0200 Subject: [PATCH] Side attribute editable for Unilateral exercises --- playwright-report/index.html | 2 +- server/prisma/dev.db | Bin 204800 -> 274432 bytes specs/gymflow-test-plan.md | 9 +- specs/requirements.md | 3 +- src/components/History.tsx | 58 +++++++++---- src/components/Tracker/ActiveSessionView.tsx | 35 ++++++++ src/components/Tracker/SetLogger.tsx | 9 +- src/components/Tracker/SporadicView.tsx | 33 +++++++ src/components/Tracker/useTracker.ts | 1 + src/hooks/useWorkoutForm.ts | 7 +- tests/workout-tracking.spec.ts | 85 +++++++++++++------ 11 files changed, 190 insertions(+), 52 deletions(-) diff --git a/playwright-report/index.html b/playwright-report/index.html index 3b8cf4a..39da39e 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -82,4 +82,4 @@ Error generating stack: `+n.message+`
- \ No newline at end of file + \ No newline at end of file diff --git a/server/prisma/dev.db b/server/prisma/dev.db index 7db089899088b64d3a01574ee6b67522e4842987..f7413bb1c9d4498f0e00d8eafc0549abb47add80 100644 GIT binary patch delta 70840 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 z z_sA0ue=n_HGkt+K_C>CZ;}on9+=lfE`zs1Pas?TN>S1$3-Lua=;#GK81&TFHA&>Zf z^|FdEcP-z<4@>8Vd2NQD>7LK>Gu7Mk+3fR5AIyYZ>&h{9s_%9#?Rld6?()2@Bb68A zS5(T~`@6p1@yf#I^H1h4&)u3E&s^5?LVRaSI=5xM-hD;3SbSsd$EklR-<93hQ^^f< zrV7vRT8KI8H z@4=!l&v>YJkzqV2Zy@e$3^@n`jP+bqtO6dUCm<(A37o!|!rfmJ15qNds=~wJW!x() z=O8a+y=tLNqDLddl?Nm)R)GDE4HI{C;YpRSG(;%l{TEtEfJzeGFGwQhaaDsMV_&$6 zgj_=0BkGV1Ay5E76x}BV`sm0Y4vDm(2+BnDhdpXo8k`_xjj^{+46KJfoHY<9$3hx` zYLHuq)+)v{pWp-VLLBZDBTd)B0SNMOuq-gQ@Z$x-?5}h30?P!v$9v==wOGUd0_efd z42uHqI}t`maIWQe*y+&SrPWbIrK}56B=&k?U4rGJrsAMCapI$)M?^Sy((D+(9r-Q+ zk_Jr;xe1uF1?a|HX|jHFPDh#@u~jMoeyb{edlreE3{lKY*DU-Vh)q;zOqAPf8j$!;{ed0D&qZzV1NWOVrh0nE}|g- zrW7?41KCelGZKpLf)I-gf*hp6#KBNeVv#hMvDg-YXCee6p#d)MZQ&_WrNSiqMuZrE z-iU7CjkO4&ur2oJ|~B#en{xiD^x26#yZ1R*4UtBV_Si91XCf0Msp;GlJ_@Doj3V zA7k-0nb@LW{^$c(0novarnsL1z^hc4Jgg&pff&cOu$4IifuK_V2%cCje3N(aRjOwRovatEiEj=jp23KKBJ;*(-zQG_uOQzcs^tWp|b2qxC_Nb#k@ zgp1v@gpmlHg@FOYTpiU;#pfK@_>eJwH_eXVTSaw&AU=8r$Co#O?+J?-ni?pUn-@!k z2{-_DW082KIYQ{u*iPVE!oh2@bs>ImsW9oVra-VY6c64;1P@^=1M0(2(G@Yo`dYoI zF<~8Z@W@tiATW0fm*E)kL0zYovs^`(B!ND>H^gD7;0APCRp)EN7Xzs< zVG^p_n#Oq*NQckJ@xZ#w=f?qR!L5L4cGK+04BP?^EtYpSNFaPXvuM&da$pw-5K}5l zf&&=@qv0J8wkH*&&ArCKf#r)h8&0X?ONEKo*3sAMOe;bN5ITq-z6)k%2Za{?8^W~3 z>-hL>935FlNF95Q^GIM*xD{q*jVK`iZ6FmUi`&gQu@-y6vX9ch3F20`;1Ad56p0uF zK@mf%6JrbKv(=^lAB%@iC>@?*AROjrbOJ>xOemVMA{<<@lViH9)^Sq_OoEQhMG`_=YD|$M)Cx`yzAQ$ne0jb%2g9|wegO)$ zX-b7jTsi43!;rxDMonUUV1t;@Gfb1P2gQ|EN0^_5rbQfv(9`(b@O?a5^{R!XhbzC#j?NXNP={0D^U zH5DeJG>|H2emV@2Tg&w!Bt%H3u#WcwAjPeg8WZ6OgnzJ$T7U&yXgWwI9z?ORJm$sw zA4!FY2sYw{xHR~f5ViuT3F930Xzq-OC5vS?6($pUd@aN`H~_|lR>xt$B#g%GXmw$L zQKZHcvu9$}LTC!L4t9kNhA=G5d=c@RaI2shBM`uMba*~?U3hAUsSX_smIV_-L8KQI~EL(kV#gmX+#bN7+~tkLUAJ;1Jej>38hQiFg+ zQVe5(7>JR;#IXbZ!F)Vj$J}sJm>?V| z3J4LXi~FrQ7+e@cD3)MC!uJ6K#14;(&jw|f-gRQ`Bx;(B5lEsgT`eeD#@X*Ze8bt( zAK!g*5)I~>N1pxoS2IPhy_9Wd&%EoV^p)@WRPG@R8x4aT3MWPyZ*bon(HV$Re4`F zU7F~6prcg$jgBw&ys9V4eXe*#=kFEP^n4=U)%keW((=ERt|>m8x%3mc;jS}XAIZHw z|L5I*fU>+9U;XRTbv?IpaT<06BNadc$1|r&_`zcVHnAFLKEfY`uB{lZZa%~3#y^Ey z8iSblez0%iX1U|ok^oT#fpYCQWECx4F8Acpf6A3C`w5p)_IG21lUqz#8$dp}7z}-kRv1<}(BkAh38N zVK))JW&{gbg(8B+FwF+o_uJ3hEuX>tii(Ft3Lq;mIL0l6G=LBG(x_#FrmV-3&324k=o#%qkjF}4xUBghbQ=+raI*b6TKRuAYheoU=fzNKBXmAvWSLP5VZwe2DuEjzwg$Q$kS{wo?la z2!WaSacxp2%xR}vk8>>w zhu;lHl8KN+o)f3fEwcGUrmkTeHo+tDv|XsmXM`#N_yUV#*b^^Q2cya`bD~@jVsJhj zK2zWI4*3vvaZ~7UM2$$KjAlgMh{qi#WC3TsL!3+dncL+vifQ3LBzHmsc=1B$N5obz z8TLxu8w^L`&o|{01xn;Xtd4?~3;xpo|+0O9uhi91HQfQxn}P zAJWLCAPkgaLzF8MVa|fd;(Ak=M8)mS>e|oTBA-DAhN+8SP@(rR4>o9gFJuDnC@@+f zj(6HaH_L}O!@@lTOwvV;28s_d0!Tu$qh(-r183^ z(|;ykBuOG$>hA^Rvs;hflux}jS4myhc-=SBSJlNo;zgYkIH$fRMxGCs&qvFt@8({V z+CkLhoX|D(t)+EAGy!5wJ=0uh8>e0frl$V2qma4{yfG&@O?`7I8eb*}uu6SZOrHc! zrM}W}AJZqmP^m9Nvz|$UXi`t)-wM%0*`i{h7|G`ZBB{@bk@fTN$1Lz8^)F&*5&)6< zjGR6JYDj%rjMVB0WO(Y6a{2@UHTALP^m7ZU)IY%7erkq~J^W01d+D}qsbA*P+tZ&e z7fN5vuP8lI_+{yLOJ2z?brrvn{*Cm{DihfoDsRpFclia4OMaNXI#<50l5H&cVOmql zYGrpp>%J@fmhO3({@h2pzL&YVqfy-2^}+1M@;lQ@yIz&+D=z68?dt1V(D{=}wehwe zrr+B5%v0%G8WYc?Z)?2eN9o!v$!@@7Kl@R7O|G%yTj`r~!d}pL@e}Er8o&2!`jeUB z>Ba;9m3~9xf1XJ%ZT@b{1}e3#&b?)Zn`7&JTYIgE72JLR=QSCqAaJrEvP0?DE-to z#K;Yf zxYS3*$RzkE^Fm*Uc0 zrFp3*ir+0h?i62{y}Nk0xVAVy{TCfp;m3uKrGJr~S9n8V9CN^}>0S9>=HkrTGO6!3?zkcIZK+lh8Hsa8cvbLl<0c|Tj0hY;D+uWjxp3r`g-wt^X%m9a zMg9Oj5d1tuITNQv94o=!Ix#~gHpkR=1>_>>Qxw<1c`Pz4$d)I(2+L)a;3C{1xHdFb zM^_diRl|8D%)po&g$0tp0qpDINyA2w<#q7c3l&&s^%UF?4r{`%S*?G1tMg1Z&gG5#oQGh~MMy=LNmGB4RU5MYs_drmQ z2yhlLc!7sSJ8)GaPJK~MPpCOrL0E_hm=PfrxONDTCmf#m7mI)@JMfz-C~gaaIYk1Q zMl3G@#srm#up0tqbg~mfn7%7j(0YbU5CRSfp9=}|!N-E9o0t?02S4d{s!a}?6*kpH zUP3%A(pN=#7-1+Pa*5UA30eiP@bHcdX$gL@-iR&mkit5;?1Z z=QrVYT_fUH!4oJVtnejk4CO&~Sa=li%(=LfQvV>g6#F`{iA0DIGsyI~2+6p>AAnIg zBC?GD3$JOrAs7w6qKIjyh7vSI5{HRjo@`qtA>Nf$H8H%oREaR?i=Z1@`XveQazz9z zjtFvU@TC!Hp8AkfA|LuXxcw_PXO@)j*p<07m3?vXK(?!RPw~pazvcAIH?nr&!%0!t1 zU`EHm73rz(h+W3lN_L|NP|%4GxK=+vnuvR2i8NUeBVm$nDBKEo zcSXK0fpC;NafstnA}m*8q)z-gCz(@#Ba`|t`0-*+Ct-tBtRdZskn_~Pijf3pc;Z9y z)kLT&VL2Qx&Ltrg0}E)j)RQ%|*s z5ybI8{%#;XvM3=&fFtD}NS{@3(2x{rrM@nvCnkba3A}&!0mZfD-Vg}{yAf4*G>U=1 zl2Tt`WIaP=D4ozK;x+@3Dg;|1gob;8Pf$41$$9X!)E5L%Lh%UY!7aq`^#L-1G1)CX(@JuIUr(z4QZ`cHQbrJMK=8R4_AX&x+ z(MJS%!4N_=k1Q+VmVCFV`;x$opBjHwB(iD$}NfVs#oDy6_gcwkxjH{CRM>+ClQdjT{6ZN7C2IB~?4P2b;O`;wNuGR4gr#|x7 z!$xN7b@j_qds9D5Q48+#J@4uHgPtQjn|jpryL&E4|E&85-5>9MTlb5*hq^a*-`-vB z`cCE}T_5dwOVaH8RGM!JS_0GpTU)TAYo%eUH=)6Athn4@8d0yp9mA|RH zs&We3xK)`~{?F{v@@L9_S$-MDq>=Kb@@?f(>070bl>RjJ!&3eE*@sI(=AP23(xT!o zi(k*atoV1uKPmo3`dIo)#runQ7OyS*cjirnFJy~_M+?7SI9k|N&jdmZQa(7n+qZ+LVlss)FKQ{PVSOf7B?IbazFqZtF9nMuR2 z$Hqnv%oy>ljF(#2=zeqNy2gDEWV-9#A#d#9;Tc1|e-4q;N6oRBM7}$71ZH@1?Dz~C z-&~aKo%S-`*ntBxNj%*oQE$Dj5{-s&FoVXEGiM3+k5js8#)z-a9N`Wtjxjo8#1olk zQa7}BjH7(&xHoM?eK_@nnM*aDBL`=$xc5&GbG z{p6{cTl&$NLp*&{89X$D$Un>+L3uF!)XW3)LDF^Rw>N!o|H%GRGia<^lwFgS0~$Lw zWv)sOHomtlb3S-R4A&92N1ixy^ko8#rxIA9+V#d z;<|Kq;|gYL{M`c?rE%8gPv3rD`qK10jobIKn#~NA!CZ}{8h^U>E`D_B=|#;M4!=8n zeR^%<&mPPyYuvXtGq3T8!S7$Ub#et~KX&c)=|bZ*ck+*)B{ZyYYF}n@;(M;SuA?MZ zdF|h)ufO4esSS}Y=$WUZnfK*%$u9Spy0A3s*!pIO}K<=>hc^4`A?8}gP$T4nd%b3eQH z>-X{GN0+BBZ|uK^Cl{??jy)ewUzXn4c$u4C)ELzG@q_J<8etb2tL^ln8)lvdV`RT| z^yKuflX`#S<2=#avY)>veSODjao8JQetYJ+OzMq|!-BAXH%oZ&w#>rj%3e8|xjvtI ziTp?l=U$vT^UO%)*7&X5IABG_S1ZOhDWynaRc%EtE}%v@dHlY3=K%7SYO z|6Oyuq?&%Lti#a)NGHgqlR zDt3Om^B+3@wDb9$LFcN@MU`Jx>R+q;UFFr4)0MpyyK-gu$H=mW%P%Vrmp7O1D3?p$ zE`7B0=F$tuxnEPdsgx-`ReZeo+Tw5Kj_0=L)Lc*Y-?N|0zAgLW>|l0X_SS4Z^UchM zGH=ZMR_3A1-I@96pQXQ;es{W&`eFKbdPmwwUpkXToyJ>s`N0_<`a7vqVfgIZ7A$By zdLXl^@$x8h!_DVBBgnN{Zwj)&vHnL_pP#xI$xF(Sw>I+$76ob|yo znd@&#y>{l=K56Uw51p7X)ju|VBhK8g{)TMdG;w!gd{jL(vVX?F56mPrY!8`ZGk5QM z^3)2^oJqaC@%6(jiaXxj__uq~m$QwN98)rg3$WD2z$RGU{~d0+aJu}S1!**1wUY(T z!EGB$_hv4cH%;D6iM#((CVb1x?K@A9OF0hHfBbpr>u+>s4jyyt1Mc9Aukp`~O+%TL zjgN$!x6NVZ@;_Vwk9hk~rn~Wr{h39LzrGhfd#dqTKCSrIMU8{|8EEc@ef;xK=APye zQQniezVU@1b4larh!gwI`!kDI&zyMFIeFyxOcDH*b4J+y$%8Y`<5P|9VRrV}2$P38 z++v7J#>6f7-Nw%XmNYP&@f-Jv#a$`*|7V9YtIpY<5!*d>VCKwUlgH~%-Xf0ITO04c z6LE6S0VX;qc6Djch4F1ulT=ItID){ZoB zP3t>h%`0y-G;-|l%n$VUa<$JrmbosU`fYLe|FDSXVlBRjXR+18F2k{QGHa>c0``q`&mefc($YWXWkwQ4JYc=beId#y>r z$l7a_0I%_*N7M7_2X^h(j`oi(8yr6DtQpxgvVZgN-m(7PO@}uhcDJrucKBdz!+Lk! z_|D_U?Ue_#gTuSDo$HU!vQ}%kV%E@bn+e3{ya>OvYK}Ac%Zc8JwdI|?a+l4%z9eBb z?WM~@BX)VyL3eZi&b76b>qqtt4fk(8vFypm~H=f$FTibD9`_bW9R%WSXgQMUT}W{)7889>-x`&e{Z9dqSI51u|^5ARz!IKDyG zhDU-8`tAeUx77Olqel*J9$lxcA6>Tdlzqq>iB|RR36HPeHg;^)%4pW5>rTzp&Z=LP z`^=jtHHw8yEp}CFv3-3>g3;P*ZK_Rjtzqxh!}~Yx>OFSo=-Oq6PKT>bA01p1DYbzE zzPWF7eBFlO&HL7^TDEH0;X}(#9$Veld%{05+vzoy>w3*LrVa&vH@Iq(iTo3*Cswu> zntg*EG_GvsYQdL*bB?!G;}gNH^=uUxieb-d%iVE^&H z?Xw;V#i*H1@8*6#cX#&N*;iy&WS+^qJhMFg&GbvtPU`8@OU|5GQr|ui6={q>xXPtTOt;gs zVz5KcniR7(8reP(wy0j*#DZ8R@LVu|2d4}PJd*5#EwV{$=t)w1B-LMQQJvkPR-frnyJ!~BWR#gC z`#_6q67SnZqFL)xc|wxx{VlRdyaP!>UnSN1T2#Y`JS7zu3%)g6WMsfl1y)q{1FR}! z%i$lOdO(6WSxI)^6j{1HQ0ohShp4hC%0u8(CH*SID`ZlKh+-9_`p%k-M1}lm=}407 z-WJ&;-pUIkEX~MQl2rG!sFD#*i3Sp{$=HvobvjFsMMSnAz9K4qQP10qZ6`_ak0iUh zMK+0#W|m;#`gy%&B-vdpvLxV%T2=T-D4>BuN2F$x0gh9NG6wk4DCI}hkGa#LB-NcQ zs$^yf0u*p1yBIGSMMvs z+3hW|Nt{|qvacl7Z7r%zzjgiMj_V}x)spPi7TE|ld8Cmaq&lKNB6WmCbpfshjFx5z zR2S219Ph0RuKER2xFp$uDYBGP6Lof|Tp;qQ=!(I`r6dG-x70lpE=$S{BnW4fRQp?0 z1GkRzRy2#?i;E0cDqP`GBs-P-DGEGL#FSOH^S~~c1}4dFX^~ChTbxW3qeA>eGQ}j> z%`LJ?{E&0!lIfwOx~WOEp1L%N(=ti6nIyZhwb&%F4&5fg3)F#QUYcF=r*bJ(>Bxp<}$@Wc=O%TVC#G)an)+g4s z$kOO#&Y+JZ)&{xQH7%+X2fUc~x-Q+?CKWTBylH6wD`It+uI^b(94`LDoGS< zNp)3=Dn-5(O8I*t2m?+?8#7U7j2h0CK^1N)s#Cy>^2Z?3IT}?-vMXC;39cZsm`rMF z8p0C^#33SrJX&&sVT}RB{!M)@rL``4RY|h zetp(*xYMiQ&Vn1ygN-w%)~Lv6)KurZ4R_m9%lv7|>yli4;rhBsu;qDEW9p#b`MGcH zw5R3@vj~mq|S;_&zV{m z8_x6RzO~h!`T{kDDQ+^YsBjV>d(PDAaxA%17RL5lo9(GDR;8FGY0WalNx<&;Q!AKh z&%0bkd+NFB9_w{c_qdISF9?Xwn_5?DM9Q7J;d*=OB!!eI?(lz78bNqgG(}iV31>TZ zYOOtWlG4nRz?uSQ0kNlZ7Fd{^TGMJYNjP_EwLNu`V$ajdG>AqlbJS^=oEi%&D#p1} zE7PVqLvi8(T>QT>*;(7nsi$+XNP2FAUn4CLx ztvz*;3fC7YJyD;f_Jo`oZf4dfsekU&)%Mg$N@mZc4%BB>u#i)8$Ss_O=Y8ut+EXVf z!kt8A(CSkaHzud%m{E-I+_%2HJ#~_@-bvJoCP(4>Pu5t*){S3DId8*nYfnublR4{z zC(%8oF|)cT?~ISru9?PpP+@6%>LiuNljtYYrWP}sbz$u%Zui^`U(%jBNiFjvI?TCK zFE{L(R#)|Nr@pm4^<;JQixx*uqUfA6H7-PzyCwF!j(ejmG;e87ouu4)5@qPTsc{HW zwy*Wl^%fYuxjl7~0`5r^rt_xO%^KC8TT{!K>)`O4+EY&}2`^&fXRQc7msTZ|s`IAi zT2s$##?;JwW4S)1d?cwcpG5OIZ))NPsP=R2)HgI=S`-+cpQLzu67B5#sg)X?3(lQ- zaeKdm8)w$RpS29W>^i)ACUFYEt8R@lu1(?tEv0JWibaBYpVa4))O?r3FP~1FlJ3TG z&6RNt*S6}rlNNB!OppEi2bp5JzBcz4sr>woA9sALls{5>vh--_B_%)q$AwF;DY?Zf zN|zL$DZaPxz2Ym1gVZd%GS@7*@Z6VDW8v`9j>f&urk9>Qdh^YVPydJt4@-XDs)nFU zh;;c!W!lYCZ=pWJ8-AX?O{TmgQFEJh1d`MqY^J;<@v)_zk|{59IL@TsEcK+wkVxVS zYv!CJQJ+Qr7b!1E6u;Ex#q>!umS)aL5=(OGpP^aL%nsFNBDH2F$z1S8pqpl@SQ0eS z%sJVSgsrDOB;Gy=D@;8ukC&+T@z|N4W{wtqb!C2aW#+0C_^T^JHJxc!W<`E&ima}; z_54s(lQw(^O}n<~$#Y|H;qWoh}Bg+I*yp#1sr+sn@{?<*_id|{}ts`Qo8 zBc&G=zDpvcEHqU7o8p<`0T9AtgO_AK)FB1Kg`+9j!4xoYm_w2Pi6##}-!p%{W1iwv zA{UZY4RdQUB}HjR!Ll;&5~150`I0Q#8c0R z{NuSimQsdfF5-2Vaym3Cy(MuZq#hUhbjtwlD~6gaM8BICl}GW#oE4O+nOzXJJyB52Pf~jeQ zQ&vusq@%FFQZ%i)cs=3PG<0*Yr=}53+U7LV`VnIVo3$PBMR6F#;Uy4jO+!PmXlfea zw3XA?37Q!v0dcKajL>qLv())EcyXK4Bx!}5B*3XP4LSDV)XNB`u6&speU!1?Q)>Vh zzAT*F?xTE>h^MK5r`9yot~IBo5l&t?O_CV zpfwFW#U~FM=S(<>kg-N1nC!bP{)2YZ}obdg{{%ud$rwLXE4Z_pPQmlR}pY zvg~QJLB=_sN>t}1m7W&VbTXvRkT%89s1#vLO(VR>a+)MvuP>+!)u&aSZoLf=ob3}J zyvlN#Bt5f}c)VJ(&;wZCzN0?LpM{%QPIIx2+pUJ%NxWOFX(){EPwm;=ty!kJb=NP_ zt~-fd*qSD!Ez#68ceSROyVrM}#cmT?%XA?p)6_JotxQehw5FM>Jv!lww0@eiez+dCrlFpATR_{bX_7QopVcB!Kd0h& zYZ{vHwuOz=nkGqW_KP@*ru;;$S>lkwtyyGK`1%}O z;ggtLTGP-Lt$on+)-)GyDPM2)luzR9X-yN+T5D?0wAM6OrswQOKZ{?7=4hhH4Gq9( zjo{G@Fm6pl|J61}Q(M!BAiKG`*(dSgv}URMw%NwDN^2TX^@*-%H2jH0TOV3RQ@I9F zjRl(kLWdqgbXH4{kJg%oj(Y8vS=O3`7=O+`RedPMLK`Fs*ND1S{E6Of3ceM(uDE&< z-%M*7s%5otK&>?meMsm?M)NPjqo<`o4-{(Jh$d|?-nymHMJg2W<#9wTG(CVptw0jO zZXMAxnLuayQPLJF2!hr$Bm%clO|>-*J*a7FX7C=O8yT4;q8<+s;^0+$P{f84(sG{5 z1>ZGYW@s@2Z zr^FZu1duRAXGNX&qFb{|U-5|QUUW>M!&efwNoyKf*H697p`}ct3)L2IK=~(HV#QQw zV-^~?(N>#3X=Q4Zg)}1ftWbI})~`N3?EBHs(4tj?K{UFk_xRXQ>#{$%1bWvAda}vw z96fF&xhq+h$h2ya5PJMIsaKyZ{N<9$uUZCAc3%E}t!40QD$ASSiS+FUyC3gf-1Xef zCp+({ydF=|L!}RvZYn;%@Qp%m{tfwDN1Xfn+|AkF&U`Dg7OP4}Dn4`Oj{3y)y6jyn zBO*oj=Oq0FlLY)EfoA1R_4ArP6L6#vaHO?zw-;z)eWl6n)K~Q-X=|7yDyRwS>U4Fx z2&kJ^-nPDP-8t>4S)ppzoL11^#C&yn_vb76ru9?2U^~Uu3hNi^Qa_11=UCzS{j-}3 zG?d9ugNX&o%q^VO#<70EK8_?{(3l1RM{Rgq;{lZuiQ}c(neNi6kA`M(NFzboI|hbF zyBNfe5sE^2Asmrb?y5)cJONl|-9lA1(ROvM-t@9g%wINh50f;I{J*h6iK*w6&)>u5 z3eA?odE&a7Tp&D=|8PytX;cy!b_aK!ddoEE>N>u3%u@TxxgtteUWCj zqStL*HoHv}ZJ0oton?7cNpaxQQ#|ITnaiW$^4u+UlSCvY(Sm1LUfo=7IMh3sf(kdz zJb_6X`zDE6M30$Od#+DcpJzXTL@#QT_G#4=nSaB~6G-3Zi**#14TUef!a7ieqScN@ z%is$vapnqV*+aQNGRj+GWbw>Te9=b6^~p}gNy0BLW*cak24-EJ0T;BUzVM>yU$}l% zpPrz9a-tB;1U7~9zHnWJK~ssvs@e8ryyV3Eg)={Kg2v5sqV}*tVPK#cE2ak8b&EEr zR1qdC*^8)`9?}Q;{1w*EGkeSm2IT^2``MCp3#RYkReeeNMb92;TE|{td9;b7$33OF zlNe0S+r!B%L>iNP-Qv^pXKvwz8dW2I+nuUsU*i9l70!ALSs=+bty^-vT%d?Yt=r9ByG8q#MR<3n{GrJoYjhSp-&9tSKLWH zBh>zRsr~0xZF{`<_KOd)Dun zr4}0Ls6Zqp?`*mhD?NFFP=@ zO*40#TC=BbS8eC`v9Xo?rn-L1s-wOB!4Z9MbZDKn=D>;FM(@DU_1?i%;m+RkkAzMF zD``ND#i!EqsM{v~iePBvOmKYnwAY%%>E2#zv-Y*z*b~;yeWPpE>>fHWxNn1dV*Byo zA!p_Bwadn60X(pK)5zX!``2qH_ibIHna2(v+O%CeymOs4%UU_1E@>5$wa;`CU`2Xh zivzEo*xg=h63=~mt)&>ye`w#<-nB=M&Qj${q{xy=*EsS>dfry_O8zQrY%ONoJF%;MUN7kX zpXUB=uT_)-Z%nL&D;(N3wr$zysDA9=!D!&%vVQlJ+PkBF!*4xBn#8yM)@=#SP;*}HvvzrOmYF}}gwvVEVuYt@ZwaI2swQ^`2hkF7tNTyT*4L@o2P$ z9u2##+MqRdz+As}P%~GLdb@Y5U3Ku-x+Bi%S;T}U#RNX8Sr3F16XsMAzut*$txe`Y zBng*jFJ0D@mj^;yJ$_8>-!nWIYn#=vp=H4uf6KDtTgG>vK4`9wJaugQ&K>=mo$bSG zdUu^(J?=$^0)3Vueyp78h?Xh~;UOl(-(6BW+t+SuYq1>O1X;oDwVpWU9Uqlz?H%jw zU*^~YLq}@s`Um!y%c4CV;>}X_oLsl7X6@R%YTNd}wEI^b+GYFk24m&;uHo6&s!>7J z6ctp(iBMq@H6k=7;o9nnf%b_=61dS`YO@l*Tuy^y|*zsMv_9%w}I&bv(`}@}&-s|hj*3ZIuMNYCvk!^`I@pr98#nGw7_O}bbj*k&nYd3@bvgZ80l?_qPbI=+MEAI_*BcMVPL3Uo*KZtH77U)$hWD>JV#YfLjNTLK?E6ffzim%e z)fH|9tERLP;E~3gTZ`?(Z8Ue!(Dqto+2qEFLT}f#-ZVZ$JU=##t!dP z;>a8u>UGx{TeltEbSPMVEErz9KN{Mz=Rp5H%U`p87M3RL^-4`St9(@e2k|#p@f2HF zJ+Z01)LDaD&WUYlFZK9=AUJqfoQN%B-sYWKjtsc!I=c1rZ9KSXi((w88By=r5k1sz(=MD57&fnA4;(`ygy?^pZI=e{a}!L2Ec$KRWtCkp8z(*R^n`wVPsuQf@8 z%(S(Rj~%tne&DKkTQ+PsI<#_V%kdrCP8?j{yKHFZ24~x;{=NJAc8&}UtUTqcJ-Fl0 z(SadnU$9|!|H=(RyEhJavxqdYRK2D*MnBYCDt|RtYMX&=Xfv=RQ8w+R9y?&4k_>E1 zxGo+%JhJuBflXuTmbDx94{u&GxU&D)=*m6r(e=C5_G^x%jci}FYQ)&TY;b(+SafnX zm6c~J0j-(r>ZgxnQ7B@{Y_xh}eS24vMC!Dc+N^3XPr!!Zl?R8N@ng=wI!)bv z^yH{9Ymp_N$4Azb*H!-16)Pvl}v>$?Qu%oj#cQkJRZiXOw{xi}TbH?OUu` zjzOa+A~28nP6qCzQ5=@QzYTb}O48os(9^lX({h%}KR5QFw!u1QBLsuS51SO|%P zh|hZV(*8wFns~cy%?Wg@DH@>%#H|x#O24K!2o37`IP}FLl$~0K26f+PU?l2nhI$pR zpH4swv2rvBqtJ>jX)bKiG*pYqoWd>>VWyz~Ar3`qB+*&PwmqBvn{-Q?CFN+(QsTip0!Z$z%J1Wu{L`+x#CH^#+KOwWBV~aj;6n??Wg?OoAD#FzjDhzTt zB3|>|=TGiu92!_f%xa|Bq5~Agi>M4|S8dpm9U})Km%e;+*SD7IyuL}(CnTEiVnT)@ z6YG@dEk^e(m49 z)K3yABxzpTq)B@>gDzWfRf|l{k}e#&B9bQ56hDa6FeVoJyttrQ!h$5tYnn6(Sh5}6 z^s13U3qHO(<6}-LZPYNnAx;$CP7(wpXAZ6=)(Bg$_D zQ533vl87Hk^QtCI-&g5aCfXxLKHo^yDOzQTIBU-fLt3KQRzy{DBHvV+DbnjtgyiTH zr=)`$&B6%6vRD&c83oPTuWZtE>6qt}SwP5;C^<+)M9!xOyW+1D&5I$Z$l#mwz}Yh^ zmOOHu^{=H3O$P8gsh;2IxupA*-HW>ZtV`*9cjvv8k5&dq4}OTYC8Nck7N1)z7G7Ss zI{&)-l8(RZaB?5Wt;>EYyC?Hx=0N&~=~HAx|IV2+cMqIc)m)0MvC|aTtj1a_;;e`< z^T`^b)~01ealnbzx$K(C?bgZgv3Wg5)a@czfUa#^QBQOZROIXRuWY>@{x{LGk&p+1 zs#F6uLEyRQz^18OFI|g*B*~BRGu+#x$vuf9mB=h7q;#5rxs1!rMT{r|sS*%||4&WQ zo?X(sr%5w1VpE}MW;GK0iavRS2$~N49`P%h4pvtJ6_WIdmNf5f(!@K7(~{0_t|6Ks zS}fLqX;y8RifMx}s8~wqgik;l`^l#B`H4yDSCD72)xqDj;8iRcD?QU+JBY3$5Z(VDOdOYeYXnpg&e+QKSGxUf}~s9?*A zZJ%B7nM6PhhibbXD8Z-ADb^h1uOn$1O_~T{v2$J(2qJ0HsMMlfqOQb->X{LpVu|6L zJM~G@)SEOTDl%(=!_hZ^@9qO%91sxFf{+)@;p|5ATBW0;3Rfn7fpsE@IBJwERC%FR zo|B}hHEFtZK2=4^O@y~p#LW_9suRl^#2yvJ?J$mo8++Ee&m0x_fg-Ashqk1tPST`E zqAK`97>nM!VZhD?hDl{&2fq~^JgK6a#3U4QoHu*f^(tw{FrkR4P)b4in?Np*gD8WRL=@N%#<>ReFmtZEaOEjkp(qIGP7;?9!htxcM22l3<{apw5Q zn`(e>5=Nwg*+jo#urV>+nD+^WzgYb_N%NK_O|CluaFki){{^-IXZm0^un_7_%$M;& zn0jQgDV#D+@&yXZNt!n|X#y%kOQRn%XPfapEXkvvbQM(~25u#nkBOa1&p2XrIY2MhR$@j25QW9ZA0?mmk+-Q-s&W~IunHziy$_?nU;Cs#Uc|(&Xy9G<*qgecj@v6u+AcQ{P zmfALu2_BimguptNOvXHWsUE-k!`(M^J+Jc{ovUd0oGyE%50q{w{#N0s!b*T~rX$FG zICoR_d6{oyR;AxS46%3S%&ql_`I}~X$AvzEq(Olq7>+Z^ThUP2WvQerke*HHdDka3 zfhOI6H&J$%CgC<*hFa%QM5aOvveu{~o*?&mUb)_OKewu}8PZ8-wTy%l3pUR5KGSsC z@kcnwH%(gEla{p?J7-N{)#ZfNEK*TOX!0!sM*|(LiG$_fhoZwdJdU2uF>(&AGS&#wK)V)EvynoR_SsXyXWv3i z_=l*$)(Rt;zi#Fpa{gnXV^(7r3||lgNS>Hc4d```AqUQY=JNl4g>~X#6l1nH7lRdI ztF~KK{{?+B_fW_qq9zT|RGwGIYy__XlF~@QwGclhjT<;}G0n>{#5jmGKnNgs{Vp(EDJnsZa4YmHuRiw@ldf=&vf^+-+IpO>MgCL)!N3M z>6y`E^;)go4(U?GkWdxCpi~A(0x9fNk;IPxRkle&uuZgZs`#7it-fpP#sP)GC@T`e&$Mg zm~ej;6iEZ_$tt#BSHw0BhnZ6b=mmdT&_Q4i;yy#8{Yh+J;ei9|4)}f|LPTH!-72oz z&F#(v?h0sbOj>r1lUo?nEG|s6m>82%9*8)ABaOI@_khInmt|r36xw286u{d}BedM> zox>>a5{w6DlQ+fm@O7Aucb82NV~Mj(^lPP$EG2$GQ3YyX^XU+@5c4Uj?;Hw20v^g< z`m^~S?;Ju$k6Wl)TUl#-00tPD$!6mOZW=0My6PkrD;a2FzgLr&YHo`e8NEgH(+0HIJ5W zz}mYR*_ONZyj&@;4RDcJ4iJH4+sW5%UaxYo*+!ASxKyETy#_d!d8h*OEu zA}g}Lw0mF+r3QP9Ix;w$psHGq1c={ZTriLj+nvKZ_RS$vm?2LA2nmb}c*Iviu$xNZ z9cEMPfg0Wd;YE1?c*eHb=`^wfAkm1BEj>sXjnX+ROd+%p8vDiEynFNQ-YJB1YY-EF zs9R3Pho(*_%5+;Gl$=HjwOAZLfiHo!Y*99FCykV_%1hJ-v(RT5ItGI9x>IP1fp&4X zrWy}#lNFf4i)SkYD==dqkGyCAjtqtSW4Qw=+2a@*w1kU`o)~8vSW6Lj*W%PB9miYY z*#KG7beAGBU`OAZLYtOh91w*%%))L#MiX;C!HG%}a7MmcYh9$hZ_FG7hlNKEyYMQTJVUK#h5=5cI2jrb8PK0x|TNGxN-6|#dB@s;`A9d^_j8JIqWvD*W2fc@WqOM0M z>9Mz0&xM*k(p2C0sm6CUe6Hbi{U6sWbqQ#9VZ1Z7zIaxq**iM2H68l(YTYuVaqiXzIz1_fNoHn zwsFa0#W?RZFa%Cd14SP2GC4Kx$EAu!4Vbm`scGu&4vmAsSxxkoAg#`;CgYP**g^T< z)NSEibBbnyq&drmmT1pC{EAOaS9W*kR1|PndU+*4F^ThVB*>M6Z#yX_@&z{MOpt77 z$Xqnr0$c&$HIoqUAV)L> zrbR@Y%j&6Aii9O6o_E5XBj2Zu6kXHZp%oG>*&Jv)vOe~Eot~m;il6}hCBTPOGRx!v z3#nD=#>ADhKyGBA$wfgyQ1DLEG>5b>vUF(p?V3^cuIYO24h_SA9FduBB}>LBCv=7s z1P$SBfWs<|^nHH@*5<9S_j2a*{91+r61yo_@6ceda?F%}*Yq%VhYkV)mnCYYZcb^Y zwruDqMB(yxO-FKf=pgW}e?Hd+=**H!By6&%Qd2TCuqjXNp8drYPD^oj=pdNmA!*-Y z+uxF*`L5}E@9KYwL(?AI9Xbe9cT{$mx?!c~wWy0=2kK>wSMDwj9R!36l0;VfJu4gl zjwU-GP$l2BxHZ>RJ|cRqrG_t3#O0lJ!;z2Hx_Yk1j{W1Y8>;#$AEDm={goY%Z|919 z#vl$$kS_j8^Hs)4(b4vT!HFa`oy>EDZqwi97?b4-=t6LW@D@%P#=Q>u- zFR!etQ=_9Z9gE}2nfbb9=^R z|*Hy^>g2UFz$dgi@nl8NJxAbj>P@9q}HNqi+v=8 zSK~d=!L`+m%hSeucD!d;&#W&S16J31|Ma=5XEPJ$`&A!t0rSdYR`SpPmc!5HN7gc2 zjlz?lL(I7=0?lLqpRZ`^v>u<$I!igbvfkOLB)1a7mzA>%=aU!OhPqYh9=VN)G6&mlp=~zS-%EmzJ;1v{|bo%Ny&<=_{8P=gzHZn;q-%zKPzLKHAx{ zI(5-+cjKJmu!!zTV>Eu09#Ssq)wP}3TM@}1E@shGsI=Phsx!;++_j0#Ygf#fwTYF! zj`3(mZep~*ZG0=UVvNkDQypeze7WoV*wxW!PVZ^&*vJi@Px~;-F%HEf`Ci|(%rDud z_p;1g+m>s&o=gUDQH!Rp#+B8WDW(t3PR#d=tI628?yhXsT3Z-4XL^*?`PuQzxmjbg zS6fMrbk3Wb=eoMjE|0C~TMP5!Z9WPl&B+SDhZRvR`Z@Vo<+F4TkG`YQa5S~@Gdfi_E@OL3xf9Dq9Drb0Gq`w5r-GUeUGvkZU;~-hMCoASb$Lx{_VW4)X+Q0hxa4R--Lu)dXUH!(8}}(Z z9&}A-HJ5qeK2CVi^vu*|Hj#E_ePwYYu@K$3qK{jPL)!F}{+_u@o!O1~l%~yZB(j6O zgYoo0=g5Wj3#${ulkIx@YBn=bzC?u4VGy+X&U)dOV7I&bbl3I=UDKI$e=pwa#1~Cp zFw7NJH@f`6w)FUlndlfDot}-a_o)}O!OJVwYFq#0`sKOa^w7#`${d*M1M-^Ln2avv z)BkLSmrr+R{L8$5?J|dgR(Ub+A3=qilwryH4>+fxB*)5g$`N+?mQi-Q(wyR&KRN`aMhBDBG-UDsub-e9A z4cpr-DKF@4P>&U)8%h`~D9}#9k8+^4I><*1eh7^c%pgc3tTZyjF#(4P1@y=p2<;s> z8u%$eAMpgWt!$qngt)DgbpgTJT|&(dc|mWgCXwDGG}cj`fcV9MMucq0ZbWZx(gQi- z1Yv`e5SuQ=`IkzryAZdZ&=2lom}>hL?m1 z@CKYF@WYsDfto7RwKZvepqoKJ-C!2_PNAswqqh5!PdwmQxR*DevMq`o1XIv#i0gh)}~Uj@QZxdI1;!;DUzt|ZlT*JC%pz{(qyPC2MslU|AgnL5~UD|nn?BVEby;YIxo1eZ1v zu9w(y$-4$Mh-q3JdI6^ZH%BVL{0Rd$hU(7JHS#^Va>(3^nA-xGTNVI1^&h3KI1@;| zOHefubz~P&IUyp-_}4nsh;8KfblEP=6D4aFHV@Ti@j z)OIPV?=>;Fg+6N#1Ch_Wr{wmD@xm*1wi&o2QHtb2f?(xYDItx*fp1N-WCTkk`3Ji= z7E2v~{d=H)vTXprtUOh?esOZ1ykqu#g$IS@BMCUm$2`@RQ=BB#?ooGQ%xfPS357!h zfE&Vt@R4bp6|BP@;8NnJNSnS=8KZhC<7=@3;(j?)IjVU6*aNXT0=G+rTBDGY5h z;b))Z{@96k&1<>nU4a}yiiPL|4x&S%M_dTYOJimrt&<9(dL#m$!*U2VNd!KKAWg6Q zcR?bl7*4)RgJ>T0u0Rn0!j~fnYDsH$1L8MKN%G^Qsi1*bHiTE}a1(zK3w@^0F%Ut7 zJB1^jDYTQE7Y2M8p`nXtKuDwMXeKRRu?`eGT1Z1%5)Y2^#9R9+d{7mk3omUt32(;nZddBAq3hV8A9p;Xvfa9RK1nh7lnhk(p&f zK%=7>pq`q4xmYZu*oo@)SC@dRwthC$^x39(6&+sGg@?v7I8h}*ugLJ$guI}C#H=-x16?l9Wn?zH1gOJ>17N^9*7 z!#P%LNpjI0CWu#jXcoXmT#&}Ga)&V~=dkxQqwX+4JjWmqzB>xCW3@1fH1r#BhdImv zbp5{|aJxGUT{()aZ-(7bJYF2c3|}`c2vY72V?>o=kEJ1Zm>`}`3?lfEObB<#7p3pfJ++k3Q?e$oC=K1g!8|*p8l3?U*XoaA}2yY`n!09;B za6RC5Lf#cBwE;~k$QRm^T^QZI;@;KNrGNk$13XA>jXMtCiNYk*nq$#xa8(T@7UYX` zZaeGVmR2(4%Y~Q#gO1b0B(77zK@8}cG2~L3XaWN8oqBAscE-Iea6(Sc(gO(FAi^Oa zhmj0LjB*sBsZXJdrKST>IrJVWSilp-AnzkP!rjIFOYMGejfj!eI*)2s$Yns+@D%oO@fsG%5TNZ6&WJ+7DtWv8@#s4MqvH4%G}q zFXFBd)|FYa6KeXWrmr>qZqsJdM3dHZW8<5R-){U0%)YCQLyZ$>X?6S`4KFwRNyA4Q zrW<-1Zf&Tlf4%Lq5Avl?;uP5v$}8BeYx&abyw>K>)Pt-BX2}riu_UJHzK)6 zPvq7}W$h1<1b(jeS8MO9y}kCPn*Xl(PR%nlJDfi^)l{9TJYTW%L`zHlndxvx{?Tms zRL{+oo#7&0DQVH!d$Sv<`K7sRo3%I}`ccURl5Fx^@UJ&lc30$w{-WY!MSK2xR#?fu zmJ8pIziB3XOVurv-QoPNJsv(84n3WJKf~p3p9!Z=$^R~nS-?m=_xygo;_ixD@?VdlnbBgI#rq6 zXVTi{QgmZs%5&lK-idGu(s?fUn|x|-^#5EApG<~6T5?w~pzMwb{)YHh;il`HN-@vA?FZpE8bA35 zHw~?I6%@A$tWFS$EbJ)pgkc=A0k9qAbY$x})kvcW53*xB%HYmj6))Nql#+sZ!>Eeu zXv5QpttebU{seI|EL}LOM{`(3#cM}GKM5(JmqHZ_P4UWT<*(Pi7|t}Di{$EmcI?OX zXPW-5@$SeC4Oi>c{;P~}b@2lDjzrE=rkx$gzRzDK{=f)c= zYib^=`f2E;$X%6}o9e2D>O<9!G`zc_yYBv)S86{~`Sa>0o1Tt5SF_dlX!U*dkJlzE zPSy-p*xj`)O%K$(+3?w8J=N;5=Gr&vo~n4h?z`1*g+CU)qw8DU@GOyQ7$|H zU$OT97Qt6Woht+293ygJ5V&F3x#PiMKd}C8+&|>p@{kDJ4ZsJhzThm3Bjex!x#JP> zsO^`phvg03(hU~gy9G4!p|RT4vhqnXP=Wh29Zf8Z>zLf z83E+VJ;z=MJ&dmPNY!w+j2%S2+a(vGLp@qZ+v!Zu(UR75$MGy5V*p`kyWFuL@Z|eD zOyPO2Gj;%>yPFyWF1r`D!1I%7Vp5Wc#1PN1yR%l1oSi&YapPbw?oT{CxljyK;2+p zHO|U~hc}6{V^T5CYO8%C^mvedZLN{#c;8Zvh@=?Mqm;{{0UwllM-)ii%@`#@Ow0Ne z=V`2rl$*jds;UrL0t=ROFSE1S2jt2ijl|yXJPv9}CuxXco?IkVW%z!{o91W*OYc(@ z?}~XwE|kt%M>W>EEn+{%`v$Q@DUB|GhO?6PqizJX77@b1t^o5-Q%CfDa?2o%rrs+T z9?|g0>GyQ2W27CR_0nm%GDs7o_teE$YXN!@osv5`J%N~iQffRQuhFTrD8(7k4P|$G z0^K542IuRJ5H)oe(`fK1JVRxQcglsdya3<8J__A%VJ^M%KVQ23*gG0N8+v%>lV3i) z^VlnGw`NU)w6J8e6NtAcaDt{92^rNy2x+KVLd8e(rk$U?@_1F_qoI+V&wca1s+u0) z?|K|4$Jsmo;lX`8Sijd&U&iDTE^v-u*JChTf<7~}lsOh^+Pc?nG>Helz zQ>5{Sjn6fHqA}NaSK|#0Z#2Bv@R^3ChGaua{hRf#)IV0gRX<#RTisiA->G}D?vc9t z>eRa0$PXgVMm`?NM(&JUU;E?Q7ivFUyI9*(+g$V0nwM+7P_qf7aed9sS6^)#=6j@w z$I)vmutT%+>Ytyst7Q_4r{^Jur(76>-t(YyVSuDXKNs2+fnJVt%Y^iQBJly*%hneq zHH`QSNitv^Ul&4iO>;K1Ahkps7l;$yK2mJ&9Pt5tzp`NH;8- zWrB8o^V!pOqx@Hp~o~`Y$ zoozQp$6sl^wcH!`amIHZ beYJHUuvZ$$`Pg~-wKEQY5ZHGQ6q^4pLqN9# delta 4458 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 = ({ lang }) => { const { sessions, updateSession, deleteSession } = useSession(); + const { currentUser } = useAuth(); + const userId = currentUser?.id || ''; + const [exercises, setExercises] = useState([]); + const [editingSession, setEditingSession] = useState(null); const [deletingId, setDeletingId] = useState(null); const [deletingSetInfo, setDeletingSetInfo] = useState<{ sessionId: string, setId: string } | null>(null); + React.useEffect(() => { + if (!userId) return; + getExercises(userId).then(exs => setExercises(exs)); + }, [userId]); + const calculateSessionWork = (session: WorkoutSession) => { const bw = session.userBodyWeight || 70; @@ -455,25 +466,36 @@ const History: React.FC = ({ lang }) => { )} {/* Side Selector - Full width on mobile, 1 col on desktop if space */} - {set.side && ( -
- -
- {(['LEFT', 'RIGHT', 'ALTERNATELY'] as const).map((sideOption) => ( - - ))} + {(() => { + const exDef = exercises.find(e => e.id === set.exerciseId); + const showSide = set.side || exDef?.isUnilateral; + + if (!showSide) return null; + + return ( +
+ +
+ {(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((sideOption) => { + const labelMap: Record = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' }; + return ( + + ); + })} +
-
- )} + ); + })()}
))} diff --git a/src/components/Tracker/ActiveSessionView.tsx b/src/components/Tracker/ActiveSessionView.tsx index 9d20d79..ec8d5c4 100644 --- a/src/components/Tracker/ActiveSessionView.tsx +++ b/src/components/Tracker/ActiveSessionView.tsx @@ -60,6 +60,8 @@ const ActiveSessionView: React.FC = ({ tracker, activeSe setEditDistance, editHeight, setEditHeight, + editSide, + setEditSide, handleCancelEdit, handleSaveEdit, handleEditSet, @@ -243,6 +245,39 @@ const ActiveSessionView: React.FC = ({ tracker, activeSe placeholder="Height (cm)" /> )} + {(() => { + const exDef = exercises.find(e => e.name === set.exerciseName); // Best effort matching by name since set might not have exerciseId deeply populated in some contexts, but id is safer. + // Actually set has exerciseId usually. Let's try to match by ID if possible, else name. + // But wait, ActiveSession sets might not have exerciseId if created ad-hoc? No, they should. + // Let's assume we can look up by name if id missing, or just check set.side presence. + // Detailed look: The session object has sets. + // Ideally check exDef.isUnilateral. + const isUnilateral = set.side || (exercises.find(e => e.name === set.exerciseName)?.isUnilateral); + + if (isUnilateral) { + return ( +
+ {(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((side) => { + const labelMap: Record = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' }; + return ( + + ); + })} +
+ ) + } + return null; + })()} ) : ( diff --git a/src/components/Tracker/SetLogger.tsx b/src/components/Tracker/SetLogger.tsx index 30bb41a..f70acb6 100644 --- a/src/components/Tracker/SetLogger.tsx +++ b/src/components/Tracker/SetLogger.tsx @@ -101,24 +101,27 @@ const SetLogger: React.FC = ({ tracker, lang, onLogSet, isSporad
)} diff --git a/src/components/Tracker/SporadicView.tsx b/src/components/Tracker/SporadicView.tsx index d33b7d8..18633c7 100644 --- a/src/components/Tracker/SporadicView.tsx +++ b/src/components/Tracker/SporadicView.tsx @@ -143,6 +143,39 @@ const SporadicView: React.FC = ({ tracker, lang }) => {
+ {/* Side Selector */} + {(() => { + const exDef = exercises.find(e => e.name === editingSet.exerciseName); + const isUnilateral = editingSet.side || exDef?.isUnilateral; + + if (isUnilateral) { + return ( +
+ +
+ {(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((side) => { + const labelMap: Record = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' }; + return ( + + ); + })} +
+
+ ) + } + return null; + })()} + {(editingSet.type === 'STRENGTH' || editingSet.type === 'BODYWEIGHT') && ( <>
diff --git a/src/components/Tracker/useTracker.ts b/src/components/Tracker/useTracker.ts index 1e7dcd5..786a73a 100644 --- a/src/components/Tracker/useTracker.ts +++ b/src/components/Tracker/useTracker.ts @@ -226,6 +226,7 @@ export const useTracker = (props: any) => { // Props ignored/removed editDuration: form.editDuration, setEditDuration: form.setEditDuration, editDistance: form.editDistance, setEditDistance: form.setEditDistance, editHeight: form.editHeight, setEditHeight: form.setEditHeight, + editSide: form.editSide, setEditSide: form.setEditSide, isSporadicMode, setIsSporadicMode, sporadicSuccess, diff --git a/src/hooks/useWorkoutForm.ts b/src/hooks/useWorkoutForm.ts index 82fb231..f912a32 100644 --- a/src/hooks/useWorkoutForm.ts +++ b/src/hooks/useWorkoutForm.ts @@ -26,6 +26,7 @@ export const useWorkoutForm = ({ userId, onSetAdded, onUpdateSet }: UseWorkoutFo const [editDuration, setEditDuration] = useState(''); const [editDistance, setEditDistance] = useState(''); const [editHeight, setEditHeight] = useState(''); + const [editSide, setEditSide] = useState<'LEFT' | 'RIGHT' | 'ALTERNATELY' | undefined>(undefined); const resetForm = () => { setWeight(''); @@ -33,6 +34,7 @@ export const useWorkoutForm = ({ userId, onSetAdded, onUpdateSet }: UseWorkoutFo setDuration(''); setDistance(''); setHeight(''); + setEditSide(undefined); }; const updateFormFromLastSet = async (exerciseId: string, exerciseType: ExerciseType, bodyWeightPercentage?: number) => { @@ -104,6 +106,7 @@ export const useWorkoutForm = ({ userId, onSetAdded, onUpdateSet }: UseWorkoutFo setEditDuration(set.durationSeconds?.toString() || ''); setEditDistance(set.distanceMeters?.toString() || ''); setEditHeight(set.height?.toString() || ''); + setEditSide(set.side); }; const saveEdit = (set: WorkoutSet) => { @@ -113,7 +116,8 @@ export const useWorkoutForm = ({ userId, onSetAdded, onUpdateSet }: UseWorkoutFo ...(editReps && { reps: parseInt(editReps) }), ...(editDuration && { durationSeconds: parseInt(editDuration) }), ...(editDistance && { distanceMeters: parseFloat(editDistance) }), - ...(editHeight && { height: parseFloat(editHeight) }) + ...(editHeight && { height: parseFloat(editHeight) }), + ...(editSide && { side: editSide }) }; if (onUpdateSet) onUpdateSet(updatedSet); setEditingSetId(null); @@ -137,6 +141,7 @@ export const useWorkoutForm = ({ userId, onSetAdded, onUpdateSet }: UseWorkoutFo editDuration, setEditDuration, editDistance, setEditDistance, editHeight, setEditHeight, + editSide, setEditSide, resetForm, updateFormFromLastSet, prepareSetData, diff --git a/tests/workout-tracking.spec.ts b/tests/workout-tracking.spec.ts index b63b1fc..d8b9885 100644 --- a/tests/workout-tracking.spec.ts +++ b/tests/workout-tracking.spec.ts @@ -392,38 +392,73 @@ test.describe('III. Workout Tracking', () => { await page.getByRole('textbox', { name: /Select Exercise/i }).click(); await page.getByText(exName).click(); - // Expect Left/Right selector - await expect(page.getByText(/Left/i)).toBeVisible(); + // Expect L/R/A selector + await expect(page.getByRole('button', { name: 'L', exact: true })).toBeVisible(); + await expect(page.getByRole('button', { name: 'R', exact: true })).toBeVisible(); + await expect(page.getByRole('button', { name: 'A', exact: true })).toBeVisible(); - // Log Left - await page.getByText('Left').first().click(); - await page.getByLabel('Weight (kg)').fill('20'); - await page.getByLabel('Reps').first().fill('10'); - await page.getByRole('button', { name: /Log Set/i }).click(); + // Helper to log a set + const logSet = async (side: 'L' | 'R' | 'A') => { + // Find the logger container (has 'Log Set' button) + const logger = page.locator('div').filter({ has: page.getByRole('button', { name: /Log Set/i }) }).last(); + await expect(logger).toBeVisible(); - // Verify Side and Metrics + // Select side + // Note: Side buttons are also inside the logger, but using global getByRole is okay if unique. + // Let's scope side as well for safety + await logger.getByRole('button', { name: side, exact: true }).click(); + + // Fill inputs scoped to logger + const weightInput = logger.getByLabel('Weight (kg)'); + await weightInput.click(); + await weightInput.fill('20'); + + // Reps - handle potential multiples if strict, but scoped should be unique + await logger.getByLabel('Reps').fill('10'); + + await logger.getByRole('button', { name: /Log Set/i }).click(); + }; + + // Log Left (L) + await logSet('L'); + + // Verify Side and Metrics in list (Left) await expect(page.getByText('Left', { exact: true })).toBeVisible(); - await expect(page.getByText('20 kg x 10 reps')).toBeVisible(); + await expect(page.getByText(/20.*10/)).toBeVisible(); - // Log Right - await page.getByText('Right').first().click(); - await page.getByLabel('Weight (kg)').fill('20'); - await page.getByLabel('Reps').first().fill('10'); - await page.getByRole('button', { name: /Log Set/i }).click(); + // Log Right (R) + await logSet('R'); + + // Verify Right set await expect(page.getByText('Right', { exact: true })).toBeVisible(); + // Use last() or filter to verify the new set's metrics if needed, but 'Right' presence confirms logging + // We'll proceed to editing - // Log Alternately - if (await page.getByText('Alternately').count() > 0) { - await page.getByText('Alternately').first().click(); - } else { - // Fallback for i18n or exact text match if needed - await page.getByRole('button', { name: /Alternately|Alt/i }).click(); - } - await page.getByLabel('Weight (kg)').fill('20'); - await page.getByLabel('Reps').first().fill('10'); - await page.getByRole('button', { name: /Log Set/i }).click(); - await expect(page.getByText(/Alternately|Alt/i).last()).toBeVisible(); + // Edit the Right set to be Alternately + // Use a stable locator for the row (first item in history list) + // The class 'bg-surface-container' and 'shadow-elevation-1' identifies the row card. + // We use .first() because the list is reversed (newest first). + const rightSetRow = page.locator('.bg-surface-container.rounded-xl.shadow-elevation-1').first(); + + await rightSetRow.getByRole('button', { name: 'Edit' }).click(); + + // Verify we are in edit mode by finding the Save button + const saveButton = rightSetRow.getByRole('button', { name: /Save/i }); + await expect(saveButton).toBeVisible(); + + // Change side to Alternately (A) + // Find 'A' button within the same row container which is now in edit mode + const aButton = rightSetRow.getByRole('button', { name: 'A', exact: true }); + await expect(aButton).toBeVisible(); + await aButton.click(); + + // Save + await saveButton.click(); + + // Verify update + // Use regex for Alternately to handle case/whitespace + await expect(page.getByText(/Alternately/i)).toBeVisible(); }); test('3.15 C. Active Session - Log Special Type Set', async ({ page, createUniqueUser, request }) => {