From f764f06f0d0d4350687396b2d87aae1074712926 Mon Sep 17 00:00:00 2001 From: Chirag Rami <87525902+chiragramimi@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:06:01 +0530 Subject: [PATCH] Feature/ai-adviosr (#13) --- .../xcschemes/NutritionUxExample.xcscheme | 2 + example/ios/Podfile.lock | 4 +- src/assets/icons/food_edit_image.png | Bin 0 -> 11676 bytes src/assets/index.ts | 1 + src/components/filed/FiledSelectionView.tsx | 120 ++++++++++++++ src/components/filed/FiledView.tsx | 138 ++++++++++++++++ src/components/filed/index.ts | 2 + src/components/index.ts | 2 + src/components/listPickers/ListPicker.tsx | 17 +- src/components/listPickers/menu.styles.tsx | 10 +- src/components/logOptions/LogOptions.tsx | 2 + src/components/picker/Picker.tsx | 5 +- src/components/tab/TabBar.tsx | 91 +++++++++++ src/components/tab/index.ts | 1 + src/constants/colors.ts | 2 +- src/navigaitons/BottomTabNavigations.tsx | 1 + src/navigaitons/HomeBottomNavigations.tsx | 6 + src/navigaitons/Nutrition-Navigator.tsx | 14 ++ src/navigaitons/Route.tsx | 4 + src/navigaitons/TabBar.tsx | 4 + .../params/NutritionNavigatorParam.ts | 10 ++ src/screens/advisor/AdvisorScreen.tsx | 1 + src/screens/advisor/view/BottomBar.tsx | 85 +++++----- .../view/message/MessageResponseView.tsx | 18 ++- src/screens/foodCreator/FoodCreator.styles.ts | 15 ++ src/screens/foodCreator/FoodCreatorScreen.tsx | 63 ++++++++ src/screens/foodCreator/data.ts | 34 ++++ src/screens/foodCreator/index.tsx | 1 + src/screens/foodCreator/useFoodCreator.ts | 36 +++++ .../views/FoodCreatorFoodDetail.tsx | 151 ++++++++++++++++++ .../foodCreator/views/OtherNutritionFacts.tsx | 130 +++++++++++++++ .../views/RequireNutritionFacts.tsx | 124 ++++++++++++++ src/screens/index.tsx | 2 + src/screens/myFoods/MyFoodsScreen.styles.ts | 22 +++ src/screens/myFoods/MyFoodsScreen.tsx | 47 ++++++ src/screens/myFoods/index.tsx | 1 + src/screens/myFoods/useMyFoodScreen.ts | 9 ++ 37 files changed, 1121 insertions(+), 54 deletions(-) create mode 100644 src/assets/icons/food_edit_image.png create mode 100644 src/components/filed/FiledSelectionView.tsx create mode 100644 src/components/filed/FiledView.tsx create mode 100644 src/components/filed/index.ts create mode 100644 src/components/tab/TabBar.tsx create mode 100644 src/components/tab/index.ts create mode 100644 src/screens/foodCreator/FoodCreator.styles.ts create mode 100644 src/screens/foodCreator/FoodCreatorScreen.tsx create mode 100644 src/screens/foodCreator/data.ts create mode 100644 src/screens/foodCreator/index.tsx create mode 100644 src/screens/foodCreator/useFoodCreator.ts create mode 100644 src/screens/foodCreator/views/FoodCreatorFoodDetail.tsx create mode 100644 src/screens/foodCreator/views/OtherNutritionFacts.tsx create mode 100644 src/screens/foodCreator/views/RequireNutritionFacts.tsx create mode 100644 src/screens/myFoods/MyFoodsScreen.styles.ts create mode 100644 src/screens/myFoods/MyFoodsScreen.tsx create mode 100644 src/screens/myFoods/index.tsx create mode 100644 src/screens/myFoods/useMyFoodScreen.ts diff --git a/example/ios/NutritionUxExample.xcodeproj/xcshareddata/xcschemes/NutritionUxExample.xcscheme b/example/ios/NutritionUxExample.xcodeproj/xcshareddata/xcschemes/NutritionUxExample.xcscheme index 4042878..30f6f00 100644 --- a/example/ios/NutritionUxExample.xcodeproj/xcshareddata/xcschemes/NutritionUxExample.xcscheme +++ b/example/ios/NutritionUxExample.xcodeproj/xcshareddata/xcschemes/NutritionUxExample.xcscheme @@ -50,6 +50,8 @@ ReferencedContainer = "container:NutritionUxExample.xcodeproj"> + + C3U0;elhc__ zSbU2`> z|Dy-03w?Gm|L2}x)Fs7;p( z9D4dmEYeGY)?%L6N(|)d9LXZO;{*ZyrAO?9bI%aFNKs{Jb5UX_j|Ttpp}25gpn}~i zMk`A%BG1HbY!C^lS)kz`3{32o>?kH4DFnanR4*xQM*xIKeG6c7>GoPQva=PS0F6Sg z_DYmIs4FjU5hc^66Z_GImmG8Y(UJ)MpO=;Mp~3!y*%Z^0A0Xhon9eb+*Ef(kEg0sj zGA+-{Y_2B3w#Kf_Tc%)a2r<(f@<76St3ap@4;4fX@78q(qD|>|G8?ld;5E8|GGSWS ze_xz`Ldxa#;W2}KItK(zj&V9b-9T;pF9=j1Im={2OiCIGVNV~A06-c{r|!T}p&!%I zY{1oBP%0Gm0V;9(H$ZWF7f4ucIBGGN8BLXl*goNZ^MXYbk*7<0`}5)-ZGDDiy=kUy z;M;IccIefU&%6?taPsLYu*PT3GPcUkS~=w^j&@eR@!&~LN|BlI1H|Z=>a-{%e=v*L z1{YV)hdro67)7wGEbvPo^FIP}nLSo)#EE;YegLG)809^-GaF=F(o${3a8lQ$y!u(7 z(~QZ-2(yNqdtNSTj*9^AIEUezY2&iia)>Vwir*^xlAvW_QnD#e62|;D7_1}|zqxI^ zVMset;Dz-D1D~*qgi0tSF?6jraw8U#-UA2t;=%l)d`Z?>EEXVE)dv;fu+;RvriTV& zBiJB}t6s#dwrk;pF+1R*Ai5Jj4HM~Zv!QZW=OLtv#|Suap4IiWy&x_1>5)`Lw+4pw z(lCeuf5wiNYu9IpgoVRK8p+{L=!7A}z=uAcRr`SMN|C7yJV+-7EI1vX_#HU-5KzZf z{TxOLlJa`HzjyEpNBwY8%_XSnH7R2;5v0??nwrFRs`eMkQvjP@s2?SuQuW?hj)F6{ zoh+YLLXd51=xXI=E9)ul5-HcS*Tjs){`pYR0s~*8NaO=0ey0!tfoU5*EHhv~NVM_c z;}f>8an}$9dql7wozGj!m59}tW{Tgm^oD;>;P}y(SF<|UT`)5%lB(X>-LYAggaIAW zRLm0`LjKW2wREDcV5ov)wHeA->#M!26I6UC<7{JYVXG7BJa4n+JymG$p6A3{ z)=jG ze)aJNcLuRMtq>E_6w98a_G-b7-V!Rj)Wi92*;C5Nmf^KjfxRko@3hklt~)>9)%CQM zxty-nj)6QCLB}RN!TDD=f%Q2nX0Inw9!_+7vJE}$^f=_RXye`yzJ4ucR%_q3p4M?= z@Jy1VHk>ZAf#jxnHTz>lpNn2i!#VbrTQjP0%jD~cx3qNE%E=IsNnBRI@WhzEi8Ne? zZ)y6X;dVjd7W#2a!uL$86!WWQ+*4X1j<|Ne)0(L0*RNP-;WxFm_pL2X|4y)(8Ax2C zE@Glt>Kc7k6&L)56MjW(Hs$`+f;akPioYK~-fRUz9mQ9fJI7(n}4YuyoN|6Cj+ozBPWhbTqwtVl;YW!&TcKPIX3lPuwYhV;DOj}-_w(CWE6dZG2Dk~S z#36fyyW*ejk_Fd~Lt3lv)~BQlxxz**8tPjdr7sIgqo+(B!qZVAw4!Z>{?2=~RZh3# zlj?5}$fls9XGH`Hy-ioi>>IWLcd;uKYDA|4bIFcz$^}|97QC4kgQo1eM$V}z|Rb!;>b0-d+)}9h=2oAt~3LP^?5`%%)WFfkW&}b`LZEeIlx0s6D=;BVHkvTkq6CujX$*#?#71 z31$j*NQ!(-%JF&qRC~Lo4(_bI@kbiZ@~z!DiyNX}ev&z@UgS9oI~$ntM_IL7kuyIS zj;4a3X0k!WT&*7~AGiG#+NtbtG=8DmCa6M-y`Ba7s@yy+-FJC9ZA}zmb zr0_O*cNY;HE4IiKKprgL$bH#!>YC|u9k*@nu`wX%eW>KSF|)Y!$v%Xqczm~mVrpc| zB+kA4lCI#=JmJ%l2y2BUc7a%VVHHKqE#HOkM08DGj#6t*pH~-abFUNXLxgN3>cVQ| z@UK>P%R~B|n3?O!cwj`e9u<;bpHi6+&j>^=ya39-`KOfpg)r<#zti9T=UuvP#X@fQ zk(xUBvhD|cZ`H*7emq}!qu!4v=c{!3{>SJXz5s%dR-F323BssZ-@E+cJ7|=5&2g7a zhUJ3G=-+(O@86&H9=I_N4iX;OOye=nHyyunpLlUidM{Kkzx9S&7?lU1Q&e+YaP`!g zuF=JLOb}mjKPV0wU)2N4WYGIejP$qGmBtf<5a2>Gb~`7vEOyi`qj}x@8`eL_s+ld^ z_!_XF==BY!v`EwmjXp=k5JXtibR4orrzTCr0nT?Zv2r3_4fX%VwBP(-T>E^nvq(X< z8u0EWl*PZyz;1%^|`ODz%@=-mF*dKGTm~I;h~51mpX!TbU1Uq;=@As2^8Pyz+Oq*y`5S z9$u=9{(NlnW}&0MwKw7dtGK%9Q5eP0r=s=Tut&mPuf!!XY@*k8`N)pDL}VA!+P|Pa zs`Ih7`O0@!=u} zNcwvZSr~N*<5`tZ9}e=l(}uRa=dHu1eqJuoV@m`thUitUt_ENa-@P>M2)!K3ZTd8| z!gXPgiqf_yS3;~G2wiyMQls)ys!4S6rSiO*Rdxl_d!_Q-uyPZ9jm*27>y2qq?mIH{ z2d^mkCzbk)<6n(IUSBF#%pbR%N$MKLNr(Ef_^ZX?2-pWdG-?$l-6d%sWLoiv{N zab-PgI{iiO-{|^clY5ao9n`1hkIS$;2lEPxmyyk%aR!sU9Uieq%EWEXr}BM9S&Jio zLErXO_6I$SB>v}vNX0vv>G2TZn&(_f6*8lCpF*!fCnWv?hHp_mr6pLt>Bt}T?_T|DgV(I@Aaspbf1%y*fj zJx)tYhCd%;W2GpUYO8&yN?mwoGzOTR5j#$OcFj9iAThctM{PafeEfk0zc|a3;&Xp1 zdrAe7abN1Qe7w@QR4n*l-49*A?ktL&v!-e0rt%1e2-q{4Z}W4;zxPj5vBFraL!sy8 zdWkLJdBRcL#T}uR=&i=;$cx;GLZ)KEr}iXsc~01f9U5&uF%NQ=wvEC&u)O zwYsSE1aKh%f9yz^rAX>46Q20M>%OS%m(8$QE`oA}OsHaS#1kw}?1hvxws&ajD%2f| zv*w?oKib)Lr3FLA@hKT|L>rW4L-tCCl*|L3f__)P-@$Iteb@WH8G30Q33Wz06qf-r z|Jh-|dAJQhbmAs@7kL>p3&#$ZHoU43k2B<&+M$wm=L@WP(ds=mcszZNK_XvLyce%oSlS#UWRIsa`}_`_u*gT#4Cpjp7_sc=%aeD_Hs-@6RgmANO;S7g&Q zuO0=gY&!^Vt6hnRR|*I+}bsj%(-LZ7jq#z|3KZz!(DwB`HTm1%;S>u$3uht4K{z zd7`gQ%|hJit`oi(gV=_@)XYTGVy2zJ;Xf`App3@2;mn&#=FCdkq|@Lpw9v*)>rM>7 zIxeBV)xL34d8Xpb3Mn^;hV&5=wFT{%yx%m4#l->^Nu z(0)0ahXl-S@PL8Dkp9*U2a6$IY|`+{7Dm} zLhT?iaoL_!zxx>l%3au+^rBh9LQtbOy#iaw)3ipLx}_{ zVk1S-HbTBBh21&Px378hT@#aKFVJtrn_(#WRoRDhIg@*z97=fV{{@ZRl87jF<$a^3 zOML~ft3o7Q-mDJl!uk)++BdWY!g&-ASZ+pK}3n8ycKcnzj z>^h$u;_GsnvmhG&c(_U^QP45GoiQ+4y!`z=^I0sWL1V7U{jHiO9>Vf(u8->&iH(s{ zWA})%dz_7d?$sQ2aR2}l>-p&7*f0F+l=@fDb96T-j{QVmrD)GAI;cem075(pJ6ShQ zns59*NU!7pM}Ay!iuktQeK9UGi(@<27aN|CHaA^PB~s%? zAC!?=H?U;JxGr7w`7*Pm2`zA>Iqs4*C{*ATt+##aeC28HB)d`5|8k8pP8Po+;SoF8 zC_RVhOJYhh(1Q8{fM^K+y}nrd`7|8J!Jq|J*`Fu+vXGB*JLrxC5(1UvY5lE(gL|Ke zSisCeA7-*K5l_&X+2^HSl14^{17^V@0NQFygodaoetN=WolEG#14B+7l1Koi4g?wV zwk_wKp_I1J^q*ja%=`8A#nnVzGei{qN`4q2fIITDzZEUI6QIfp!~)=Paw_|KO?S~A zeiCh;dmeKD@LW0cY)=x^S9SvZ8_LZ9j4X;+rgRa#x^CxP{OE@|d3)=&SSFza6liMU zOsG7u$k4ebAM9y_W)1Wz&Glx6g(MN%7tFbuU+0^1_Z}T7SIcl*CZu}YnST6geoqMx zom`ff^xfYlf#TKA*GNJn&`U#!(uDmDU$1Xr8&Um_Z4d~tKK8uJb;~6F*Hx3Pz#Ex& zQMf=}pz3~fOObfn=Zz5zFtC#=P07Sn5*kU!O|wP_z@vrHV>DK|Z+!IL7=Q`Oh&Ot6 z1VLfE;}QTgIyZe@L{M;Y-(Kzd^K_-ar^D zXI!{9v}JZmv9;R2mY{?_Zy;j!KpUpz8A_{}1e3U{_vUft*GR*W=gs%;LxG>lH61@# zt92@{7e0!uO_6cW`l5$6 z6hM@ecRhmv0K?wf3QHGmv~oCTlaf4V31>1>Y(+Ch3iYUCrEkmB(k!ZNRK`^5xNXV( zZ~E7b(ImI(+1YP2j3njF9fg;<45oTWwtW14a}?nZGfVe9CPP~$;~MM9t;)|>z*o6l zhAzs*)U(%UU9$5UQy=3(0GNXQ+k>Z<9Z|CGU`*g8lTjernrQIDs=&a{$7m1L9r@1^lf}d;^7F z_Tp#+Ramj1LlJufIUJAE&ItP7hE%9W^HNa%|J;*uJy)%n;i+5QUo=QE-oi9D`p7KE zsBGw@eOs9(;)Pv9r8L5n$o3~G(;d2}E98WxZnCB>fWX579pm|-$1cKqHRGI@DvcZf z*y2bfiUQ%9+5k|5z!ez@4Q0*60NASF%9FeMdJFV#rE!P>*o==)eRM2uKmA6&E&H>O zkdTetn>Hqx;q7(uT-QBw-Y4>%iHj>>1R(gNzwMQpHEZ_+!#5Q31}f|wYdkqa|o zv9bAiTR)RHLSbP8c?S&VY|rMGLt;j$y*f}cgj>K?)xTtw3j9$?wdQ$O&lL3LCOmf- zM%}EMMd*P5w3A8)ap!^EMiPMm^@vK3+&v3M*ftgj_`+atxonOIGs8lgQk5T{HqOZW zd5ED@X88EPOOaOWyu+g&>R;RM(Zlk!XEk@BBpkr)YjPz)-^CCM%(s}wZ+A#NZT2Dg zo#-K%yYuRz5KX(CRbADo28s~U?I*Rh4TR|YM-~+v6~K;gIRsc?QPUWbJQ;*F!DG(* zv3nBGvusT^97z4P7Koenck@JpsI5`HGs%kX z6Wu@nc-|lWrxhGu$@AZO?O!E1At}Uy>%!~D(Md~hO_9I&Eu(7Z#h(B`syDejI)xCj zbdCc6SH$~jN!QT{ASr2vfp?f+lU970cl3M2jZjAg8blvxEr0h;`F@%TA(UL@K(A7? zL#l;%@$vm{dtQD~!;!83HafiE8;ZWfW>f8$=*bZ}8#J2ZK83WQgyNN=x!L6y^v!{^ z9igI60OG_N8Bj!p5iE=Ym=;`zrt-j!@1Ock4HOVuYqhk1z6`_BLZ6*#G@2tiaoR4B5bThk$?v zXpz2G?3a^nbk37cSa{3$2>_*~2+MH2QAE3FZuuBR)NAvJRx$f0=B|_@eLI55ECren zU*Vy<%;sjyF#Nkr8Ai-Goy`@OEJF{(wqUZogEwMK_o#4fehQ5MK(@w)gGq5!-4C+D zN&vcnT+rD+Pht~+eW`3*00VHQ)|<5)Me&?m05=WqTQnA6^M$j}`-iS92+h(ZXF>ot zy}{M#*?=}QAuIYBfZ(ruT}jSO$VM1rComrev;|GV;?+g)x27_ei^+T!~U zky}_Nco1RqmwZDe?f>u6AaI*L&c;U_02Iulr-uhb2Ss2_?XG&sb9f-Ad?aWFuo1wKKR?V z6k@u+D+4Mexy__y(7nVz3iBv*objq+fhVX+E~KM&Q!)ZtU%1E)WSdmG4X4&*>C^!wQbJm=$G2@f^`^d}b%x;t|5CydSVMlVwnN~oH*Ob z8hjvhd}Vt7?b%!>^}FtN69A}vn_|(dIUxoM(t=mi`Va1}V*!0L*=}ux#-C5j-HGB3 z1Kiw{)9_>Q0n@gsGIQ$D9}987NKn(!T`V48D&C8~HAg-AZYmCVV;0&Z@K_)7WnuLv zrXU-;wzZZvvwX z3zoU4eAwIX8l=!!Rw_p}6e++Ddwb!jBlb&s($cZD9}n||pjR?EJU+zHh@4*(MrNRw z*m_0*0+adfd=~Er{;#>45jH)L`fpw@OBmhPJab}bL>aO_Q^H3Qz&wi; z8p4yRoM~bUQ$f7o^I74lt2&N(@OleuVUeE}X8EW=+XoiUxhr|y07&&=teVsO-k? zhrOtDk#|_|>conyB<_cc@2f9tyK=p6uf?nrz5;I+ZYt(CP%Ru{@n;*{J^r%zZ&m8c z@B2(>+&#{SZyyfs{SVG%?&8vmfHx~G9P*JUHE|y1CI8?>NZ#1P^=`nIJ~}@=8uxjn zWkJ7HaQ9hm6UBHey~SU~(T(-pPsht!?c7|{+^I{cUz+4AyRpIsgs4KKDKpsdwzVg- z{_Q7bIoUF3NOi3(-3|FWe;L$m77Jg=L1#%7L%FDz1{3v!EsrGpQ8KztmonN z07;sdTER9d(l8a12JxWf zSKcn_;`O_}Lqu?8O|PQyJ0x`od?-NhGN8_o#&vT((&he^eWmZ=e2w9&H z%s=prk7=c3E#P7BA*G{(*jX3VDV^_9{r$gM1NKdYDXH#yi_xivsHo_T!V6=Mt5qkZ z-KqTs;fK%Gw)pN+PKIP0Kd*ywuWpDg91fDcDC{XIK4I(?WshmK+jNmWuD<_depeaZ z#}9Y2RqB$FD3A+!@jlwZ=}-znBfS1e0IHS5cc^W-dPGNDTKWgIe-lfl$Msp{rX4a}rM}zH*o3=az?kr~c zoEkdS;wuYzeh_C{Zp5IJToc^>NO6R`XQrCzcopC8c(cE{<7Ui-8Rv1|0cVy1s!RIy zQ7-C3*n2Tc%bKQdi77_CtXzgN@VDLJhw4Ku*-cLv^@&FA;|v{if$`6LD>)h?;l?qC za?-XEY=kO@gO>W}c&!$k&6SQvw4sc6K`EV=@7>6u_puBJ`{KHila2@3Fc0JS;8$3~ z)nEw7boQgg(k;7{&8Vk6R?E1@Mlrw$@*5R?&HN4C3W_96u6oP&OUf1Nxm({+$l| zg}WuBZUS%RO)!toGdFu5`xJ7=!o2jD*CWm>fOY;#<;_j&oG1QsGb|0Mi+grYs-v^C zXA`ZAvz85BBh;Xm-%T|;{SyvfM3evGX!?&BJmWP{@U8guF@y_jli%kiW_y5Fha|*N zWQ1)lcZ%)uQ-Q~BU%g0f&+_T}H{F@q)nn1NNvyzX&ueEeEBS^NryjXpoUQARvBD#y z4JCiVT^=B(aed+8u&qTUdJ_c1o z&f++6Y>)evUs1)R^n2c5rqi%} zIuz}M1s1QGQM=2kxb2^JqjmWLqQ{y$^iDpiG#bkF$RMr0eKe+D;FQQA#$o#PHASJ& z&(l>UXiY-)=Pze5dc8!LWdkbssTgIC4YbWYE>0bGDgQhLX;`eVaxRQ2_J3ebd797` z`0hQgOzwyl9YMsl0L5bCzW3+iws7tryU*VKPDYYLb2@u}e%79Z(AA>rrKD{1qyJil z<>jc;iGi8T*RamHw?h*S+YbUe=m*f5dyu)R4y~BnKY7+K@{vGsB0o~@NmE#Z#l#yE zOgQb%hwckkk?5~w34hytIZGap581i_%7`bGlZy2$=lxNB!AT zq;Q=kbA^PenE=d7gT3RsIz?@pCIwfEBH|Hy6k}uN;rqwhXbOm8#`Rw4Q-w?@^S|Ar z>M?AprZ732$aVi8M(z!sA}1cyVL*5|vxDs$BFPLaE5e-%WaEM9!DKrbKMjb4sfn66 zz|J6nt8A2qMR*@GZ1mEP)|Vn|rt3+#t?wtt1~Go1fD*KdaFS8vnwlvRMk_G%cS6qbaPQ2ibh_Pg9e$0j zSNvAhn$P$^_tyu$^plTlVyr*rA<>=<8-1tbq6Gcf#iZqeL4T+F5YnFL+DhZ3c@E+b zw4`ty!?|tiS`ZjB(XI7if-9~6ql^SiKvBUGQ{ga85#@bI` zKw;_{I$KAAHV*rE#-ieky>2lQq6Y_*sgFs`82LX|`cj6^$iL4f17o_T;*eVGf1ySy zyKU!EOxW;7kxOBaGr`n(7B-Lh7L#*R!%Mb(!+qdgGe-erAEh_Y z9Mrz$Omc7^vDEeKk$0#C{Gl&`FL2bKfiVS09o(&WeR6lLNrMM2mASNzZ+)~6iY&4-FJmbPv~ZHLGQ101EiIRumUBEb z>1(B$yG8ryHBO2g)(QAR-yGM70FirKBc#oR^OX@J=_`zR_{85iGUWbu+C=nwj9< z6Z(9TkKrOoe8vG0!o(r5lJ1k<>u5etf24S6y;$5{M=dypQIJi%wB-?kT{|W930mqX zh!ehCsS^~?F=h-QixQVlfI#8oMy=`i;Af void; + isColum?: boolean; + isCenter?: boolean; +} +export interface FiledSelectionViewRef { + value: () => string | undefined; + errorCheck: () => boolean | undefined; +} + +export const FiledSelectionView = React.forwardRef< + FiledSelectionViewRef, + Props +>( + ( + { + name, + lists, + labelList, + label, + onChange, + value: defaultValue, + isColum = false, + isCenter = false, + }: Props, + ref: React.Ref + ) => { + const branding = useBranding(); + + const styles = requireNutritionFactStyle(branding); + const [value, setValue] = useState(defaultValue); + const [error, setError] = useState(); + + useImperativeHandle( + ref, + () => ({ + value: () => { + return value; + }, + errorCheck: () => { + if (value === undefined || value?.length === 0) { + setError('please enter value'); + } else { + setError(undefined); + } + return value?.length === 0; + }, + }), + [value] + ); + + const renderFiled = () => { + return ( + + + {name} + + { + onChange?.(item); + setError(undefined); + setValue(item); + }} + lists={lists ?? []} + label={label} + labelList={labelList} + style={styles.pickerTextInput} + error={error ?? ''} + /> + + ); + }; + + return {renderFiled()}; + } +); + +const requireNutritionFactStyle = ({}: Branding) => + StyleSheet.create({ + formRow: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 10, + }, + formColum: { + flexDirection: 'column', + justifyContent: 'space-between', + marginBottom: 10, + }, + label: { + flex: 1, + }, + labelMargin: { + marginTop: 10, + }, + pickerTextInput: { + flexWrap: 'wrap', + textAlign: 'right', + }, + }); diff --git a/src/components/filed/FiledView.tsx b/src/components/filed/FiledView.tsx new file mode 100644 index 0000000..c182f24 --- /dev/null +++ b/src/components/filed/FiledView.tsx @@ -0,0 +1,138 @@ +import React, { useImperativeHandle, useState } from 'react'; +import { Text, TextInput } from '..'; +import { + Image, + KeyboardTypeOptions, + StyleSheet, + TouchableOpacity, + View, +} from 'react-native'; +import { Branding, useBranding } from '../../contexts'; +import { scaleHeight } from '../../utils'; +import { ICONS } from '../../assets'; + +interface Props { + name: string; + value?: string; + label?: string; + keyboardType?: KeyboardTypeOptions; + isColum?: boolean; + onDelete?: () => void; +} + +export interface FiledViewRef { + value: () => string | undefined; + errorCheck: () => boolean | undefined; +} + +export const FiledView = React.forwardRef( + ( + { + name, + value: defaultValue, + keyboardType = 'decimal-pad', + label = 'value', + isColum = false, + onDelete, + }: Props, + ref: React.Ref + ) => { + const branding = useBranding(); + const [value, setValue] = useState(defaultValue); + const [error, setError] = useState(); + + const styles = requireNutritionFactStyle(branding); + + useImperativeHandle( + ref, + () => ({ + value: () => { + return value; + }, + errorCheck: () => { + if (value === undefined || value?.length === 0) { + setError('please enter value'); + } else { + setError(undefined); + } + return value?.length === 0; + }, + }), + [value] + ); + + const renderFiled = () => { + return ( + + + {name} + + { + setValue(text); + setError(undefined); + }} + value={defaultValue} + containerStyle={styles.containerTextInput} + placeholder={label} + error={error} + enterKeyHint="next" + keyboardType={keyboardType} + /> + {onDelete && ( + + + + )} + + ); + }; + + return {renderFiled()}; + } +); + +const requireNutritionFactStyle = ({ white, gray300 }: Branding) => + StyleSheet.create({ + formRow: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: 10, + }, + formColum: { + flexDirection: 'column', + justifyContent: 'space-between', + marginBottom: 10, + }, + label: { + flex: 1, + }, + labelMargin: { + marginTop: 10, + }, + delete: { + height: 24, + width: 24, + marginHorizontal: 6, + }, + textInput: { + textAlign: 'left', + flex: 1, + fontWeight: '400', + fontSize: 14, + backgroundColor: white, + borderColor: gray300, + paddingVertical: scaleHeight(8), + borderRadius: scaleHeight(4), + }, + containerTextInput: { + flex: 1, + }, + }); diff --git a/src/components/filed/index.ts b/src/components/filed/index.ts new file mode 100644 index 0000000..3f64981 --- /dev/null +++ b/src/components/filed/index.ts @@ -0,0 +1,2 @@ +export * from './FiledSelectionView'; +export * from './FiledView'; diff --git a/src/components/index.ts b/src/components/index.ts index 356ce32..ec6124e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -41,6 +41,8 @@ export * from './weeklyAddhernce'; export * from './progressBard'; export * from './timeStamp'; export * from './mealPlan'; +export * from './tab'; +export * from './filed'; import ActionSheet from './actionSheets/ActionSheet'; export { ActionSheet }; diff --git a/src/components/listPickers/ListPicker.tsx b/src/components/listPickers/ListPicker.tsx index 399ca81..5fad0e1 100644 --- a/src/components/listPickers/ListPicker.tsx +++ b/src/components/listPickers/ListPicker.tsx @@ -18,11 +18,12 @@ interface Props { labelList?: string[]; value: string; title: string; - error: string; + error?: string; style: StyleProp; label?: string; extraWidth?: number; onChange: (size: T) => void; + isCenter?: boolean; } export const ListPicker: React.FC> = ({ @@ -32,6 +33,8 @@ export const ListPicker: React.FC> = ({ extraWidth, value, onChange, + isCenter, + error, }) => { const branding = useBranding(); const styles = menuStyle(branding); @@ -49,6 +52,8 @@ export const ListPicker: React.FC> = ({ }} > > = ({ return ( > = ({ } > - {label ?? value} + + {label ?? value} + + {error && ( + + {error} + + )} ); }; diff --git a/src/components/listPickers/menu.styles.tsx b/src/components/listPickers/menu.styles.tsx index 95d4c7a..6a19ad5 100644 --- a/src/components/listPickers/menu.styles.tsx +++ b/src/components/listPickers/menu.styles.tsx @@ -2,11 +2,11 @@ import { StyleSheet } from 'react-native'; import type { Branding } from '../../contexts'; import { scaleHeight, scaleWidth } from '../../utils'; -const menuStyle = ({ white, border }: Branding) => +const menuStyle = ({ white, gray300, border, error }: Branding) => StyleSheet.create({ main: { backgroundColor: white, - borderColor: border, + borderColor: gray300, flexDirection: 'row', borderRadius: 4, paddingVertical: 8, @@ -17,13 +17,16 @@ const menuStyle = ({ white, border }: Branding) => mainTitle: { flex: 1, marginStart: 12, - textTransform: 'capitalize', }, icon: { marginEnd: 12, height: scaleHeight(20), width: scaleWidth(20), }, + error: { + color: error, + }, + optionContainer: { backgroundColor: white, paddingVertical: scaleHeight(16), @@ -33,7 +36,6 @@ const menuStyle = ({ white, border }: Branding) => }, optionTitle: { marginStart: scaleWidth(12), - textTransform: 'capitalize', }, optionIcon: { height: scaleHeight(20), diff --git a/src/components/logOptions/LogOptions.tsx b/src/components/logOptions/LogOptions.tsx index ea92f92..418ee3f 100644 --- a/src/components/logOptions/LogOptions.tsx +++ b/src/components/logOptions/LogOptions.tsx @@ -13,6 +13,7 @@ interface Props { onTakePicture: () => void; onTakeCamera: () => void; onAiAdvisor: () => void; + onMyFoods: () => void; } type Type = 'All' | 'UseImage'; @@ -45,6 +46,7 @@ export const LogOptions = ({ {type === 'All' ? ( <> + {/* {renderItem(ICONS.logOptionFavorite, 'My Foods', onMyFoods)} */} {renderItem(ICONS.logOptionFavorite, 'Favorites', onFavorite)} {renderItem(ICONS.Mic, 'Voice Logging', onVoiceLogging)} {renderItem(ICONS.AIAdvisor, 'AI Advisor', onAiAdvisor)} diff --git a/src/components/picker/Picker.tsx b/src/components/picker/Picker.tsx index 5a998c9..af7c92e 100644 --- a/src/components/picker/Picker.tsx +++ b/src/components/picker/Picker.tsx @@ -3,13 +3,14 @@ import { View, Modal, Pressable, Platform } from 'react-native'; import { useBranding } from '../../contexts'; import tutorialStyle from './picker.styles'; import { Card } from '../cards'; -import { scaleWidth } from '../../utils'; +import { scaleWidth, screenHeight } from '../../utils'; interface PickerProps extends React.PropsWithChildren { top?: boolean; bottom?: boolean; options: React.JSX.Element; extraWidth?: number; + isCenter?: boolean; } export interface PickerRef { onClose: () => void; @@ -46,7 +47,7 @@ export const Picker = React.forwardRef( (fx: number, fy: number, width: number, height: number) => { const newMeasure: Matrix = { x: fx, - y: fy, + y: props.isCenter ? screenHeight / 2 : fy, width: width, height: height, }; diff --git a/src/components/tab/TabBar.tsx b/src/components/tab/TabBar.tsx new file mode 100644 index 0000000..228b399 --- /dev/null +++ b/src/components/tab/TabBar.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import { StyleSheet, TouchableOpacity, View } from 'react-native'; +import { Text } from '../../components'; +import { Branding, useBranding } from '../../contexts'; +import { scaleHeight } from '../../utils'; + +export interface TabBarProps { + list: string[]; + onTabSelect: (value: string) => void; +} + +export const TabBar = ({ list, onTabSelect }: TabBarProps) => { + const styles = tabStyles(useBranding()); + const [tab, setTab] = useState(list[0]); + + return ( + + + { + setTab(list[0]); + onTabSelect(list[0]); + }} + > + + {list[0]} + + + { + setTab(list[1]); + onTabSelect(list[1]); + }} + > + + {list[1]} + + + + + + + + + ); +}; + +const tabStyles = ({ primaryColor, text }: Branding) => + StyleSheet.create({ + tabContainer: { + flexDirection: 'row', + paddingVertical: scaleHeight(12), + }, + touchableTab: { + flex: 1, + }, + tabTex: { + fontSize: 18, + fontWeight: '400', + textAlign: 'center', + color: text, + }, + tabSelectText: { + fontWeight: '600', + color: primaryColor, + }, + lineContainer: { + flexDirection: 'row', + }, + tabLine: { + height: 2, + borderRadius: 24, + flex: 1, + }, + tabSelectLine: { + backgroundColor: primaryColor, + }, + }); diff --git a/src/components/tab/index.ts b/src/components/tab/index.ts new file mode 100644 index 0000000..832e14a --- /dev/null +++ b/src/components/tab/index.ts @@ -0,0 +1 @@ +export * from './TabBar'; diff --git a/src/constants/colors.ts b/src/constants/colors.ts index a4d66c4..a077578 100644 --- a/src/constants/colors.ts +++ b/src/constants/colors.ts @@ -20,7 +20,7 @@ export const COLORS = { grayscale: '#6E7191', grayscaleAsh: '#262338', grayscaleBody: '#4E4B66', - grayscaleLine: '#D9DBE9', + grayscaleLine: '#D1D5DB', grayscaleInput: '#EFF0F6', grey9: '#D0D3DA', monochrome: '#6E7191', diff --git a/src/navigaitons/BottomTabNavigations.tsx b/src/navigaitons/BottomTabNavigations.tsx index 39db9ec..ce0f537 100644 --- a/src/navigaitons/BottomTabNavigations.tsx +++ b/src/navigaitons/BottomTabNavigations.tsx @@ -23,6 +23,7 @@ export interface TabBarProps extends BottomTabBarProps { onTakePicture: () => void; onTakeCamera: () => void; onAiAdvisor: () => void; + onMyFoods: () => void; } export const renderTabBarIcons = ( diff --git a/src/navigaitons/HomeBottomNavigations.tsx b/src/navigaitons/HomeBottomNavigations.tsx index 64c52fa..bcea557 100644 --- a/src/navigaitons/HomeBottomNavigations.tsx +++ b/src/navigaitons/HomeBottomNavigations.tsx @@ -144,6 +144,12 @@ export const HomeBottomNavigation = React.memo(() => { logToMeal: undefined, }); }} + onMyFoods={() => { + navigation.navigate('MyFoodsScreen', { + logToDate: mealLogDateRef.current, + logToMeal: undefined, + }); + }} onTakePicture={onTakePicture} {...props} items={menu} diff --git a/src/navigaitons/Nutrition-Navigator.tsx b/src/navigaitons/Nutrition-Navigator.tsx index dcfec6f..a9fd3f7 100644 --- a/src/navigaitons/Nutrition-Navigator.tsx +++ b/src/navigaitons/Nutrition-Navigator.tsx @@ -19,6 +19,8 @@ import { TakePictureScreen, AdvisorScreen, ImagePickerScreen, + MyFoodsScreen, + FoodCreatorScreen, } from '../screens'; import { DashboardScreenRoute, @@ -44,6 +46,8 @@ import { TakePictureScreenRoute, AdvisorScreenRoute, ImagePickerScreenRoute, + MyFoodsScreenRoute, + FoodCreatorScreenRoute, } from './Route'; import MyPlanScreen from '../screens/myPlans/MyPlanScreen'; import { HomeBottomNavigation } from './HomeBottomNavigations'; @@ -201,6 +205,16 @@ export const NutritionNavigator = () => { name={ImagePickerScreenRoute} component={ImagePickerScreen} /> + + { floatingRef.current?.onClose(); props.onAiAdvisor(); }} + onMyFoods={() => { + floatingRef.current?.onClose(); + props.onMyFoods(); + }} /> } /> diff --git a/src/navigaitons/params/NutritionNavigatorParam.ts b/src/navigaitons/params/NutritionNavigatorParam.ts index c0f4a90..0229789 100644 --- a/src/navigaitons/params/NutritionNavigatorParam.ts +++ b/src/navigaitons/params/NutritionNavigatorParam.ts @@ -60,6 +60,14 @@ export interface AdvisorScreenProps { logToDate?: Date | undefined; logToMeal?: MealLabel | undefined; } +export interface FoodCreatorNavProps { + logToDate?: Date | undefined; + logToMeal?: MealLabel | undefined; +} +export interface MyFoodsScreenNavProps { + logToDate?: Date | undefined; + logToMeal?: MealLabel | undefined; +} export type ParamList = { MealLogScreen: MealLogScreenProps; @@ -89,4 +97,6 @@ export type ParamList = { TakePictureScreen: TakePictureScreenProps; AdvisorScreen: AdvisorScreenProps; ImagePickerScreen: ImagePickerProps; + FoodCreatorScreen: FoodCreatorNavProps; + MyFoodsScreen: MyFoodsScreenNavProps; }; diff --git a/src/screens/advisor/AdvisorScreen.tsx b/src/screens/advisor/AdvisorScreen.tsx index 996f961..1c80766 100644 --- a/src/screens/advisor/AdvisorScreen.tsx +++ b/src/screens/advisor/AdvisorScreen.tsx @@ -110,6 +110,7 @@ export const AdvisorScreen = () => { keyExtractor={(_item, index) => index.toString()} renderItem={renderItem} showsVerticalScrollIndicator={false} + contentContainerStyle={{ flexGrow: 1 }} style={styles.flatListStyle} // onContentSizeChange={() => listRef.current?.scrollToEnd()} // onLayout={() => listRef.current?.scrollToEnd()} diff --git a/src/screens/advisor/view/BottomBar.tsx b/src/screens/advisor/view/BottomBar.tsx index ec5862d..6daa06f 100644 --- a/src/screens/advisor/view/BottomBar.tsx +++ b/src/screens/advisor/view/BottomBar.tsx @@ -35,44 +35,55 @@ export const BottomBar = ({ return ( - {isOptionShow ? ( - - - - + {inputValue.length === 0 && ( + <> + {isOptionShow ? ( + + + + - - - - - ) : ( - - - + + + + + ) : ( + + + + )} + )} - {renderText()} + + + {renderText()} + {tools && tools?.length > 0 && ( )} - + ); }; @@ -98,7 +105,6 @@ const ResponseViewStyle = () => }, receivedMsgView: { backgroundColor: '#6366F1', - alignSelf: 'flex-start', borderBottomRightRadius: 8, borderBottomLeftRadius: 0, }, diff --git a/src/screens/foodCreator/FoodCreator.styles.ts b/src/screens/foodCreator/FoodCreator.styles.ts new file mode 100644 index 0000000..ba70ec2 --- /dev/null +++ b/src/screens/foodCreator/FoodCreator.styles.ts @@ -0,0 +1,15 @@ +import { StyleSheet } from 'react-native'; +import { getStatusBarHeight } from 'react-native-status-bar-height'; +import type { Branding } from '../../contexts'; + +export const foodCreatorStyle = ({ searchBody }: Branding) => + StyleSheet.create({ + statusBarLayout: { + height: getStatusBarHeight(), + backgroundColor: searchBody, + }, + body: { + backgroundColor: searchBody, + flex: 1, + }, + }); diff --git a/src/screens/foodCreator/FoodCreatorScreen.tsx b/src/screens/foodCreator/FoodCreatorScreen.tsx new file mode 100644 index 0000000..73b287c --- /dev/null +++ b/src/screens/foodCreator/FoodCreatorScreen.tsx @@ -0,0 +1,63 @@ +import { ScrollView, View } from 'react-native'; + +import React from 'react'; +import { foodCreatorStyle } from './FoodCreator.styles'; +import { useFoodCreator } from './useFoodCreator'; +import { BackNavigation, BasicButton } from '../../components'; +import { FoodCreatorFoodDetail } from './views/FoodCreatorFoodDetail'; +import { RequireNutritionFacts } from './views/RequireNutritionFacts'; +import { OtherNutritionFacts } from './views/OtherNutritionFacts'; + +export const FoodCreatorScreen = () => { + const { + branding, + otherNutritionFactsRef, + requireNutritionFactsRef, + foodCreatorFoodDetailRef, + onSavePress, + } = useFoodCreator(); + + const styles = foodCreatorStyle(branding); + + return ( + + + + + + + + + + + + + + + + ); +}; diff --git a/src/screens/foodCreator/data.ts b/src/screens/foodCreator/data.ts new file mode 100644 index 0000000..1316467 --- /dev/null +++ b/src/screens/foodCreator/data.ts @@ -0,0 +1,34 @@ +import type { NutrientType } from '../../models'; + +export const Units = [ + 'Servings', + 'Piece', + 'cup', + 'oz', + 'g', + 'ml', + 'handful', + 'scoop', + 'tbsp', + 'tsp', + 'slice', + 'can', + 'bottle', + 'bar', + 'packet', +]; + +export const OtherNutrients: NutrientType[] = [ + 'satFat', + 'transFat', + 'cholesterol', + 'sodium', + 'fiber', + 'sugars', + 'sugarAdded', + 'vitaminD', + 'calcium', + 'iron', + 'potassium', +]; +export const Weights = ['grams', 'ml']; diff --git a/src/screens/foodCreator/index.tsx b/src/screens/foodCreator/index.tsx new file mode 100644 index 0000000..df80ca5 --- /dev/null +++ b/src/screens/foodCreator/index.tsx @@ -0,0 +1 @@ +export * from './FoodCreatorScreen'; diff --git a/src/screens/foodCreator/useFoodCreator.ts b/src/screens/foodCreator/useFoodCreator.ts new file mode 100644 index 0000000..8f8bf77 --- /dev/null +++ b/src/screens/foodCreator/useFoodCreator.ts @@ -0,0 +1,36 @@ +import { useRef } from 'react'; +import { useBranding } from '../../contexts'; +import type { OtherNutritionFactsRef } from './views/OtherNutritionFacts'; +import type { RequireNutritionFactsRef } from './views/RequireNutritionFacts'; +import type { FoodCreatorFoodDetailRef } from './views/FoodCreatorFoodDetail'; + +export const useFoodCreator = () => { + const branding = useBranding(); + const otherNutritionFactsRef = useRef(null); + const requireNutritionFactsRef = useRef(null); + const foodCreatorFoodDetailRef = useRef(null); + + const onSavePress = () => { + const info = foodCreatorFoodDetailRef.current?.getValue(); + const requireNutritionFact = requireNutritionFactsRef.current?.getValue(); + const otherNutritionFact = otherNutritionFactsRef.current?.getValue(); + + if (info?.isNotValid) { + return; + } + if (requireNutritionFact?.isNotValid) { + return; + } + if (otherNutritionFact?.isNotValid) { + return; + } + }; + + return { + branding, + otherNutritionFactsRef, + requireNutritionFactsRef, + foodCreatorFoodDetailRef, + onSavePress, + }; +}; diff --git a/src/screens/foodCreator/views/FoodCreatorFoodDetail.tsx b/src/screens/foodCreator/views/FoodCreatorFoodDetail.tsx new file mode 100644 index 0000000..8715dbc --- /dev/null +++ b/src/screens/foodCreator/views/FoodCreatorFoodDetail.tsx @@ -0,0 +1,151 @@ +import React, { useImperativeHandle, useMemo, useRef } from 'react'; +import { Card, Text } from '../../../components'; +import { Image, StyleSheet, View } from 'react-native'; +import { Branding, useBranding } from '../../../contexts'; +import { FiledView, FiledViewRef } from '../../../components/filed/FiledView'; +import { + FiledSelectionView, + FiledSelectionViewRef, +} from '../../../components/filed/FiledSelectionView'; +import { Units } from '../data'; +import { ICONS } from '../../../assets'; + +interface Props {} + +export type FoodCreatorFoodDetailType = 'name' | 'brand' | 'barcode'; + +interface Value { + records: Record; + isNotValid?: boolean; +} + +export interface FoodCreatorFoodDetailRef { + getValue: () => Value; +} + +export const FoodCreatorFoodDetail = React.forwardRef< + FoodCreatorFoodDetailRef, + Props +>(({}: Props, ref: React.Ref) => { + const branding = useBranding(); + + const styles = requireNutritionFactStyle(branding); + + const nameRef = useRef(null); + const brandNameRef = useRef(null); + const barcodeRef = useRef(null); + + const refs = useMemo( + () => ({ + name: nameRef, + brand: brandNameRef, + barcode: barcodeRef, + }), + [] + ); + + useImperativeHandle( + ref, + () => ({ + getValue: () => { + let record: Record = {} as Record< + FoodCreatorFoodDetailType, + string + >; + let isNotValid = false; + + (Object.keys(refs) as FoodCreatorFoodDetailType[]).forEach((key) => { + const currentRef = refs[key].current; + const value = currentRef?.value(); + currentRef?.errorCheck(); + if (value === undefined || value.length === 0) { + isNotValid = true; + } + record[key] = value ?? ''; + }); + + return { + records: record, + isNotValid, + }; + }, + }), + [refs] + ); + + return ( + + { + + {'Scan Description'} + + + + + {'Edit Image'} + + + + + + + + + + } + + ); +}); + +const requireNutritionFactStyle = ({}: Branding) => + StyleSheet.create({ + card: { + marginHorizontal: 16, + marginVertical: 16, + padding: 16, + }, + title: { + marginBottom: 16, + }, + container: { + flexDirection: 'row', + alignContent: 'space-around', + justifyContent: 'space-between', + }, + left: { + flex: 1, + alignContent: 'center', + alignItems: 'center', + alignSelf: 'center', + }, + right: { + flex: 1.5, + }, + editImage: { + marginVertical: 4, + fontSize: 10, + }, + icon: { + height: 80, + width: 80, + alignItems: 'center', + alignSelf: 'center', + justifyContent: 'center', + alignContent: 'center', + }, + }); diff --git a/src/screens/foodCreator/views/OtherNutritionFacts.tsx b/src/screens/foodCreator/views/OtherNutritionFacts.tsx new file mode 100644 index 0000000..8328df4 --- /dev/null +++ b/src/screens/foodCreator/views/OtherNutritionFacts.tsx @@ -0,0 +1,130 @@ +import React, { useImperativeHandle, useRef, useState } from 'react'; +import { Card, Text } from '../../../components'; +import { StyleSheet, View } from 'react-native'; +import { Branding, useBranding } from '../../../contexts'; +import { FiledView, FiledViewRef } from '../../../components/filed/FiledView'; +import { FiledSelectionView } from '../../../components/filed/FiledSelectionView'; +import { OtherNutrients } from '../data'; +import { FlatList } from 'react-native'; +import { nutrientName, type NutrientType } from '../../../models'; + +interface Props {} + +interface Value { + records: Record; + isNotValid?: boolean; +} +export interface OtherNutritionFactsRef { + getValue: () => Value; +} + +export const OtherNutritionFacts = React.forwardRef< + OtherNutritionFactsRef, + Props +>(({}: Props, ref: React.Ref) => { + const branding = useBranding(); + + const styles = requireNutritionFactStyle(branding); + const [defaultList, setDefaultList] = + useState(OtherNutrients); + const [list, setList] = useState([]); + const labelList = defaultList.map((i) => { + return nutrientName[i].toString() ?? ''; + }); + + const refs = useRef>>({}); + + useImperativeHandle( + ref, + () => ({ + getValue: () => { + let record: Record = {} as Record< + NutrientType, + string + >; + let isNotValid = false; + list.forEach((item) => { + const sleetedRef = refs.current[item]; + if (sleetedRef && sleetedRef.current) { + if (sleetedRef.current.errorCheck()) { + isNotValid = true; + } + record[item as NutrientType] = sleetedRef.current.value() ?? ''; + } + }); + + return { + records: record, + isNotValid: isNotValid, + }; + }, + }), + [list] + ); + + // Initialize refs for each item in the list + list.forEach((item) => { + if (!refs.current[item]) { + refs.current[item] = React.createRef(); + } + }); + + return ( + + { + + {'Other Nutrition Facts'} + item} + renderItem={({ item }) => { + return ( + { + setList((i) => [...i.filter((o) => item !== o)]); + setDefaultList((i) => [...i, item as NutrientType]); + }} + /> + ); + }} + /> + {defaultList.length > 0 && ( + { + setList((i) => [...i, item]); + setDefaultList((i) => [...i.filter((o) => item !== o)]); + }} + /> + )} + + } + + ); +}); + +const requireNutritionFactStyle = ({}: Branding) => + StyleSheet.create({ + card: { + marginHorizontal: 16, + marginVertical: 16, + padding: 16, + }, + title: { + marginBottom: 16, + }, + container: { + flexDirection: 'row', + alignContent: 'space-around', + justifyContent: 'space-between', + }, + }); diff --git a/src/screens/foodCreator/views/RequireNutritionFacts.tsx b/src/screens/foodCreator/views/RequireNutritionFacts.tsx new file mode 100644 index 0000000..0d5c47c --- /dev/null +++ b/src/screens/foodCreator/views/RequireNutritionFacts.tsx @@ -0,0 +1,124 @@ +import React, { useImperativeHandle, useRef, useState, useMemo } from 'react'; +import { Card, FiledViewRef, Text } from '../../../components'; +import { StyleSheet, View } from 'react-native'; +import { Branding, useBranding } from '../../../contexts'; +import { FiledView } from '../../../components'; +import { + FiledSelectionView, + FiledSelectionViewRef, +} from '../../../components/filed/FiledSelectionView'; +import { Units, Weights } from '../data'; + +interface Props {} + +export type RequireNutritionFactsType = + | 'calories' + | 'ServingSize' + | 'Units' + | 'Weight' + | 'Fat' + | 'Carbs' + | 'Protein'; + +interface Value { + records: Record; + isNotValid?: boolean; +} + +export interface RequireNutritionFactsRef { + getValue: () => Value; +} + +export const RequireNutritionFacts = React.forwardRef< + RequireNutritionFactsRef, + Props +>(({}: Props, ref: React.Ref) => { + const branding = useBranding(); + const styles = requireNutritionFactStyle(branding); + + const [units, setUnits] = useState(''); + + const servingSizeRef = useRef(null); + const caloriesRef = useRef(null); + const fatRef = useRef(null); + const carbsRef = useRef(null); + const proteinRef = useRef(null); + const unitRef = useRef(null); + const weightRef = useRef(null); + + const refs = useMemo( + () => ({ + ServingSize: servingSizeRef, + Units: unitRef, + Weight: weightRef, + calories: caloriesRef, + Fat: fatRef, + Carbs: carbsRef, + Protein: proteinRef, + }), + [] + ); + + useImperativeHandle( + ref, + () => ({ + getValue: () => { + let record: Record = {} as Record< + RequireNutritionFactsType, + string + >; + let isNotValid = false; + + (Object.keys(refs) as RequireNutritionFactsType[]).forEach((key) => { + const currentRef = refs[key].current; + const value = currentRef?.value(); + currentRef?.errorCheck(); + if (value === undefined || value.length === 0) { + isNotValid = true; + } + record[key] = value ?? ''; + }); + + return { + records: record, + isNotValid, + }; + }, + }), + [refs] + ); + + return ( + + + {'Required Nutrition Facts'} + + setUnits(value)} + /> + {units === 'g' || units === 'ml' ? null : ( + + )} + + + + + + + ); +}); + +const requireNutritionFactStyle = ({}: Branding) => + StyleSheet.create({ + card: { + marginHorizontal: 16, + marginVertical: 16, + padding: 16, + }, + title: { + marginBottom: 16, + }, + }); diff --git a/src/screens/index.tsx b/src/screens/index.tsx index a3b32e3..a296d0d 100644 --- a/src/screens/index.tsx +++ b/src/screens/index.tsx @@ -21,3 +21,5 @@ export * from './voiceLogging'; export * from './takePicture'; export * from './advisor'; export * from './imagePicker'; +export * from './foodCreator'; +export * from './myFoods'; diff --git a/src/screens/myFoods/MyFoodsScreen.styles.ts b/src/screens/myFoods/MyFoodsScreen.styles.ts new file mode 100644 index 0000000..299e27b --- /dev/null +++ b/src/screens/myFoods/MyFoodsScreen.styles.ts @@ -0,0 +1,22 @@ +import { StyleSheet } from 'react-native'; +import { getStatusBarHeight } from 'react-native-status-bar-height'; +import type { Branding } from '../../contexts'; + +export const myFoodScreenStyle = ({ searchBody }: Branding) => + StyleSheet.create({ + statusBarLayout: { + height: getStatusBarHeight(), + backgroundColor: searchBody, + }, + body: { + backgroundColor: searchBody, + flex: 1, + }, + container: { + flex: 1, + }, + button: { + marginVertical: 24, + marginHorizontal: 16, + }, + }); diff --git a/src/screens/myFoods/MyFoodsScreen.tsx b/src/screens/myFoods/MyFoodsScreen.tsx new file mode 100644 index 0000000..306a2af --- /dev/null +++ b/src/screens/myFoods/MyFoodsScreen.tsx @@ -0,0 +1,47 @@ +import { View } from 'react-native'; + +import React from 'react'; +import { myFoodScreenStyle } from './MyFoodsScreen.styles'; +import { useMyFoodScreen } from './useMyFoodScreen'; +import { BackNavigation, BasicButton, TabBar } from '../../components'; +import { useNavigation } from '@react-navigation/native'; +import type { StackNavigationProp } from '@react-navigation/stack'; +import type { ParamList } from '../../navigaitons'; +import { useSharedValue } from 'react-native-reanimated'; + +type ScreenNavigationProps = StackNavigationProp; + +const TabList = ['Custom Foods', 'Recipe']; + +export const MyFoodsScreen = () => { + const { branding } = useMyFoodScreen(); + const navigation = useNavigation(); + const tab = useSharedValue(TabList[0]); + + const styles = myFoodScreenStyle(branding); + + const renderTab = () => { + return ( + { + tab.value = value; + }} + /> + ); + }; + + return ( + + + + { + navigation.navigate('FoodCreatorScreen', {}); + }} + /> + + ); +}; diff --git a/src/screens/myFoods/index.tsx b/src/screens/myFoods/index.tsx new file mode 100644 index 0000000..25009ff --- /dev/null +++ b/src/screens/myFoods/index.tsx @@ -0,0 +1 @@ +export * from './MyFoodsScreen'; diff --git a/src/screens/myFoods/useMyFoodScreen.ts b/src/screens/myFoods/useMyFoodScreen.ts new file mode 100644 index 0000000..d4cb24d --- /dev/null +++ b/src/screens/myFoods/useMyFoodScreen.ts @@ -0,0 +1,9 @@ +import { useBranding } from '../../contexts'; + +export const useMyFoodScreen = () => { + const branding = useBranding(); + + return { + branding, + }; +};