From 3ac9a74fa122c259c62334f7acde90368f074845 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 13 Mar 2026 19:25:49 +0000 Subject: [PATCH] version backend --- core/__pycache__/admin.cpython-311.pyc | Bin 212 -> 758 bytes .../context_processors.cpython-311.pyc | Bin 763 -> 763 bytes core/__pycache__/models.cpython-311.pyc | Bin 209 -> 4384 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 1038 bytes core/__pycache__/views.cpython-311.pyc | Bin 1364 -> 5068 bytes core/admin.py | 7 +- core/migrations/0001_initial.py | 67 +++++++ .../__pycache__/0001_initial.cpython-311.pyc | Bin 0 -> 4035 bytes core/models.py | 46 ++++- core/templates/core/buyer_dashboard.html | 38 ++++ core/templates/core/dashboard_base.html | 159 +++++++++++++++++ core/templates/core/index.html | 167 +++++++----------- core/templates/core/login.html | 67 +++++++ core/templates/core/seller_dashboard.html | 48 +++++ core/urls.py | 8 +- core/views.py | 72 +++++++- 16 files changed, 573 insertions(+), 106 deletions(-) create mode 100644 core/migrations/0001_initial.py create mode 100644 core/migrations/__pycache__/0001_initial.cpython-311.pyc create mode 100644 core/templates/core/buyer_dashboard.html create mode 100644 core/templates/core/dashboard_base.html create mode 100644 core/templates/core/login.html create mode 100644 core/templates/core/seller_dashboard.html diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 5e8987a0cd478c226e501b5189550e758662c560..8f6f021692f808b9b304d41b6a63223236dbf6fc 100644 GIT binary patch literal 758 zcmbVJy-OoO6o2FH>?RTKkaQslwkdSMTCTYhFC3^KHrpjTb8&SiBX4&(gROsn-L+PV zTq#!m6T7ltnCjrFbO&~Fg){L2tCi&O=6(F&_rB(8X{iQUPVa8Qf7<^s$xN*O!Q(ms z_z4Uci9isa5MT?;iWY?BTf+8jL47J5-x03w65!;pGAnUWokeBOtxH|;mwqquE{;71 zht9!q=HR$*In3UJ4xO0wIz-VP6X9@T_U%x$xF2)eMfp99xT<{aC43-pq+AWLG)NTP zLB>&4r>>iDvE8Yu5A3iv9LTjm4igOfX#PeyLNXr3NOgjaF|IM@`e+WX>^Mwx59EU| zPB>n}qekIr)jN=aducx%CaK3yc#tL%y&n>f;^SVxy+9)OrbDkCkDe+Ux=$MMPq?hv zW{db%o4*zzWDG0WYaPRv?0xzh=vnOn>i1AT|CYl_F)cJMpY1yE(KAXlFCUk9zWFSazW% delta 157 zcmeyydWA7%IWI340}#~ttjM$n(vLwL7+``jJ_`XE(-~42QW$d>av7r-85vTTf*CZK zUxE~9GTvfMOv%m6^V4Ly#g~$mn3tZfmz4 RBO~Jt289dQP!StY1puVrA!7gl diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 75bf2234fb21a6b62efc5cec11af9512dd0c9616..2f0013b4a7cb0e3edfe0bb0a46ed6dcd125cccc4 100644 GIT binary patch delta 20 acmey(`kR$|IWI340}#~ttk}r?fe8RVqXqW> delta 20 acmey(`kR$|IWI340}xbw%iqZTfe8RYW(H&c diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index a251b5fb7c2b603c587f8162d643eb10596d2858..aa045157ef9627e8278734c9f3f18d70a03340c9 100644 GIT binary patch literal 4384 zcmbtXO>7&-72f6FF1brmKaT64N@8T$Fr`*%1xFR!R(2#=iep7_Alfe2qFaiyl2#^n zmD#0Z328x~IS7RhIvB8lq<~Nas#Nn}pgsiXr6_XnaS7~!SoGGDZW5$ZPJM5dq)3W( zQDjJdoO$zhc4pr9-n{v1G#cTcnE&{@HY;%4f3Q(2!D{8`-%xqR5svUVF3+cV9_zlG zFYizLdCtepa>V}yM*=Fp8mK+I6?%(Sh-nF+CB8*VU|M0&io8Wj zWLgqv$#2mLljs7MjP2tzle`T+WqD1_HOUFnU78h|PT+3Z*ENMA)k9Wg{uD~i)OPzt1K-8s7A!qHMf$>sFpBu?% z9xFLjU$$1<;9@SLKTPs83Z2>dETMsQH1E*`fM?w2+$#KgWwoxi!0qw#Fl3+r6L3XD zJ(I^d1WYxzWTIl?yWx=)Bd?AW77Myn7*QXq*@9)zk!G+TH~O)zrVM}?P0bOC#&?CO_ZOz#pT0#quq96#Mo!J>Ou-FnrUGlq!8*fJ zD;erZ<+T+(r?b# z-4o^R38#CaI9G}FZZ0{o-tCkf8!X2Lo!DS;_8`Cq`wzI&;LQvt)#MDgM_7)21@I8c^(pYrP(jx|EvW4| ziS}vc1xsGs2AMtO{Qy(W6Y4S!iIqEMX~@4&Wb96 zS;Ms!@xv2=9q4^g*GNa0>7}|3#xqRTFLIfg~!Yz@p zgXy5zJe^2MYI#Q&)99{i_)Bt&#u*X?0=`3HK)|>wK-?sStmTSpUd7z=Q4HX&Ow}Ba zldyo2O8Px%KriHSbQBc!Ujxu9SVIc!<0-IL4X;F7)-2PF5jCsjGdX1~2XW4%s6ufU zAwtR-h=Pi4e5OFC_&;e*lQ>XYj>oI(sZqQKHHQ$8m-ET9?TGD0W*`J>%laL4orOYr z5tQAK7w{N`U2)-7N}0bmH|L7ks?VPI15Kd5L{u|NwKYpKbXT7C*6uk;aE)qN)s=7S z;O=TQdIn9)$UbzV(=c92%X^{Njby3H_B~^@^{Nqg6-B^sYW@qL!KISCa^i{Yb1(0|QeI!Pf4gGGwQ^i@;#%>8y5}{yV^{yy z6{oAe+%@2I4HWNGhaBe4-kEUqV#e%GfFNR0~XDy0}}+Iw1*Jyi3v%b;iW0pL+` z{dwRk!H!>t?{@0zv~zjfYuMuTsvAbzxgq@}z6@8x{Ew!vhG!0VqLr}A zt>?{clp!SGXZ{JG!87C@~91~S79`izW6n!sf(CjnU4XIL zlf1LOwi?lgLD$6d2Z-Kv_UVu9_+9vJPk%k<47}$x{(0ux3*Yv5bz8h!rF{I&lxM`n zBZvM5fkpUVV{IBCjWCHYh0u=q53mE_D*Vhp0<>Yi%Fcw@M&`%*Fjel`7G93Mny_P& z<=CVXn=H;&;{AA7^q1oUPJ954hxU;l-=g4pDx5(%BW{Asamnn~$D+eu(nplg?NK zbx*_Q(v#dF%8ySv&xLph9H(J(=E>Y4%8ySvI|Lqgy*+M)p2|4sPk5YlJ3Y2_{{<^Q Bw66dF literal 209 zcmZ3^%ge<81a&?uGA)7hV-N=hn4pZ$LO{lJh7^Vr#vF!R#wbQch7_h?22JLdAO)I? zx7c#?Q&Mw^{WO_wai(M?=B4NBr6d(G10`27do?nz*T#%TYs-K)+l&TLgMz5gq7l#dyU7C|>SHuC-&IrWCvOwYkGb1D84F;JD K*iaE0Pz3-{*EE9w diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index f705988a223abc7b5af333902925bc196186bbf5..c0a7488945cd53b84b8f1f01fd04fd1c89dc0b9e 100644 GIT binary patch literal 1038 zcmah{y-(Xf6h9{yeX`x@%a5f-;CI zBu90ko@pmpgic|PBt>+1XLoM5PoUQ+?2(Yd)VvwU!~f)|XT^JY52ogos77X| zB6CzF^HY&BHAv+?(p9YjdH4=h7cM2ysv_{f4tv#%Ft+?|-+SMuyCM&&AB9rClqafU zQEEr~l(mQ*^xD47h?t+KEk;S7(M~9E&v&Vq4XESXiABs9_wsC)4pm2XGKZy(�Li zwO;5tK&BW{ci`9|ZOKTHOTnQAAK+s`fZ@@-ur;u|RLC;m9p)!YOkqs);p{{;Q|>0y zfW&a_uY1JPWbzuft23fNc5JWf*COTwOy0Lx8hbjv7qWi4W=CPK_7TPsFbp`$$)McG zA*(Z8zT{!kqC&HSmPADXYQmgD%DmtUx`qIB^URdvGNX3ng^@}3XeSDNX72jzLol#A z)J(@C$H+tRmN#xMi>@g(ETM6KIMps!yQ7+Ijzq>BbD?V-gLfia*>q$smyu5n!GAXa}=Nr7-h|7(n-1zZ^<7SMT32u&}mUw0D=xtJ2 zJ73_H^|-R0RMx-kal9Gh%>-|bf|mKC>Un|VdW`D{uBT3JJUMR3(zp@hMuHn-?=pW{ V<1e7ixWZLYD`gG}@?ncGia&6BBRK#7 literal 347 zcmZ3^%ge<81a&?uGP8j6V-N=hSfPy1HbBO7h7^Vr#vF!R#wbQch7_h0<{YM6<|t-H z1}277#uS!R<`mX7Y|EG#7*+!@1f(-Yv81pEGiY+W1PN#|-eM_8EXjDu2<2qt=ce9b zC<567lFLiXP4&}cy~Ul9m6(^FuUA@>Q(Ocx>=tWTW@>pcSm7;hpfJ#slGLKS;*|`a zfzk}Wbo4<+>6a#z=9QG{r&gpUmzLxg>6hmhWfvDDCa3Br=NAE`f#&HIRQ}?y$<0qG z%}KQ@;sa_1Ii}bPNPJ*sWMsU-Aael~Jzx;OfQmk_F|cwrxO510gv?;Oz#@N*W8V3NU&sfd? diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2f0989c7137f7930a63ea76fcd63516c4974e834..795ffaf35703926ab0b83a7285810ba73e290836 100644 GIT binary patch literal 5068 zcmb_fU2NOd6(;pdiu#uJ)K`VnQ2D78#i?DXN!L z91Aw)hYnbaJh(uBWs-J9tx}j@-Pg2TOUb-fCU0V3k-b-_M)QgOP_Ww^)Gi= zhxLj&y!V`Y5AV-8-#PbB9*>KF@{hm&h5r{r5dXxDPO-Er-+lp=PY9M^NrA|=-ei`9 zwnear)~t=hJgZ%2FborA1fPB{Eq?bZ6bB%r1CDZ`LdNvOcrz5d2~w z8-Ti#rG%gu%7(;nHY|>0M}S9vKx8AV3uu&cu*?UsDTezJSK!6Gu)uQy zYXmOeDutk z1_Fseo3E=1CmPN-k;GOCN&)xi6gUpT3jc2xfcputLDV{uf%o8s)cQ4a`wjNlBsWNw ze9t^yWMM5OlC^GH6nnQjv5LQImZ6bW*0y0$=#G>aIt~TTxm{Kro!X#|4eJJ}_&PFz zSfx63*3LQ}!>pck43GE^{hY)7Sn3hAX;VTSO$6)~OK;d7!#bXH*8+#{s-C)Oo=ZYm z;Sxf=#PX$LLM|YVD<#%=WhKFt?(#?~iCjrZu$+?T1vyi7!#S_<)#`etAc+PwS1!Me zTL!%#;_?iZa|~B&~wIbNTqJuU8o>1G9`!Y z%b-^zxveGC(aCbKGQO;UsTzR}6D+yNJ!R*ffNX9(=}${fCnkGzM)nipgQz)me~A z3U^PDv6&@s`QiA?iX?I~m1S_U$_#gpD^wH-&8$i2J-M7Oa5Luo?(*E4oGGsxHryLc z!f+4#NzFf{`=_?9))T3n+q;RgYU1qn)b`Zp z2ew{sxPtf7A7<`nw&t4-B6hSMJ<^CzYVj#OKGldH(c`mP{FEL)RUbW2A34}KbokM_ znmYfvMLRUFADVBBAA0mlHTlZ3i`w{W`uJda%m%UA?XlYR3$SF%qQO*T4#BbB;ROz0 z#krD~^NNDdG-x(xUe3v!Aeazj!#!oJD1nk#>dJM+Xa=+4FOLJ+A{tC^`xg(#H72Pu zNtIdHxumDh>FEX4wt!O5V_-FSCFW}GKxKF}y_r9TPe19d1Nr)~2G%?vT_OggmSJhi zx@h=YE6cHYd1YD3Bi8UOSJpYyW2CI+x=;q@g>Z;Vk?0t_`Zpoi9S)%1f^BpP`W(gm zP6F8?zIFz-lMm9G^N{X5)FeoIy5Wneqf?r1TK7$>)O3Rhso`T9GodpRs%>Kb4q@@LzjF17-GVv90Mtq;LFJdA|Bb9g$mo3%bYy%DT+aL;Sbo~&16ZG*ia1+f3d zf>EMXHc6#V`~Nk|ZiMLQ#_z}Qf6`@aT383`+#okB5DRRZ)_t+Sx<3{yqFERZ5!!AV zkKyGZ7R$LDxD*b?RpvOvg}lPCxxDfm?`_q05nEZ}QpfDji&%lp@bxT7;S~r0u6#k^ z?{YaMzifCQAHXmz;ixYgZbee^Lc3LsnHV=@ylpfdvS8$ej~PyB8OIC2U2uB^(g;6f zBX>BZumVO5;LNcGWqv8Qyl&VFIDu(Fv(2=BgkcAr#bg4AjEx6_u?Y(Z8@)*|gvg%* z`3WxYy@Q~=_dR>m*e*3zJM+hLDmA82Cv@t>mvn5Gj;Zk}jh@!&=`ZQPN2A-vH9Dcw ziCVCBxEB1*ZlRAgt%N=Jy^9FP9=y62O74b|JH@Ah8cJ%RvwG<4UTAJNG^d5;_0as* zbwKU0Q=&c2zP0qpHijTd=^+Zix>)9^dvM-QJ%f%7uK#nbqwgg8=I&*EgEOdMzuZ?? z|KP3wJO&2f(Z&YZ&<0uSONDz%2ElE^%7%N}P$tqTH3*2*@2tLON2;6d9V*Xx^<%>MPke6*; zj&hJ07C0GHxTT6xt|+-X___pbUmH(z?R>LIf_{8MXu~^t4NERyVy4^<6W0x2dlv24%a9}CDdxEDZPoC! zIC4c4^JpC{VHIW&z!Shhpc4jgSTtkf1*pjPAaQ{(z>I7c9|#)rg3i2vGofiS6G~~m zDcv`vQd3_?#ve#~k@RjP{ppp@uB(x>7Mar{b6YnW(a8oy|90v_&wbDD$%o0`xgNPR z>d0Tmz6h%CysOUM)@E<(v$va8%c$cU0?2omsJ1XfmXU*c_}E@Jy&FzHd;4$isNu90 zzNUw-?S*gchHo{((TA>&Z#=lsh?rp}GOb6Z8zb?DXLfF>BPX?yllsWXrjr(5po2OU+oKY@RN~X56B?b= z>Ew=OC$M9|F)NK@R-ox6#xnKD!Fp_RFP7PjWwh7{J$9l#daPE`M_*J&UwIa&M~~Lt z)uX4>=u4lz4?(N;Ydv;Gjh%Zoi^G?X!r==Dgs)&be4Tj~)tEV*nNw|ZX81}SKri4E zIReCRv~Gn6hp^V2>|l!nS5pY}{BkB=QC2eGl@iLsbxKA!#2fy0Cw4g_N-QVHrfs!M zuV;za=(=6y6$vSDC6mqk*)%r8-Bwdk;9lIi96H+iFbbMhj7&qH#7%s)FjMMQW`XVM z&xE-qGvD}uItPO*0(Tz$3PfOMgggthX(dUrPS9KCuTJ>3%->hU33c$P6ECZSPn~#O z{b^5~IIjNKQzs6qgU?q#)~!3X>F|?hnnagOkmMB-9Hd7n!s2gQtYorD0QtrW%?}Q? h7;aM(Am?BNrd^@Qnlz;+>((Yg|&qtiZg{Vm_d{6B}j#zCgUyc z^wg60vecsD%=|ncm#ru@FD11I$l@r;%uTJz&r99h&zQ`pWfugNP$oyP%_}Qn0Saic-eNCGEi6qfE-4ZO@{4#ugxKUX79UnwAd_?Qc9wP0np{N^ zAaQXZaf>Y_D={xUzla$m!Z%q!NX!mo(k&i{xL#>VW=?St$QeZ-6IL=5$${81K;jpN wO>TZlX-=wLkus3W2*kx{43nn_={hK1U_e1P7!)s{q7Q5WjOHI0FbS~v0Ob^D761SM diff --git a/core/admin.py b/core/admin.py index 8c38f3f..bfae149 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,3 +1,8 @@ from django.contrib import admin +from .models import UserProfile, Category, Product, Order, OrderItem -# Register your models here. +admin.site.register(UserProfile) +admin.site.register(Category) +admin.site.register(Product) +admin.site.register(Order) +admin.site.register(OrderItem) \ No newline at end of file diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..6221d50 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,67 @@ +# Generated by Django 5.2.7 on 2026-03-13 07:23 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('slug', models.SlugField(unique=True)), + ], + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('pending', 'Pending'), ('shipped', 'Shipped'), ('delivered', 'Delivered')], default='pending', max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('buyer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('description', models.TextField()), + ('price', models.DecimalField(decimal_places=2, max_digits=10)), + ('stock', models.IntegerField(default=0)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='products', to='core.category')), + ('seller', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='OrderItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.IntegerField()), + ('price', models.DecimalField(decimal_places=2, max_digits=10)), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='core.order')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.product')), + ], + ), + migrations.CreateModel( + name='UserProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_seller', models.BooleanField(default=False)), + ('phone_number', models.CharField(blank=True, max_length=20)), + ('address', models.TextField(blank=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb0650e6cc04a96bd8ac126c00b105297b4df89c GIT binary patch literal 4035 zcmbVPOKcn06`dLWDM}_4*)l1Llp5PHt-qA$(*~^-QF7eq=Ss?lKr$HdJyIhNXXu%s zWVt}>MHgLW7cGM<)E41FK(i3gF0$yt%SYfXgaHZ!=t?^p)+(#sH^b4tmFyws@_p{R z@7#0m`|vN#&0!9n%0K=t|2x8Q|6vvP@ihvs7oqSihd9J5T#kodK@m!xoQG#^9>rVo z<$NW7&d+lK_kcs*KXJ&1ed__&(2j@Wet_3`ask9IaT)&stIlV5D`;TTl+~h<5v*`Y zF6xpgYpP-SOB%w8k#CH%e_9)r*L#5OTMpZJasuLW9)ZK&br&;-{U_$r^x4m zwr}iXjfbAVyx5b?m@zu7o*wA%b=bEd7 zqw6uZyl&?UtF61S;MU)9d(n-PyjR=L&R+Ch;`Plp@cL{d;ul0BVSCc%;@g434{n76 zHahJ7>uwWDqRwjSbX-6yZEM{)((0;qpX2N*y#DGmuZX{t+4iG3-$l2ox6ftcozoUC zwRaq;cQ1)l@RCTq_Xd%=dz!zuj6&wEeZ7Zz&gUZPeQSGt)&8?NQuJNI*T5U_HE1Kd zc@%r^1^G5)>m2VxJgoNNv#8LBt#y?Du-e~0m$@IHGRtxWHe(553?Ds6_F~YCgybveaLDm>vBobx5USI%L*A-mnB7h zhONj3)>kzHi>g$@mY2x`1dhO|5-v$kMFp!xbItM^N~H*G{)#F;so;#Dv%Sso7i6p; z!}2ZY2srvPmsIH61`WsBj_7%aeJg)~)! ziLePC>sVowI35e81_*TxRr2P+OJ=tTVI!~0Wp;#GKASKrQ0Pw0~!vMcNWazvVurZ(Mu!5q^ zq$pZpQG^ReMZr*xh$3KBSUy25Agi(o8K<**fYoMfO69V&3W^A`G{I_qOJ~;`>)PbX z^2g%J()1t1xrOZXtQ9%Zkb&!_DLb9#*tJGyHx(=LkzAaFM4w@aXoaTMB)w5wf{b;F z%~=S+vRuMWH9Q0C%SH7QSP6c#E~!P$640s@v=f9yJuo%7G&Px>wnEGJsp*W0WSy`# zY7bRdakw#8aA|s3oL`v*xF2bnf+e*v@q&t%H4u%7wK8s8&Hl!{2K3x7zZ+Q7N_e2M zT2al)0Dg+|;EO)6sp*f6GNji4i{ZfWm2O~gaBxU$u-RYU(%BW!7C|T&6Tt6_qtDMR ztuG$b{aonopTjix-d}_C?)_SDk_IR19-+f=BcxL+sjc0b3NPMCepP=yx68-hbjy4h>t;I)ae3V4T z>H%+COWhX;gf+6VeQ*{^m3Ra=4*+0nwTfig%e%75=o5L65}*6PNEb4$MFu**}wfM?d;#p z{xtKihcvrPvtQC|fmEJ@d(#f7sTJloNbnmZ^ZQd^69Ur90@Ay?@#7X9dqBq)>DcF_ z_=IeH4Vs($f*p!QI~0p-aB|Te{79(BLX5NsBMrnM6ZOO~X~`>)2G9o?^ub!^Fzvhw zHy4JuMD(vo;(0CcoF1`@y%L%lg2km^y!7b zm-vxZ|BK(VkN&y4mt=xg+}RlW(Q3c*C$it&9ramIgHzJpIQ#ROT5z`R(9sBcm$$cP zXM6gL*|m*-0i|DpA3CSRZ}l&s%KlXvCWyL+=Xuzz_-?{=Ip2M^aH8cd;V$^r{lDS; Vb?&_QCQPHZUa^kD3wmu-{{_vm1y%q6 literal 0 HcmV?d00001 diff --git a/core/models.py b/core/models.py index 71a8362..67804bc 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,47 @@ from django.db import models +from django.contrib.auth.models import User -# Create your models here. +class UserProfile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + is_seller = models.BooleanField(default=False) + phone_number = models.CharField(max_length=20, blank=True) + address = models.TextField(blank=True) + + def __str__(self): + return self.user.username + +class Category(models.Model): + name = models.CharField(max_length=100) + slug = models.SlugField(unique=True) + + def __str__(self): + return self.name + +class Product(models.Model): + seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='products') + category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='products') + name = models.CharField(max_length=200) + description = models.TextField() + price = models.DecimalField(max_digits=10, decimal_places=2) + stock = models.IntegerField(default=0) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.name + +class Order(models.Model): + buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders') + status = models.CharField(max_length=20, choices=[('pending', 'Pending'), ('shipped', 'Shipped'), ('delivered', 'Delivered')], default='pending') + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"Order {self.id} by {self.buyer.username}" + +class OrderItem(models.Model): + order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items') + product = models.ForeignKey(Product, on_delete=models.CASCADE) + quantity = models.IntegerField() + price = models.DecimalField(max_digits=10, decimal_places=2) + + def __str__(self): + return f"{self.quantity} x {self.product.name} in Order {self.order.id}" \ No newline at end of file diff --git a/core/templates/core/buyer_dashboard.html b/core/templates/core/buyer_dashboard.html new file mode 100644 index 0000000..4d3cd16 --- /dev/null +++ b/core/templates/core/buyer_dashboard.html @@ -0,0 +1,38 @@ +{% extends "core/dashboard_base.html" %} + +{% block title %}Buyer Dashboard | Ethio-gebeya{% endblock %} + +{% block dashboard_content %} +
+
+

Total Orders

+

{{ total_orders }}

+
+
+ +
+

Recent Orders

+ {% if recent_orders %} + + + + + + + + + + {% for order in recent_orders %} + + + + + + {% endfor %} + +
OrderStatusDate
#{{ order.id }}{{ order.get_status_display }}{{ order.created_at|date:"Y-m-d" }}
+ {% else %} +

No orders yet.

+ {% endif %} +
+{% endblock %} diff --git a/core/templates/core/dashboard_base.html b/core/templates/core/dashboard_base.html new file mode 100644 index 0000000..94e0f1d --- /dev/null +++ b/core/templates/core/dashboard_base.html @@ -0,0 +1,159 @@ +{% extends "base.html" %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+ +
+
+

{{ dashboard_title }}

+ Sign Out +
+ {% block dashboard_content %}{% endblock %} +
+
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..5a817bd 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,18 +1,17 @@ {% extends "base.html" %} -{% block title %}{{ project_name }}{% endblock %} +{% block title %}Ethio-gebeya | Marketplace{% endblock %} {% block head %} - - - {% endblock %} {% block content %}
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+

Welcome to Ethio-gebeya

+

A modern marketplace foundation with separate dashboards for buyers and sellers.

+
+ {% if user.is_authenticated %} + Open Dashboard + Sign Out + {% else %} + Sign In + {% endif %}
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
+

Runtime: Django {{ django_version }} · Python {{ python_version }} · {{ current_time|date:"Y-m-d H:i:s" }} UTC

+
-
- Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC) -
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/login.html b/core/templates/core/login.html new file mode 100644 index 0000000..eaeab59 --- /dev/null +++ b/core/templates/core/login.html @@ -0,0 +1,67 @@ +{% extends "base.html" %} + +{% block title %}Sign In | Ethio-gebeya{% endblock %} + +{% block head %} + +{% endblock %} + +{% block content %} +
+
+

Sign in

+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+
+{% endblock %} diff --git a/core/templates/core/seller_dashboard.html b/core/templates/core/seller_dashboard.html new file mode 100644 index 0000000..0dcb70e --- /dev/null +++ b/core/templates/core/seller_dashboard.html @@ -0,0 +1,48 @@ +{% extends "core/dashboard_base.html" %} + +{% block title %}Seller Dashboard | Ethio-gebeya{% endblock %} + +{% block dashboard_content %} +
+
+

Products

+

{{ product_count }}

+
+
+

Orders

+

{{ total_orders }}

+
+
+

Revenue

+

${{ total_revenue }}

+
+
+ +
+

Recent Sales

+ {% if recent_sales %} + + + + + + + + + + + {% for sale in recent_sales %} + + + + + + + {% endfor %} + +
BuyerProductQtyTotal
{{ sale.order.buyer.username }}{{ sale.product.name }}{{ sale.quantity }}${{ sale.price }}
+ {% else %} +

No sales yet.

+ {% endif %} +
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..dcd0911 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,13 @@ from django.urls import path +from django.contrib.auth.views import LoginView, LogoutView -from .views import home +from .views import buyer_dashboard, dashboard_redirect, home, seller_dashboard urlpatterns = [ path("", home, name="home"), + path("login/", LoginView.as_view(template_name="core/login.html"), name="login"), + path("logout/", LogoutView.as_view(next_page="home"), name="logout"), + path("dashboard/", dashboard_redirect, name="dashboard"), + path("dashboard/buyer/", buyer_dashboard, name="buyer_dashboard"), + path("dashboard/seller/", seller_dashboard, name="seller_dashboard"), ] diff --git a/core/views.py b/core/views.py index c9aed12..4d00f97 100644 --- a/core/views.py +++ b/core/views.py @@ -2,12 +2,16 @@ import os import platform from django import get_version as django_version -from django.shortcuts import render +from django.contrib.auth.decorators import login_required +from django.db.models import Count, DecimalField, ExpressionWrapper, F, Sum +from django.shortcuts import redirect, render from django.utils import timezone +from .models import Order, OrderItem, Product + def home(request): - """Render the landing screen with loader and environment details.""" + """Render the landing screen with environment details.""" host_name = request.get_host().lower() agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" now = timezone.now() @@ -23,3 +27,67 @@ def home(request): "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), } return render(request, "core/index.html", context) + + +def _is_seller(user): + return hasattr(user, "userprofile") and user.userprofile.is_seller + + +@login_required +def dashboard_redirect(request): + if _is_seller(request.user): + return redirect("seller_dashboard") + return redirect("buyer_dashboard") + + +@login_required +def buyer_dashboard(request): + if _is_seller(request.user): + return redirect("seller_dashboard") + + recent_orders = ( + Order.objects.filter(buyer=request.user) + .prefetch_related("items__product") + .order_by("-created_at")[:5] + ) + total_orders = Order.objects.filter(buyer=request.user).count() + + context = { + "dashboard_title": "Buyer Dashboard", + "active_tab": "buyer", + "recent_orders": recent_orders, + "total_orders": total_orders, + } + return render(request, "core/buyer_dashboard.html", context) + + +@login_required +def seller_dashboard(request): + if not _is_seller(request.user): + return redirect("buyer_dashboard") + + seller_products = Product.objects.filter(seller=request.user) + sales_expression = ExpressionWrapper( + F("quantity") * F("price"), output_field=DecimalField(max_digits=12, decimal_places=2) + ) + sales_summary = OrderItem.objects.filter(product__seller=request.user).aggregate( + total_items=Sum("quantity"), + total_revenue=Sum(sales_expression), + total_orders=Count("order", distinct=True), + ) + recent_sales = ( + OrderItem.objects.filter(product__seller=request.user) + .select_related("product", "order__buyer") + .order_by("-order__created_at")[:8] + ) + + context = { + "dashboard_title": "Seller Dashboard", + "active_tab": "seller", + "product_count": seller_products.count(), + "total_orders": sales_summary["total_orders"] or 0, + "total_items": sales_summary["total_items"] or 0, + "total_revenue": sales_summary["total_revenue"] or 0, + "recent_sales": recent_sales, + } + return render(request, "core/seller_dashboard.html", context)