From aa6a8f9e7109700e1189b518fe9aad5c5c5d5ec0 Mon Sep 17 00:00:00 2001 From: Marius Robert Macamete Date: Mon, 27 Oct 2025 21:11:31 +0200 Subject: [PATCH] add V2 --- UI_V2/.DS_Store | Bin 0 -> 8196 bytes UI_V2/.env | 3 + UI_V2/Dockerfile | 31 + .../admin/__pycache__/banner.cpython-313.pyc | Bin 0 -> 5124 bytes .../__pycache__/category.cpython-313.pyc | Bin 0 -> 13470 bytes .../admin/__pycache__/clients.cpython-313.pyc | Bin 0 -> 22462 bytes .../__pycache__/dashboard.cpython-313.pyc | Bin 0 -> 7686 bytes .../admin/__pycache__/orders.cpython-313.pyc | Bin 0 -> 17115 bytes .../__pycache__/products.cpython-313.pyc | Bin 0 -> 17265 bytes UI_V2/admin/banner.py | 88 +++ UI_V2/admin/category.py | 242 ++++++++ UI_V2/admin/clients.py | 352 ++++++++++++ UI_V2/admin/dashboard.py | 128 +++++ UI_V2/admin/orders.py | 374 ++++++++++++ UI_V2/admin/products.py | 308 ++++++++++ UI_V2/assets/.DS_Store | Bin 0 -> 6148 bytes UI_V2/assets/favicon.png | Bin 0 -> 83837 bytes UI_V2/assets/icons/loading-animation.png | Bin 0 -> 83837 bytes UI_V2/assets/images/banner.jpg | Bin 0 -> 596065 bytes UI_V2/assets/images/banner.png | Bin 0 -> 596065 bytes UI_V2/assets/images/banner_placeholder.png | Bin 0 -> 8472 bytes UI_V2/assets/images/placeholder.png | Bin 0 -> 29781 bytes UI_V2/assets/images/tainagustului.png | Bin 0 -> 83837 bytes UI_V2/assets/images/tainagustului_white.png | Bin 0 -> 88654 bytes UI_V2/create_super_user.py | 31 + .../__pycache__/categories.cpython-313.pyc | Bin 0 -> 5037 bytes .../__pycache__/company.cpython-313.pyc | Bin 0 -> 5140 bytes .../__pycache__/orders.cpython-313.pyc | Bin 0 -> 8587 bytes .../__pycache__/products.cpython-313.pyc | Bin 0 -> 7087 bytes .../__pycache__/users.cpython-313.pyc | Bin 0 -> 10102 bytes UI_V2/dbActions/categories.py | 94 +++ UI_V2/dbActions/company.py | 97 ++++ UI_V2/dbActions/orders.py | 163 ++++++ UI_V2/dbActions/products.py | 140 +++++ UI_V2/dbActions/users.py | 206 +++++++ UI_V2/docker_compose.yml | 32 ++ .../__pycache__/default_user.cpython-313.pyc | Bin 0 -> 974 bytes .../__pycache__/emails.cpython-313.pyc | Bin 0 -> 6912 bytes UI_V2/helpers/default_user.py | 16 + UI_V2/helpers/emails.py | 139 +++++ UI_V2/instance/app_database.db | Bin 0 -> 36864 bytes UI_V2/main.py | 90 +++ .../auth/__pycache__/auth.cpython-313.pyc | Bin 0 -> 2060 bytes .../forgot_password.cpython-313.pyc | Bin 0 -> 10800 bytes .../auth/__pycache__/login.cpython-313.pyc | Bin 0 -> 4913 bytes .../auth/__pycache__/register.cpython-313.pyc | Bin 0 -> 7429 bytes UI_V2/pages/auth/auth.py | 34 ++ UI_V2/pages/auth/forgot_password.py | 159 ++++++ UI_V2/pages/auth/login.py | 68 +++ UI_V2/pages/auth/register.py | 123 ++++ .../__pycache__/category.cpython-313.pyc | Bin 0 -> 9531 bytes UI_V2/pages/categories/category.py | 156 +++++ .../home/__pycache__/home.cpython-313.pyc | Bin 0 -> 12987 bytes UI_V2/pages/home/home.py | 216 +++++++ .../__pycache__/product.cpython-313.pyc | Bin 0 -> 24787 bytes UI_V2/pages/products/product.py | 464 +++++++++++++++ .../__pycache__/profilepage.cpython-313.pyc | Bin 0 -> 13050 bytes UI_V2/pages/profile/profilepage.py | 208 +++++++ .../__pycache__/cart.cpython-313.pyc | Bin 0 -> 27651 bytes .../__pycache__/peload_card.cpython-313.pyc | Bin 0 -> 3479 bytes UI_V2/pages/shopping_cart/cart.py | 535 ++++++++++++++++++ UI_V2/pages/shopping_cart/peload_card.py | 60 ++ UI_V2/requirements.txt | 1 + 63 files changed, 4558 insertions(+) create mode 100644 UI_V2/.DS_Store create mode 100644 UI_V2/.env create mode 100644 UI_V2/Dockerfile create mode 100644 UI_V2/admin/__pycache__/banner.cpython-313.pyc create mode 100644 UI_V2/admin/__pycache__/category.cpython-313.pyc create mode 100644 UI_V2/admin/__pycache__/clients.cpython-313.pyc create mode 100644 UI_V2/admin/__pycache__/dashboard.cpython-313.pyc create mode 100644 UI_V2/admin/__pycache__/orders.cpython-313.pyc create mode 100644 UI_V2/admin/__pycache__/products.cpython-313.pyc create mode 100644 UI_V2/admin/banner.py create mode 100644 UI_V2/admin/category.py create mode 100644 UI_V2/admin/clients.py create mode 100644 UI_V2/admin/dashboard.py create mode 100644 UI_V2/admin/orders.py create mode 100644 UI_V2/admin/products.py create mode 100644 UI_V2/assets/.DS_Store create mode 100644 UI_V2/assets/favicon.png create mode 100644 UI_V2/assets/icons/loading-animation.png create mode 100644 UI_V2/assets/images/banner.jpg create mode 100644 UI_V2/assets/images/banner.png create mode 100644 UI_V2/assets/images/banner_placeholder.png create mode 100644 UI_V2/assets/images/placeholder.png create mode 100644 UI_V2/assets/images/tainagustului.png create mode 100644 UI_V2/assets/images/tainagustului_white.png create mode 100644 UI_V2/create_super_user.py create mode 100644 UI_V2/dbActions/__pycache__/categories.cpython-313.pyc create mode 100644 UI_V2/dbActions/__pycache__/company.cpython-313.pyc create mode 100644 UI_V2/dbActions/__pycache__/orders.cpython-313.pyc create mode 100644 UI_V2/dbActions/__pycache__/products.cpython-313.pyc create mode 100644 UI_V2/dbActions/__pycache__/users.cpython-313.pyc create mode 100644 UI_V2/dbActions/categories.py create mode 100644 UI_V2/dbActions/company.py create mode 100644 UI_V2/dbActions/orders.py create mode 100644 UI_V2/dbActions/products.py create mode 100644 UI_V2/dbActions/users.py create mode 100644 UI_V2/docker_compose.yml create mode 100644 UI_V2/helpers/__pycache__/default_user.cpython-313.pyc create mode 100644 UI_V2/helpers/__pycache__/emails.cpython-313.pyc create mode 100644 UI_V2/helpers/default_user.py create mode 100644 UI_V2/helpers/emails.py create mode 100644 UI_V2/instance/app_database.db create mode 100644 UI_V2/main.py create mode 100644 UI_V2/pages/auth/__pycache__/auth.cpython-313.pyc create mode 100644 UI_V2/pages/auth/__pycache__/forgot_password.cpython-313.pyc create mode 100644 UI_V2/pages/auth/__pycache__/login.cpython-313.pyc create mode 100644 UI_V2/pages/auth/__pycache__/register.cpython-313.pyc create mode 100644 UI_V2/pages/auth/auth.py create mode 100644 UI_V2/pages/auth/forgot_password.py create mode 100644 UI_V2/pages/auth/login.py create mode 100644 UI_V2/pages/auth/register.py create mode 100644 UI_V2/pages/categories/__pycache__/category.cpython-313.pyc create mode 100644 UI_V2/pages/categories/category.py create mode 100644 UI_V2/pages/home/__pycache__/home.cpython-313.pyc create mode 100644 UI_V2/pages/home/home.py create mode 100644 UI_V2/pages/products/__pycache__/product.cpython-313.pyc create mode 100644 UI_V2/pages/products/product.py create mode 100644 UI_V2/pages/profile/__pycache__/profilepage.cpython-313.pyc create mode 100644 UI_V2/pages/profile/profilepage.py create mode 100644 UI_V2/pages/shopping_cart/__pycache__/cart.cpython-313.pyc create mode 100644 UI_V2/pages/shopping_cart/__pycache__/peload_card.cpython-313.pyc create mode 100644 UI_V2/pages/shopping_cart/cart.py create mode 100644 UI_V2/pages/shopping_cart/peload_card.py create mode 100644 UI_V2/requirements.txt diff --git a/UI_V2/.DS_Store b/UI_V2/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7800e4469d9759f03cbb82b810e898443e2108f5 GIT binary patch literal 8196 zcmeHMJ8v6D5T2nPBZ3_PZH>u;<2W39&bcq6ZSJ?fQyWi~We#GqEPym4C zc6|w89smkdCAt&Tyrsx_S%*r_j9el_vIpR6v8aWy2#2_Kz$#!BunJfOtO8bn|Azwf z%$B9_j_1CfwXIdaD)3$^Anyl{szl#lN29trs8J~Z;V2EOLVd~ul#OT5H`vjrOleJ> zJ)ki~lVYGYr+H7(34MbdjoKVgn*$opXfhO3-pS5WbUK~!f%*?$o6x;QS2Hm|43 zwxTB-r{GlGk4{f-mzEd2TctZ^3*PBc>DJu^@9y&Q*_l(AD}MI*X7x$jn+BA~fp zU{ZfRZ=X}IPk^X)WDbSDcMtWa>zZQ~oDU{0OI`PR72;D$%zzH9cUk$?A{Z3~imXh%@_U zKrQ-J!WiFcBvF0+;2>q$PEUVVdaETOjyFUaWs%^!`^tO9nrr+(kVm%1(oOf+|cBnfzfl}~meI)&W1a#?8wwTL&0U%b*%Iz|76?zvFxs#}=t z_6%b2*Vt2iXNwEYivEV4Df;&fr2seK9#mi-o%a1yWLb-alS zcn6o!$9uSmd$^AQey#0u-7or@S&E>M9H z?R#G4|6jiQ_x}srp>=H)unPR!3gGB!WmUfFcVCDG9m-b-kLqKpvPfP>qcWjJQslTw f#|h8>VMu+?%=rd88fk`#eGyPH*v2aGpDOSg5O+Qc literal 0 HcmV?d00001 diff --git a/UI_V2/.env b/UI_V2/.env new file mode 100644 index 0000000..05a5b65 --- /dev/null +++ b/UI_V2/.env @@ -0,0 +1,3 @@ +SUPERUSER_EMAIL=macamete.robert@gmail.com +SUPERUSER_PASSWORD=Inteligent1_eu +SUPERUSER_ROLE=admin \ No newline at end of file diff --git a/UI_V2/Dockerfile b/UI_V2/Dockerfile new file mode 100644 index 0000000..4d28573 --- /dev/null +++ b/UI_V2/Dockerfile @@ -0,0 +1,31 @@ +# Slim Python image +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +# System deps for pip & curl (for healthcheck) +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +# Ensure persistent folders exist +RUN mkdir -p /app/instance /app/assets + +# Default port +ENV FLET_PORT=8080 + +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -fsS http://127.0.0.1:8080/ || exit 1 + +# Run entrypoint to create superuser and start the app +ENTRYPOINT ["sh", "-c", "python create_super_user.py && python main.py"] \ No newline at end of file diff --git a/UI_V2/admin/__pycache__/banner.cpython-313.pyc b/UI_V2/admin/__pycache__/banner.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c3e1994980a732063913cc8d727032dc0822edd GIT binary patch literal 5124 zcmbUlOKcm*bx1DBU2;WJGDTAqWvyf@7GuY>?8vTZJFeo$j-ps`xMre&Nf&E!MXg2c zGPBFr7JNv7Hc-+4jtv-++!j>OL)4pcN_y(0K!BdaSOJNR4ngWebCY8g{ayNImp{pL z(`Eq8&U^Fb&EuQ*)uXmH4}x*l{m<#&c@g>x0cwM)E*>3$#a+ZB-ZqY~&HUQ2-TXSR zBZT@9Zx18hF>JR^U@B{iQr{6^B5k3pxw?-YeE{%X^rp!TIN7j+x4%gxOj()_AoQ%A zcZAVZ3cGkO?}U{NyLlJT`nS^&2x#3VEn`vyT2jy^g_mc-2(!Egw`Hj)Yq*{l6-C0| zIpK^!<;AR&t_e%HxZ%$Me~)w<5q#30BUp2!>U4>k!fIRa1=ZMi_z6u^uBPqkGC@C% zf1lhhlIl*H)v*qNr3%%>9aIWa9wtGSGX z59gJv{4=;v{wusvlrkI|P8+UiNzP8|hVz=7(Wec^l&nYX2BRthmb5}nHT=I6W&h;y&JHkvTl>b<5EhUdJTlP<~W83`Mmz;X&eA#cJp_8ow> z*b8~AX5muJ@S1cPSzs};hcEGaEbZ6q+|?NA)gU-$?#dnq>RZ{1F0!Z%1xOv zYQ)|+&UiJJ)FiCMuzE$p`mBiMf;KCr#aT(0VwbRbRZ8nxY~<~NoD+F?cetP6s%%W{XqH3U(P_A8j)MU_D4L*_wbZ}G)@ zFIN1q%I;vLBe>!3Sn%HVmi>Jtf8RsK+2fg~*6FS-9|FQN_s&#;;d1auDR{(OJW~oj z^U&#tm_(sX8Zm9RnD?17-BqHy$~0G^x#d8)Z>ZEav`!zb(5$r^D$${JI{eLEZl3yz z?tTQ~KzymYA3(%eOgB9Zi@Q%UJVLQ!@U|GluvcxoOAZ1N8R9R{6DW8aQL$oA+7r!sH%|fH0j}O62qsAQ+%b|6@oV`) zp;Glw#5AL6Mbwa_-iRqk|Lo_ae`bef@7k6hfn|}oiSVE)-16F z*90a%R-S};&XY6)hOA_{If~O&jyR_RUT1i!zFxq&C}TK@qigse9JE4qKRDVBy5=6N^K)pde%HwSOApxgk6v25u{g65Sh>3L=2}O5ojvs+94Uu~z6=jlI=dF$ zxcx?@djQqE%cDHH5t*w^=>w1#)^0xRS zLKke}pAkyceVn(0t+%jAur7P!ol`_#=4h3Q`%x`1DfTAHx<%H`B9y47tOhO#>A!C2 zjvsX%ta!Nki@;YJvbL~kCXrH9wvy%z5>yS7;jlXPykjx5@XQuPw2Raw-JCSR+`1bT1Kl1#8;0PX8dv<*x3 zkb8M=(!D+M@$8c=7~)!J2YUb;g;VSOM9<@AY8GymYkMEx|A8|teGE#tdhtmjX&*}U z*EqY82$Cywlc%;N>Fs*&g`Pv9N9b(zJ7t?fXB@p~$`YGJx<*pBm1u{=xX7cO~;Bdkm6Lo#y6esZUe)!y`b$^?&02 z*!>CnF}srbg$}KxRxYju^*nc>2Yu1wxp3C;#R1=ixZ{hs6V_?sQndm94X`Rk8nAa!s)k3cD!=~b{TA8Q zY)|WXtIn$VRErjvG|v#hTI8-KXG02qg08CyCix25Q#cA?A$jTLS4Lhi zI?iEL(?%eyjWn(~H#%{7G!eC#5kMoaYB{FiyaMfD#$cH-S(&Fi;8%RN+c!u+leB?mJ%UJHFA=Tbx{)EDB4)LpRkK+;qCzJGT7j zsloCeVZW^2tDlf`8}J+SM5=vPAiO&42jQ^iP#4Y!xXhm zM17Hnrv6K}uS{QgMKt_JHIH6qf=$Mf1J8l&oKXuAIigKGn>7Td5_^4-ZaDz z6THns=2I48@yTT@VQHO}Sj(9PhPPBQJiFg4wzT6_iabRXHQuC?0Yyh_M`YkP|Z271rmUJj+4VX4=!v~YXr{_LE16A#PQZGjo7 znD8)a+^MvoUn*`od@B_Q?M?Bm*1nY-;9INYwKvapn_(msXMoZw)m)%K&8nHe4mGQ0 zptVo^P&4~Ya@L2GR7l`1(H->HK@S+(T}9NAI;pRhVdx0mv+ZcB$*nGL|~*$&fT=H0x9&+oSAMm5IE z3qAC2??Knj2-|_}E%<_*(T;D204fy1XwPl0`I6C2ZEu02!7Pg?Uo|WBbYH66tLyda z@l3HoWnE9kR4jpcjVM@XZ(TbhDjICQr=nshj5ex`qU640w9ltv8I?J0NQ2ez> z=xPW`Q6`fLTH2SGjnh&gms;{cHSW!O|CK1o?9h4zxqC-EI)sR&7R zkmNXkOovEpHZ>iZ079iB9U~-pF*1=(b@aVD8;ghd}2rl-i}as zI+o~Ah}=GNMdE_NSOQonC>0!&dGt!VQiS}QdIm;+lgSj9zhhr-*!6e2=GZl^E>lx4 z)^rIqU1H5Kq2}0X%^((6t~WH@Y5SlpQ&J(8><~(JEQZDAXN2ZwK5i0wMueV`mB3js za8U?cTrIhjDK546x`$?V z3#GdsSuNGHN%;oH*xlFcZ`nnzLf|Sy&L?ob#bU9(Q>gD;bQ&gJqwgM7$w8yF^E(Yo@+`e$nA=DhAb9zu&z0iKUeZ8>wX3LG1 zTWza_&FjUbH&5O;`2b$WZyf)u+9y_b2-O|y)wK&JZ=YO@{dD}JsSl?}WcvR*p@jR(!3W{#pZ`d=% zwUo^JgqnTp6;%u6^W_UQ^EH5MLE#ez0=aLv6`=h>&3*-_ZoZBJZMdB!o=0_6RWP@A z@;7>zx<+~WP0e?PKNwbKw0pI%^?|og9zcJRs>|+%j@6+3ms%@{dVZdoL#%PS?t=;B?74R}} z?*gcfwmf6j4wE)&lmUHf8zwl}*f~hAI>x9P1x@R_%KFktA4&}>Hyv4dJxk(#yJSUS zog!!nPp+NFRoH-Nq^)z54i7M?Z$ko-$LYFu@Y=ylQSr@#*AL$6 zyxuc6kSVWTu+Q7)PG%}<7Aoc|#EMR#qVt~rV}7-wZ|>9sSHZidZe6)`X{mVW;?jkc z!k#tPA=t-aW#_LdJ3%_#eDV5=nW`NiNvb?^Lm)=n1){53a8--0M#0s%cz*HBeb?TN zGR9Rzz5!Sa=eWDbca(wARs#k$c8;=6B7mq5Z|Z<}!#7OeG@5<5ob2#%uyZV6?nsu4 zGf|1XI1`D&Dtf>aNL~(3gS`}+0s0owZ_uNltRjgN+8<&td99pQG`F~-) zpSPKLQ_neuIbjO@oME(p0B?qk&8oyvGqBlIw*@vEjzAwHw>_Y`+6okm@YP$W+gRCd z$~$5HlHQC03=kAh#fjx>wFgn51Q|deY4cIteQmZNZdmF#14ro~4MH47!H{#EMk}CV zZ~YoQ_I+WGwlC?iNSm3G8?!2_p(uqgP+iAI+?X|&XQE6kbG}$bTj(g%r9 zh14_W8_;j&OC}-|5y=~l zq|&hjIF?!6D48R#r*KEhYSibG#X1Ibq$*9UZ*$Y40Fau5HIlVx&s#mW3f_5k-Bqws zykjwR=hFL^7XMxqpitGF4x9=(~op8WgqJJI)}carZXe>%0={OoG!(YX`r zV2vM~Ke*64-@BA|uVS_8(A?>Du#8*gTNZZB?^?WkkNdcFwer~9(7MZWZRqWxTh68M zuUz}kdRAcG_~`tH=T|HH0H&^f;qv_Dg*WEkSQ`3y)z7L{>jqa`)enkFP{-dqa{b6+ zn^@E?6t%B=OIFHvEuCLFvr^i#<~@}0mTs_|`@p)l^rrKM^VZOsw}-;CE>$hL?~Sb- z8d~W&y;6E+%{!dgweMcvM?)VD-8;Opv+rG2^fn6K#x-wC##=1sMek0*yK~LE3rcR< zuiHhhPw@KIybYP^nuVeHp?BH$>>IloSJfY$v@^vun~VuZ28=rHnU~TlrCp+Tzu?{f ze;XF4_)`jl2fwiOJ5CfbpB6e!bXh*F_MGUjeA;1!e2&^gfrGzIw1~cn2?2t^KxHZd z5{-&w)3Dzn+j%Vd!Q=!CK_(&5+XUbvCxYnGYS99nfv~0OJJ=YdC^Zn!rMl*M%kysf z=C`hY>ps`A-6~Kmq&2WpKX-aixAl=Ng)oF-j3*jUb)nXhH=}hC0yd9gD+A3pF-ZHv zNY$1nph{`2;^{{QNUfQgGjc~XUGw}|Xk~_k7V=D>!RKXM^BuSZO}8>OAsElh{8h^pA@DFA4oGt-Soo zYX2)4SK(*Q{A;~$^$O0qjLR!~Jff>baJ4M4Vr!4k+9S3e6oK8!OzeMA=zsCP z>!r^$=$(RV=Tev0c35aTEVdmJ+KzoPDh{3#2G5Cu7lgqJD=&|WFJBa1zPRR!XR7Mv z2HrXSnH-F{mj71%Ep{P)K7WmC%{27Pu_9L`a8(#oLgxeXBG({r4QpIerf~<>R|;I^ z8t0?+m>0QvfvaBw|3qXNPdjOGMwTm)rMl^E7cn;cuso*v1_CV@Km|mti#J-$*I$4K`jgCz$WdK zPuEIMt?V3gurY{1< zvG8&OjHR2cf$A}G`{1-#cP84Qr?P90@Z4@n$fi|a_s6= zKsCd=bZw00*Pw!`8O6-Vp6c30%qL7|e#kIR#s|A1!8SoE=O!7y_p1N7NPH$jFmxy% zFZuAm$cM)oDe%RJF9bdvp0>0K&NNz&%Pw-PQI{w1SxtG?CpQji>f9NvMlSj#c2bMvgcakidBzq_xizcSw z#4)xD6g-I`97d)e;>Ls!Y#kP$7?kqHqqLP|8&48AJ(coiz~I31SGQacB%v@I75NM0 z2(KLuF4Lh{B0|VvY|rB18F?17&b~n4`4@tLlP8`VC5N!YftA_wF?Mj^{Zuzzs$g~7pLiR*`MhlBg~kOgdQfpDh8o(K%S z=(kcwS+Y`|Bkw^inMV+o7T})u`CfS?>+rT>O|cOl{1|FdZ9u3$zR5f~WnwC7H&2+F zypGM|n17djGH7Dl`PWXreR{pTa=~`n1`aPAsX*p|ui;MK2YEj+-{qD$v9(ud?fqo0 z;5)u9moJ6y9axSFzC&<&Q&S74Hx-pnJi4at58Uhiw!7ua<#%hAYcf4Ye!Y7X!pijx zPdKK&@s6&#Zn+MsKTiK_=dX94U+?I=du92`-ES^`6OL-?8#bH{?A=vSxlvSBQu<^M zQ&%t6?iFhHinT|D+M|#5RaO*k9AL^S5q}7Mi{(3o@|}460^x5w7FvAuCk=P~%l^CV z%k8Tb-I?;rOl{ro`b_n1&!Yh(!XrwM4W`NN_~T;+XhQ?@KRoU_UdnFU5V!|7{}B?@ ztMW+;xCQVnKTu}IwE=^~z=_k{?}AWw7S2FB=9o)HE|IZwW}s6v(~Jhy=?0}z3K$EI zoWc5`=D23$Xx0xki@pPsp=J?;)z4s_iV>Wf5^V89KGzn{J%RrP)Tag^0TZaK>ej&Z zt9Xc+X>3`tz2B=hVjG$lFTcM#*X(@?ml{`uuF6Z0$-QG|yb1v%{FVnG4c0+VJu0F7 zEO`!E$`&If&1v{lNP`kI$3EN%1-bU&)|&|bj!jVS0dZ=5Z~_a4yEbRBY>!a3=RVi@ z1t$DDH2$-i@aX2MylYICfO--LgJ&OrG~iT?g6CD9YQvbK6Yzli1d$N=i^H*(J%jpYR_5mBI)VHQPn5 zPT=Z9u36xk7t>-(kI>Sy#`R`u8|bOAvSie-7t1?^ay(*R=iGDCYh3kHdSV!Z#oM$+UHdZHI)mLmv-_y~9H9FugZ2 zDU3|6OvQf>=l!NW^CS7S$uPV6&8R-GAB>@33`EaD;}#llNKU=My5`gy@G4yQ1am@%11`*#$0+WBSFd%g47e0})&~F#I5uG9 zY#K%~;qnH!j=J$mx1b20M(2R&5W{{ja3#OAHv>14&*Qmn3#9gLa}(otfN7nTU$KWc z70}Vw;U(EF%XcPiec{k-G$gw|e+xCE0P!Pu6LgUvPu&Qb&!^r2`2Z_>F+t}b>jWGJ zkKqNmN4Ed@(*xuP)=`(i(l;-S&~J<8VDKEO}J1 zOTYgMp>XIonf3gFYj3>$M(!P}2XM9O#O)J{r|t#*&HSnRU)|!-fB^s1ymIMk>ZgH^ z&V6|9qYEEi_~n3jIwZh<^*FtA1*Ul84zb~Y&~QL(7!Vo;9yx7r3Clw-VYLaxZ5su4 zxKdTjRMm);?LuX{SlK01c7Yl9sKQ<1*{EiU%OBNwy^amWo4o_!cgsOEDos#P%Ryjj zUsByn?Wf&X(TT|}OsLeM<|c|YSu~a51w1^4gbR3%U~n3455i5FpyUb$U!4ubvo-m6 zL`hQVICyP|WH2Z@a_k7+BYhtFl1lIhRldd|a%N{}H;Q1X#qLL!v}gQ+x}Pfi3w=`@Lr&!!_OzzTh2+0{m)f}rlB2?~YO ztC0MF*)X3nSsk|m3op#S@Pxtq#yPXgx^Iz)Ezb%q&pu(`<4H9O<*o|rfyI8YsaI&~ zeZs)U;{zsZ>tkEKwd!$$)!Ovf(`zQ@}>A>Vq0E4TEjE=eNRuPLZ+nG?h#}C)4&D#{L_|{d+cUF*zPHm{OAbKO-M(^#A|> literal 0 HcmV?d00001 diff --git a/UI_V2/admin/__pycache__/clients.cpython-313.pyc b/UI_V2/admin/__pycache__/clients.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d35eea2e01ab80cdb802937f2c7ee02a2c2f158 GIT binary patch literal 22462 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 literal 0 HcmV?d00001 diff --git a/UI_V2/admin/__pycache__/dashboard.cpython-313.pyc b/UI_V2/admin/__pycache__/dashboard.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7938067c0010fd0f768cc14eb802b1f245037a1c GIT binary patch literal 7686 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 ITx&x92Yb9hr~m)} literal 0 HcmV?d00001 diff --git a/UI_V2/admin/__pycache__/orders.cpython-313.pyc b/UI_V2/admin/__pycache__/orders.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97d5eab3e304883e36f9beca06df440363b481c0 GIT binary patch literal 17115 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 T@-@}*U*?p>;J8O&Ot|#F`HE9L literal 0 HcmV?d00001 diff --git a/UI_V2/admin/__pycache__/products.cpython-313.pyc b/UI_V2/admin/__pycache__/products.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45f9317f5a8537479cfbb3515921237004b07d79 GIT binary patch literal 17265 zcmc&*du$xXdEa|KcqEVS_wo49dIeq)pSNvD-gdZb)S>8wN03{i8oxP>-OA3l#l* zv$wZ*r=?}Pe{?0zemnEcH#6VNe6N{%SXk)f@chrwKc0SmBgg$aeW;H)4Lp1Xz}p

`wd4pi$jVH~1*8A)V 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 vnZ>=r7eK)VP^366GZ_rO 1: + self.second_address.value = item['address'].split("~")[1] + self.second_address_placeholder.controls.append(self.second_address) + self.page.open(self.add_user_dialog) + + def on_delete_btn_click(self, id): + self.delete_item_id = id + self.page.open(self.confirm_delete_alert) + + def on_delete_client_click(self, e): + self.user_manager.delete(self.delete_item_id) + self.delete_item_id = None + self.page.close(self.confirm_delete_alert) + self.all_clients = self.user_manager.get_all() + self.list_of_clients.controls = self.create_list(self.all_clients, self.on_edit_btn_click, self.on_delete_btn_click, self.on_view_btn_click) + self.list_of_clients.update() + + def on_delete_cancel_btn_click(self, e): + self.delete_item_id = None + self.page.close(self.confirm_delete_alert) + + def on_view_btn_click(self, item): + self.view_name.value = f"Nume: {item['name']}" + self.view_email.value = f"E-mail: {item['email']}" + self.view_phone.value = f"Telefon: {item['phone']}" + self.view_address.value = f"Adresa: {item['address'].split("~")[0]}" + company = self.company_manager.get_company(item['id']) + if company: + self.view_company_placeholder.controls.append(ft.Text("")) + self.view_company_placeholder.controls.append(ft.Text("Date Companie:", weight=ft.FontWeight.BOLD)) + self.view_company_name.value = f"Denumire: {company['name']}" + self.view_company_placeholder.controls.append(self.view_company_name) + self.view_company_vat.value = f"CUI: {company['vat']}" + self.view_company_placeholder.controls.append(self.view_company_vat) + self.view_company_rn.value = f"Registru comertului: {company['register_number']}" + self.view_company_placeholder.controls.append(self.view_company_rn) + self.view_company_address.value = f"Sediu: {company['address']}" + self.view_company_placeholder.controls.append(self.view_company_address) + if len(item['address'].split("~")) > 1: + self.view_second_address_placeholder.controls.append(ft.Text("")) + self.view_second_address_placeholder.controls.append(ft.Text("Adresa Livrare:", weight=ft.FontWeight.BOLD)) + self.view_second_address.value = f"Adresa: {item['address'].split("~")[1]}" + self.view_second_address_placeholder.controls.append(self.view_second_address) + self.page.open(self.view_dialog) + + def create_list(self, items, on_click_handler, on_click_handler2, on_click_handler3): + """Helper to create list items for a column.""" + return [ + ft.Container( + content=ft.Row( + [ + + ft.Column( + [ + ft.Text(value=item['name'], weight=ft.FontWeight.BOLD), + ft.Text(value=item['email'], size=12) + ] + ), + + ft.Row( + [ + ft.IconButton( + icon=ft.Icons.PREVIEW, + on_click=lambda e, id=item: on_click_handler3(id), + ), + ft.IconButton( + icon=ft.Icons.EDIT, + on_click=lambda e, id=item: on_click_handler(id), + ), + ft.IconButton( + icon=ft.Icons.DELETE, + on_click=lambda e, id=item['id']: on_click_handler2(id), + icon_color=ft.Colors.RED, + ), + ] + ) + + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + ), + width=300, + bgcolor=ft.Colors.BROWN_50, + border = ft.border.all(1, ft.Colors.GREY), + padding=10, + border_radius=8, + ) + for item in items + ] + + def on_save_btn_click(self, e): + user_name = self.user_name.value + user_email = self.email.value + user_phone = self.phone.value + user_address = self.address.value + company = {} + + company['name'] = self.company_name.value + company['vat'] = self.vat.value + company['register_number'] = self.register_number.value + company['address'] = self.company_address.value + delivery_address = self.second_address.value + address = f'{user_address} ~ {delivery_address}' + if self.edit_id: + if len(delivery_address) == 0: + self.user_manager.update_user_data(user_name, user_phone, user_address, self.edit_id) + else: + self.user_manager.update_user_data(user_name, user_phone, address, self.edit_id) + exising_company = self.company_manager.get_company(self.edit_id) + if exising_company: + company['user_id'] = self.edit_id + company['id'] = exising_company['id'] + self.company_manager.update_company(company) + else: + + user_id = self.user_manager.invite_user(user_email, user_name, user_phone, user_address) + print(user_id) + if user_id: + company['user_id'] = user_id + if self.is_company: + self.company_manager.add_company(company) + if self.is_second_address: + self.user_manager.update_user_data(user_name, user_phone, address, user_id) + self.clear_fileds() + self.page.close(self.add_user_dialog) + self.all_clients = self.user_manager.get_all() + self.list_of_clients.controls = self.create_list(self.all_clients, self.on_edit_btn_click, self.on_delete_btn_click, self.on_view_btn_click) + self.list_of_clients.update() + + def on_cancel_btn_click(self, e): + self.page.close(self.add_user_dialog) + self.clear_fileds() + + def clear_fileds(self): + self.user_name.value = '' + self.user_name.update() + self.email.value = '' + self.email.update() + self.phone.value = '' + self.phone.update() + self.address.value = '' + self.address.update() + try: + self.company_name.value = '' + self.company_name.update() + self.vat.value = '' + self.vat.update() + self.register_number.value = '' + self.register_number.update() + self.company_address.value = '' + self.company_address.update() + except Exception as e: + print(f'No company, Error: {e}') + try: + self.second_address.value = '' + self.second_address.update() + except Exception as e: + print(f'No second address, Error: {e}') + self.company_placeholder.controls.clear() + self.company_placeholder.update() + self.second_address_placeholder.controls.clear() + self.second_address_placeholder.update() + + def on_is_comopany_btn_click(self, e): + self.is_company = True + self.company_placeholder.controls.append(ft.Text("Date firma")) + self.company_placeholder.controls.append(self.company_name) + self.company_placeholder.controls.append(self.vat) + self.company_placeholder.controls.append(self.register_number) + self.company_placeholder.controls.append(self.company_address) + self.company_placeholder.update() + + def on_second_address(self, e): + self.is_second_address = True + self.second_address_placeholder.controls.append(ft.Text("Adresa de livrare")) + self.second_address_placeholder.controls.append( + self.second_address + ) + self.second_address_placeholder.update() + + def on_add_btn_click(self, e): + self.page.open(self.add_user_dialog) + + def build(self): + return ft.Container( + content=ft.Column( + [ + ft.Row( + [ + ft.Text("Clienti", + weight=ft.FontWeight.BOLD, + size=18 + ), + ft.Button( + icon=ft.Icons.ADD, + text = "Invita", + on_click= self.on_add_btn_click + ) + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + self.list_of_clients + ] + ) + ) \ No newline at end of file diff --git a/UI_V2/admin/dashboard.py b/UI_V2/admin/dashboard.py new file mode 100644 index 0000000..deb3450 --- /dev/null +++ b/UI_V2/admin/dashboard.py @@ -0,0 +1,128 @@ +import flet as ft +from admin.category import Category +from admin.products import ProductsPage +from admin.banner import Banner +from admin.orders import OrdersPage +from admin.clients import Clients + +class Dashboard: + def __init__(self, page: ft.Page): + self.page = page + self.category = Category(self.page) + self.placeholder = ft.Container( + content=self.category.build(), + padding=10, + expand=True + ) + self.rail = ft.NavigationRail( + selected_index=0, + label_type=ft.NavigationRailLabelType.ALL, + min_width=100, + #min_extended_width=400, + leading=ft.Image('images/tainagustului.png', width=80), + group_alignment=-0.9, + destinations=[ + ft.NavigationRailDestination( + icon=ft.Icons.CATEGORY_OUTLINED, + selected_icon=ft.Icons.CATEGORY_ROUNDED, + label="Categori", + ), + ft.NavigationRailDestination( + icon=ft.Icons.ADD_BOX_OUTLINED, + selected_icon=ft.Icons.ADD_BOX_ROUNDED, + label="Produse", + ), + ft.NavigationRailDestination( + icon=ft.Icon(ft.Icons.MANAGE_ACCOUNTS_OUTLINED), + selected_icon=ft.Icon(ft.Icons.MANAGE_ACCOUNTS_ROUNDED), + label="Clienti", + ), + ft.NavigationRailDestination( + icon=ft.Icons.SHOPPING_CART_OUTLINED, + selected_icon=ft.Icon(ft.Icons.SHOPPING_CART_ROUNDED), + label_content=ft.Text("Comenzi"), + ), + ft.NavigationRailDestination( + icon=ft.Icons.SHOPIFY_OUTLINED, + selected_icon=ft.Icon(ft.Icons.SHOPIFY_ROUNDED), + label_content=ft.Text("Magazin"), + ), + ft.NavigationRailDestination( + icon=ft.Icons.IMAGE_OUTLINED, + selected_icon=ft.Icon(ft.Icons.IMAGE_ROUNDED), + label_content=ft.Text("Banner"), + ), + ft.NavigationRailDestination( + icon=ft.Icons.LOGOUT_OUTLINED, + selected_icon=ft.Icon(ft.Icons.LOGOUT_ROUNDED), + label_content=ft.Text("Logout"), + ), + ], + on_change=lambda e: self.navigate_to(e), + ) + + def navigate_to(self, e): + index = e.control.selected_index + match index: + case 0: + self.category = Category(self.page) + self.placeholder.content = self.category.build() + self.placeholder.update() + case 1: + self.products = ProductsPage(self.page) + self.placeholder.content = self.products.build() + self.placeholder.update() + case 2: + self.clients = Clients(self.page) + self.placeholder.content = self.clients.build() + self.placeholder.update() + case 3: + self.orders = OrdersPage(self.page) + self.placeholder.content = self.orders.build() + self.placeholder.update() + case 4: + self.page.launch_url('http://0.0.0.0:5555/') + case 5: + self.banner = Banner(self.page) + self.placeholder.content = self.banner.build() + self.placeholder.update() + case 6: + self.page.client_storage.clear() + self.page.session.clear() + self.page.go('/') + + def build(self): + if self.page.session.get("user") is None or self.page.session.get("user")['role'] != 'admin': + return ft.Container( + content=ft.Column( + [ + ft.Text( + "Nu aveti drepturi sa accesati pagina de admin!", + size=20, + weight=ft.FontWeight.BOLD, + text_align=ft.TextAlign.CENTER + ), + ft.Text("Va rugam sa va autentificati cu un rol de admin",text_align=ft.TextAlign.CENTER) + ], + alignment=ft.MainAxisAlignment.CENTER, + horizontal_alignment=ft.CrossAxisAlignment.CENTER + ) + ) + else: + return ft.Container( + content=ft.Column( + [ + ft.Row( + [ + self.rail, + ft.VerticalDivider(width=1), + self.placeholder + ], + expand=True + ), + #self.shop_page + ], + expand=True + ), + expand=True + ) \ No newline at end of file diff --git a/UI_V2/admin/orders.py b/UI_V2/admin/orders.py new file mode 100644 index 0000000..b91498e --- /dev/null +++ b/UI_V2/admin/orders.py @@ -0,0 +1,374 @@ +import flet as ft +from dbActions.orders import Orders +from dbActions.users import Users +from dbActions.products import Products +from helpers.default_user import DefaultUser + +class OrdersPage: + def __init__(self, page: ft.Page): + self.page = page + self.orders = Orders() + self.users = Users() + self.products = Products() + self.selected_user = None + self.selected_order = None + self.selected_order_products = None + self.original_status = None + self.customer_email = None + self.default_user = DefaultUser(self.page) + self.header = ft.Row( + [ + ft.Text( + "Comenzi", + weight=ft.FontWeight.BOLD, + size=18 + ), + ], + alignment=ft.MainAxisAlignment.START + ) + self.all_orders = self.orders.get_orders() + self.all_orders = self.all_orders[::-1] + self.oll_orders_list = ft.ListView( + controls=self.create_list(self.all_orders, self.on_order_click), + spacing=10, + expand=3, + height=700 + ) + self.fileters = ft.RadioGroup( + content=ft.Row( + [ + ft.Radio(value="on_hold", label="In asteptare"), + ft.Radio(value="new", label="Noua"), + ft.Radio(value="in_progress", label="In lucru"), + ft.Radio(value="completed", label="Complete"), + ft.Radio(value="all", label="Toate") + ] + ), + on_change=self.on_filter_change + ) + self.name = ft.Text() + self.email = ft.Text() + self.phone = ft.Text() + self.address = ft.Text() + self.total_pay = ft.Text(weight=ft.FontWeight.BOLD) + self.products_column = ft.Column([]) + self.status = ft.Text() + self.buttons_state = ft.RadioGroup( + content=ft.Row( + [ + ft.Radio(value="on_hold", label="In asteptare"), + ft.Radio(value="new", label="Noua"), + ft.Radio(value="in_progress", label="In lucru"), + ft.Radio(value="completed", label="Complete"), + ], + ), + on_change=self.on_radio_value_change + ) + self.total_row = ft.Row( + [ + ft.Text( + value="Total platit", + weight=ft.FontWeight.BOLD, + ), + self.total_pay + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ) + + self.order_details = ft.Column( + [ + ft.Text( + value="Detalii utilizator", + weight=ft.FontWeight.BOLD, + size=18 + ), + ft.Row( + [ + ft.Text( + value="Nume si prenume", + weight=ft.FontWeight.BOLD, + ), + self.name + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + ft.Row( + [ + ft.Text( + value="E-mail", + weight=ft.FontWeight.BOLD, + ), + self.email + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + ft.Row( + [ + ft.Text( + value="Telefon", + weight=ft.FontWeight.BOLD, + ), + self.phone + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + ft.Row( + [ + ft.Text( + value="Adresa", + weight=ft.FontWeight.BOLD, + ), + self.address + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + ft.Row( + [ + ft.Text( + value="Status", + weight=ft.FontWeight.BOLD, + ), + self.status, + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + self.total_row, + ft.Divider(), + ft.Text( + value="Produse", + weight=ft.FontWeight.BOLD, + size=18 + ), + self.products_column, + ft.Divider(), + self.buttons_state + ], + expand=True + ) + + self.order_details_placeholder = ft.Container( + expand=7, + padding=10, + border_radius=10, + ) + + self.message_field = ft.TextField( + label="Scrie un mesaj clientului", + min_lines=3, + max_lines=5, + multiline=True + ) + + self.change_state_dialog = ft.AlertDialog( + title=ft.Text("Modifica statusul"), + content=self.message_field, + actions=[ + ft.FilledButton( + "Da", + on_click=self.on_change_state_btn_click + ), + ft.FilledButton( + "Nu", + on_click=self.on_cancel_state_btn_click, + bgcolor=ft.Colors.GREY + ) + ] + ) + + def on_radio_value_change(self, e): + self.page.open(self.change_state_dialog) + + def on_change_state_btn_click(self, e): + self.page.close(self.change_state_dialog) + status = self.buttons_state.value + self.orders.update_order_status(status, self.selected_order['id']) + print(status) + self.all_orders = self.orders.get_orders() + self.all_orders = self.all_orders[::-1] + self.oll_orders_list.controls.clear() + self.oll_orders_list.controls = self.create_list(self.all_orders, self.on_order_click) + self.oll_orders_list.update() + self.order_details_placeholder.content = None + self.order_details_placeholder.update() + #-------------------------------------------> TBI Send information email to customer + message = self.message_field.value + self.message_field.value = '' + self.message_field.update() + print(message) + email = self.customer_email + + def on_cancel_state_btn_click(self, e): + self.buttons_state.value = self.original_status + self.buttons_state.update() + self.page.close(self.change_state_dialog) + + def get_total_pay(self): + sume = 0 + print (self.selected_order_products) + if self.selected_order_products: + for product in self.selected_order_products: + sume += (product['price'] - product['price']*product['discount']/100) + print(sume) + return sume + + def on_order_click(self, item): + self.products_column.controls.clear() + self.order_details_placeholder.content = None + self.order_details_placeholder.content = self.order_details + self.order_details_placeholder.bgcolor=ft.Colors.BROWN_50 + try: + self.order_details_placeholder.update() + except: + print('ERROR Unable to update the Order Details Placeholder') + print("order item", item) + self.selected_user = self.users.get(item['user_id']) + if self.selected_user == None: + self.selected_user = self.default_user.default_user + self.selected_order = item + products = self.orders.get_order_products(item['id']) + self.selected_order_products = [] + if products: + for product in products: + prod = self.products.get(product['prdouct_id']) + print(prod) + if prod: + self.selected_order_products.append(prod) + + self.name.value = self.selected_user['name'] if '@default.com' not in self.selected_user['email'] else 'Anonim user' + self.name.update() + self.email.value = self.selected_user['email'] + self.email.update() + self.customer_email = self.selected_user['email'] + self.address.value = self.selected_user['address'] if '@default.com' not in self.selected_user['email'] else 'Anonim user' + self.address.update() + self.phone.value = self.selected_user['phone'] if '@default.com' not in self.selected_user['email'] else 'Anonim user' + self.phone.update() + self.status.value = "Status" + self.status.update() + for product in self.selected_order_products: + name_label = ft.Text( + "Denumire produs", + weight=ft.FontWeight.BOLD + ) + name = ft.Text(product['name']) + name_row = ft.Row( + [ + name_label, + name + ] + ) + image = ft.Image( + src=product['image'], + width=200, + height=200, + fit=ft.ImageFit.CONTAIN + ) + quantity_label = ft.Text("Cantitate") + quantity = ft.Text(product['quantity']) + quantity_row = ft.Row( + [ + quantity_label, + quantity + ] + ) + product_row = ft.Row( + [ + image, + ft.Column( + [ + name_row, + quantity_row + ] + ) + ] + ) + self.products_column.controls.append(product_row) + print(self.products_column.controls) + self.total_pay.value = f"{self.get_total_pay()} Lei" + self.total_pay.update() + self.products_column.update() + + self.buttons_state.value = item['status'] + self.buttons_state.update() + self.original_status = item['status'] + + def on_filter_change(self, e): + print(e.data) + buffer = [] + if e.data == 'all': + self.oll_orders_list.controls = self.create_list(self.all_orders, self.on_order_click) + self.oll_orders_list.update() + else: + for order in self.all_orders: + if order['status'] == e.data: + buffer.append(order) + self.oll_orders_list.controls = self.create_list(buffer, self.on_order_click) + self.oll_orders_list.update() + + def get_status(self, status): + STATUS = { + 'on_hold': "In asteptare", + 'new': "Noua", + 'in_progress': "In lucru", + "completed": "Completa" + } + return STATUS[status] + + def create_list(self, items, on_click_handler): + return [ + ft.Container( + content=ft.Row( + [ + + ft.Row( + [ + ft.Icon(ft.Icons.ARROW_RIGHT, size=20), + ft.Text(value=item['id']) + ] + ), + ft.Row( + [ + ft.Text( + value=self.get_status(item['status']) + ) + ] + ) + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + ), + width=300, + bgcolor=ft.Colors.BROWN_50, + padding=10, + border_radius=8, + ink=True, + on_click=lambda e, i=item: on_click_handler(i), + border = ft.border.all(1, ft.Colors.GREY), + ) + for item in items + ] + + def build(self): + return ft.Container( + content=ft.Column( + [ + self.header, + ft.Row( + [ + ft.Text(value="Filtreaza dupa"), + self.fileters + ] + ), + ft.Row( + [ + self.oll_orders_list, + self.order_details_placeholder + ], + vertical_alignment=ft.CrossAxisAlignment.START + ) + ], + alignment=ft.MainAxisAlignment.START, + expand=True, + #scroll=ft.ScrollMode.ADAPTIVE + ), + expand=True, + ) \ No newline at end of file diff --git a/UI_V2/admin/products.py b/UI_V2/admin/products.py new file mode 100644 index 0000000..957d0f8 --- /dev/null +++ b/UI_V2/admin/products.py @@ -0,0 +1,308 @@ +import flet as ft +from dbActions.categories import Categories +from dbActions.products import Products +import os +import shutil + +class ProductsPage: + def __init__(self, page: ft.Page): + self.page = page + self.category_manager = Categories() + self.product_manager = Products() + + self.name = ft.TextField(label="Denumire") + self.description = ft.TextField(label="Descriere", multiline=True, min_lines=3, max_lines=5) + self.details = ft.TextField(label="Detalii", multiline=True, min_lines=3, max_lines=5) + self.price = ft.TextField(label="Pret") + self.discount = ft.TextField(label="Reducere (%)") + self.quantity = ft.TextField(label="Cantitate") + self.product_image = ft.Image( + width=150, + height=150, + src='images/placeholder.png', + ) + + self.aviability = ft.Dropdown( + label='Disonibilitate', + options=[ + ft.dropdown.Option(key='in_stock', text="In stoc"), + ft.dropdown.Option(key='in_provider_stock', text="In stoc la furnizor"), + ft.dropdown.Option(key='not_available', text="Indisponibil"), + ], + expand=True + ) + + self.category = ft.Dropdown( + label="Categorie", + options=self.get_categories(), + expand=True + ) + + + self.add_product_dialog = ft.AlertDialog( + title=ft.Text("Produs"), + content=ft.Column( + [ + ft.Row( + [ + self.product_image, + ft.Button("Incarca", icon=ft.Icons.UPLOAD, on_click=self.open_file_picker) + ] + ), + self.name, + self.category, + self.description, + self.details, + self.price, + self.discount, + self.quantity, + self.aviability, + ], + scroll=ft.ScrollMode.ADAPTIVE, + width=400 + ), + actions=[ + ft.Button( + "Salveaza", + on_click=self.on_save_btn_click, + icon=ft.Icons.SAVE, + ), + ft.TextButton( + "Anuleaza", + on_click=self.on_cancel_btn_click, + icon=ft.Icons.CANCEL, + ), + ] + ) + self.foto = None + self.file_dialog = ft.FilePicker( + on_result=self.on_file_picker_result, + on_upload=self.on_upload_progress + ) + self.page.overlay.append(self.file_dialog) + self.page.update() # Required to register the FilePicker control + + self.uploaded_files = [] + + self._all_products = self.product_manager.get_all() + self.products_list = ft.ListView( + controls=self.create_list(self._all_products, self.edit_product, self.delete_product), + spacing=10, + expand=True + ) + + self.edit_item_id = None + self.delete_item_id = None + + self.confirm_delete_alert = ft.AlertDialog( + title=ft.Text("Confirmati?"), + actions=[ + ft.Button( + "Da", + on_click=self.on_delete_product_click, + icon=ft.Icons.DELETE, + ), + ft.TextButton( + "Nu", + on_click=self.on_delete_cancel_btn_click, + icon=ft.Icons.CANCEL, + ), + ] + ) + + def on_file_picker_result(self, e: ft.FilePickerResultEvent): + if e.files: + file = e.files[0] + file_name = file.name + upload_url = self.page.get_upload_url(file_name, 600) + + print(f"Uploading {file_name} to {upload_url}") + + upload_task = ft.FilePickerUploadFile( + name=file.name, + upload_url=upload_url + ) + self.file_dialog.upload([upload_task]) + + def open_file_picker(self, e=None): + self.file_dialog.pick_files( + allow_multiple=False, + allowed_extensions=["png", "jpg", "jpeg"] + ) + + def on_upload_progress(self, e: ft.FilePickerUploadEvent): + if e.progress == 1: + print(f"Upload complete: {e.file_name}") + + # Resolve paths relative to the UI folder (two levels up from this file) + ui_root = os.path.dirname(os.path.dirname(__file__)) + uploads_path = os.path.join(ui_root, "uploads") + assets_path = os.path.join(ui_root, "assets", "images") + os.makedirs(assets_path, exist_ok=True) + + source_file = os.path.join(uploads_path, e.file_name) + destination_file = os.path.join(assets_path, e.file_name) + + if not os.path.exists(source_file): + print(f"❌ File not found: {source_file}") + return + + try: + shutil.move(source_file, destination_file) + print(f"✅ File moved: {source_file} → {destination_file}") + self.product_image.src = f'images/{e.file_name}' + self.product_image.update() + self.page.update() + self.foto = e.file_name + except Exception as ex: + print(f"❌ Error moving file: {ex}") + + def on_add_btn_click(self, e): + self.page.open(self.add_product_dialog) + + def on_save_btn_click(self, e): + product = { + 'name': self.name.value, + 'description': self.description.value, + 'details': self.details.value, + 'price' : self.price.value, + 'discount' : self.discount.value, + 'quantity' : self.quantity.value, + 'image' : self.product_image.src, + 'aviability' : self.aviability.value, + 'category_id' : self.category.value + } + print(product) + if self.edit_item_id == None: + self.product_manager.add(product) + else: + self.product_manager.update(product, self.edit_item_id) + self.edit_item_id = None + + self.page.close(self.add_product_dialog) + self.set_popup_to_default() + + print('Update list') + self._all_products = self.product_manager.get_all() + self.products_list.controls=self.create_list(self._all_products, self.edit_product, self.delete_product) + self.products_list.update() + + def on_cancel_btn_click(self, e): + self.page.close(self.add_product_dialog) + self.set_popup_to_default() + if self.edit_item_id != None: + self.edit_item_id = None + + def set_popup_to_default(self): + self.name.value = '' + self.name.update() + self.description.value = '' + self.description.update() + self.details.value = '' + self.details.update() + self.price.value = '' + self.price.update() + self.discount.value = '' + self.discount.update() + self.quantity.value = '' + self.quantity.update() + self.product_image.src = 'images/placeholder.png' + self.product_image.update() + self.aviability.value = None + self.aviability.update() + self.category.value = None + self.category.update() + + + def get_categories(self): + categories = self.category_manager.get_categories() + return [ + ft.dropdown.Option(key=cat['id'], text=cat['name']) + for cat in categories + ] + + def create_list(self, items, on_click_handler, on_click_handler2): + """Helper to create list items for a column.""" + return [ + ft.Container( + content=ft.Row( + [ + ft.Column( + [ + ft.Text(value=item['name'], weight=ft.FontWeight.BOLD, size=15), + ft.Text(value=f"Pret: {item['price']}", size=12) + ] + ), + ft.Row( + [ + ft.IconButton( + icon=ft.Icons.EDIT, + on_click=lambda e, id=item: on_click_handler(id), + ), + ft.IconButton( + icon = ft.Icons.DELETE, + on_click=lambda e, id=item['id']: on_click_handler2(id), + icon_color=ft.Colors.RED + ) + ] + ) + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN, + ), + width=300, + bgcolor=ft.Colors.BROWN_50, + padding=10, + border_radius=8, + border = ft.border.all(1, ft.Colors.GREY), + ) + for item in items + ] + + def edit_product(self, item): + self.edit_item_id = item['id'] + self.name.value = item['name'] + self.description.value = item['description'] + self.details.value = item['details'] + self.price.value = item['price'] + self.discount.value = item['discount'] + self.quantity.value = item['quantity'] + self.product_image.src = item['image'] + self.aviability.value = item['aviability'] + self.category.value = item['category_id'] + self.page.open(self.add_product_dialog) + + def delete_product(self, id): + self.delete_item_id = id + self.page.open(self.confirm_delete_alert) + + def on_delete_product_click(self, e): + self.product_manager.delete(self.delete_item_id ) + self.delete_item_id = None + self.page.close(self.confirm_delete_alert) + print('Update list') + self._all_products = self.product_manager.get_all() + self.products_list.controls=self.create_list(self._all_products, self.edit_product, self.delete_product) + self.products_list.update() + + def on_delete_cancel_btn_click(self, e): + self.delete_item_id = None + self.page.close(self.confirm_delete_alert) + + def build(self): + return ft.Container( + content=ft.Column( + [ + ft.Row( + [ + ft.Text("Produse", size=18, weight=ft.FontWeight.BOLD), + ft.Button("Adauga", icon=ft.Icons.ADD, on_click=self.on_add_btn_click), + ], + alignment=ft.MainAxisAlignment.SPACE_BETWEEN + ), + self.products_list + ], + alignment=ft.MainAxisAlignment.START, + expand=True + ), + expand=True, + ) \ No newline at end of file diff --git a/UI_V2/assets/.DS_Store b/UI_V2/assets/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0abe2e4408f04463ba4552ba231c6d741b12705c GIT binary patch literal 6148 zcmeHKPfrs;6n_JSwp3(+7V%%Qp%)VxEJWzRgG<2};sL@EETLF;+ihLh&eYv)D-x34 z_3j7o1DJU8rXN6$UOo5)ym;0(vs0l(Jqj`Vl9}I|dGF1e_uK5uE&z~xvoa5$06+{I z$H)kFzY%Wd?UW42nPEgC)+hr53UkoFK9rDwkb(2g0DpHAPz3{8V8hAxx3=c6vgt4r zJ^1mE$2Rvv2xn&DU&u4Y`ANX2Y>PKb!qb1_mQT z!_jCwnuzY{k6Byy4ZrE-4R@E1wit8Fu5Osyw$&~v$rVO@!={!i0_=)~F2x5nt*~~U zHEG2Yv3g*L$7Au5a{2J^RyLPW7qW{-8TD{JyRei|mvXtIqj)TpzIl7IwC~g@d%?ND z^A8U|A)^wz@Di0C0_<{27DYaGi~6D{vaDEqBynN%;+Qf%F*%+bPo+{*m!>DKT)kFO zCQYNfUvv3(SD3@xywkKw%9w{muaz0yaIF>=Tkx%CzHUE9(?nmurd4@(JhgSuK%eCC zgjn>RYgu%+=Gv9@YSpv+!U=tcXDhqRaWT9&hFeBf=dV`z1ZOio# zPsJ4W9K*Dnb(&|s&+0pd>l(CX5~JQtAz(Y|~o$&bNTccv`y=XbPmeTEqEnOmE0pkL0dmiL>5Fm_KA7 zWZ>K}z|RL28%IrICqQv@U^jq6ZjW%}f;#_L;HXN}Bz6Ks3knHB5ra@7e=$f9j`OPY z(Z^GyN#3oeisN^!itQ0LF5IsZpa{WEUdKY=SMfA9A!t*%f~ZOC1c);z;fH{r MA?!j1{wV`L0RTgNHS;6l%!IQk`9i8 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