# 🛍️ Sinov loyihasi: Modul B — ShopPress (Onlayn savdo markazi)

## 1. Loyiha haqida qisqacha ma'lumot

Jadal rivojlanayotgan elektron tijorat bozorida **ShopPress** startapi ma'murlarga mahsulotlar va buyurtmalarni bevosita boshqarish imkonini beruvchi o'zining savdo platformasini ishga tushirmoqda. Ma'murlar mahsulotlarni ro'yxatdan o'tkazishi va boshqarishi hamda buyurtmalarni qayta ishlashi mumkin, oddiy tashrif buyuruvchilar esa mahsulotlarni ko'rishlari va sotib olishlari mumkin. Ushbu loyihada ishtirokchilar ma'lumotlar bazasini loyihalashdan tortib asosiy funksiyalarni amalga oshirishgacha bo'lgan barcha ishlarga javobgardirlar. Bu MVP (minimal hayotiy mahsulot) bo'lgani uchun asosiy funksionallikni amalga oshirishga e'tibor qarating. **Dizayn baholash mezoni hisoblanmaydi.**

---

## 2. Texnologiyalar to'plami va cheklovlar

* **Texnologiyalar to'plami**: Dasturlash tili yoki freymvork bo'yicha cheklovlar yo'q. Laravel, Django, Ruby on Rails yoki Spring Boot kabi server-side rendering (SSR) qobiliyatiga ega har qanday freymvorkni erkin tanlashingiz mumkin.
* **Ish stoli muhiti**: Responsiv (moslashuvchan) dizayn talab qilinmaydi. Ilova faqat Chrome brauzerining desktop versiyasida ishlashi kerak.
* **Rasm yuklash**: Mahsulot rasmlari faqat JPG yoki PNG formatida bo'lishi kerak, fayl hajmi **5MB** bilan cheklangan. Ushbu tekshirish (validation) **server tomonida** amalga oshirilishi shart.
* **Ma'lumotlar bazasi**: Kategoriyalar alohida jadvalda (`categories`) boshqarilishi kerak. Mahsulotlar jadvali (`products`) `category_id` ni tashqi kalit (FK) sifatida ko'rsatishi kerak.
* **Dastlabki ma'lumotlar**: Quyidagi ma'lumotlar birinchi ishga tushirilganda (Seeder yoki Migration orqali) avtomatik ravishda yaratilishi kerak.
  * Ma'mur (Admin) hisobi: Ismi `Admin`, Email `admin@shoppress.local`, Parol `password` (parol xesh shaklida saqlanishi kerak)
  * Kamida 3 ta dastlabki kategoriya (masalan, `Kiyim-kechak`, `Elektronika`, `Oziq-ovqat`)

---

## 3. Bajarilishi kerak bo'lgan vazifalar

### **1. Autentifikatsiya**

#### 1.1 Tizimga kirish va sessiyalarni boshqarish

Kirish sahifasiga `/admin/login` manzili orqali kirish mumkin. Foydalanuvchi o'z emaili va parolini kiritadi. Muvaffaqiyatli kirishdan so'ng, foydalanuvchi ma'mur paneliga (dashboard) yo'naltiriladi. Xato bo'lsa, **"Incorrect email or password."** xabarini ko'rsating.

Tizimga kirmasdan `/admin` ostidagi har qanday yo'nalishga kirishga urinish foydalanuvchini kirish sahifasiga qayta yo'naltiradi. Tizimdan chiqish sessiyani yo'q qiladi va kirish sahifasiga yo'naltiradi.

Hozirda tizimga kirgan ma'murning ismi barcha ma'muriy sahifalarning navigatsiya qismida ko'rsatilishi kerak.

---

### **2. Ma'mur (Admin) funksiyalari**

#### 2.1 Dashboard (Boshqaruv paneli)

Dashboard standart ma'muriy sahifa bo'lib (`/admin` yoki `/admin/dashboard`), tizimga kirgandan keyin ko'rsatiladigan birinchi sahifadir.

Dashboardda quyidagi 4 ta statistikani kartalar shaklida ko'rsating.

| Karta yorlig'i | Mazmuni |
| :--- | :--- |
| **Total Products** | Mahsulotlarning umumiy soni |
| **Active Products** | Statusi `Active` bo'lgan mahsulotlar soni |
| **Total Orders** | Buyurtmalarning umumiy soni |
| **Total Revenue** | Barcha yakuniy to'lov miqdorlarining yig'indisi (kupon chegirmalaridan keyin) |

Shuningdek, eng oxirgi joylashtirilgan 5 ta buyurtma ro'yxatini ko'rsating. Har bir qatorda: Buyurtma raqami, Mijoz, Jami summa, Status va Sana bo'lishi kerak.

#### 2.2 Mahsulotlarni boshqarish

Mahsulotlarni boshqarish sahifasi (`/admin/products`) barcha mahsulotlarni jadvalda ko'rsatadi.

Jadval ustunlari: **Nomi**, **Kategoriyasi**, **Narxi**, **Zaxira (Stock)**, **Statusi**, **Yaratilgan sana**, Tahrirlash / O'chirish tugmalari.

Mahsulotlarni nomi bo'yicha filtrlash uchun qidiruv maydonini taqdim eting. Qidiruv so'zi yuborilganda faqat nomi ushbu kalit so'zni o'z ichiga olgan mahsulotlar ko'rsatiladi.

**Mahsulotni ro'yxatdan o'tkazish shakli** (`/admin/products/new`) va **mahsulotni tahrirlash shakli** (`/admin/products/{id}/edit`) quyidagi maydonlarni o'z ichiga olishi kerak.

* **Nomi** (majburiy, maksimal 100 belgi)
* **Kategoriya** (majburiy, dropdown — ma'lumotlar bazasidagi kategoriyalar bilan to'ldirilgan)
* **Narxi** (majburiy, butun son, 0 yoki undan katta)
* **Zaxira (Stock)** (majburiy, butun son, 0 yoki undan katta)
* **Tavsifi** (`<textarea>`, ixtiyoriy)
* **Rasm** (fayl yuklash, JPG/PNG, maksimal 5MB, **ixtiyoriy** — mahsulotlar rasmsiz ham ro'yxatdan o'tkazilishi mumkin)
* **Status** (dropdown: `Active` / `Out of Stock` / `Hidden`)

Tahrirlash shakliga kirganda, mavjud ma'lumotlar har bir maydonga oldindan to'ldirilgan bo'lishi kerak. Agar yangi rasm yuklanmasa, eski rasm saqlanib qoladi. Agar yangi rasm yuklansa, eski rasm faylini serverdan o'chiring va uni yangisi bilan almashtiring.

Mahsulotni o'chirishda **"Are you sure you want to delete this product?"** tasdiqlash xabarini ko'rsating. Tasdiqlanganda, agar mahsulot biron bir buyurtmada (`order_items`) mavjud bo'lsa, o'chirishni rad eting va **"Cannot delete a product with order history."** xabarini ko'rsating. Agar mahsulot buyurtmalar tarixida bo'lmasa, uni ma'lumotlar bazasidan o'chiring va uning rasm faylini serverdan olib tashlang.

#### 2.3 Kategoriyalarni boshqarish

Kategoriyalarni boshqarish sahifasi (`/admin/categories`) kategoriyalarni qo'shish, tahrirlash va o'chirish imkonini beradi.

* **Qo'shish**: Kategoriya nomini kiriting va saqlang. Agar bunday nom mavjud bo'lsa, **"Category name already exists."** xabarini ko'rsating.
* **Tahrirlash**: Kategoriya nomini o'zgartiring. Mahsulotlar kategoriyaga FK orqali bog'langanligi sababli, `categories` jadvalidagi nomni yangilash bog'langan mahsulotlarda avtomatik ravishda aks etadi.
* **O'chirish**: Agar biron bir mahsulot ushbu kategoriyaga tegishli bo'lsa, o'chirishni rad eting va **"Cannot delete a category that has products assigned."** xabarini ko'rsating.

#### 2.4 Kuponlarni boshqarish

Kuponlarni boshqarish sahifasi (`/admin/coupons`) chegirma kuponlarini ro'yxatdan o'tkazish va boshqarish imkonini beradi.

**Kuponni ro'yxatdan o'tkazish shakli** quyidagi maydonlarni o'z ichiga oladi.

* **Kupon kodi** (majburiy, faqat bosh harflar yoki raqamlar, 6–12 belgi, takrorlanmas bo'lishi kerak)
* **Chegirma turi** (dropdown: `Fixed` / `Percent`)
* **Chegirma qiymati** (majburiy, butun son, 1 yoki undan katta. Agar `Percent` bo'lsa, 1 va 100 orasida bo'lishi kerak)
* **Minimal buyurtma miqdori** (majburiy, butun son, 0 yoki undan katta. Kupon ushbu miqdordan past bo'lgan buyurtmalarga qo'llanilmaydi)
* **Amal qilish muddati** (majburiy, `<input type="date">`. Faqat kelajakdagi sanalar tanlanishi mumkin)
* **Foydalanish limiti** (majburiy, butun son, 1 yoki undan katta)

Kuponlar ro'yxati jadvali ustunlari: **Kodi**, **Turi**, **Qiymati**, **Min. buyurtma**, **Amal qilish muddati**, **Qolgan foydalanishlar**, O'chirish tugmasi. Qolgan foydalanishlar `remaining_uses` ustuni orqali boshqariladi, u ro'yxatdan o'tishda Foydalanish limiti qiymatiga tenglashtiriladi va kupon ishlatilganda 1 taga kamaytirib boriladi.

#### 2.5 Buyurtmalarni boshqarish

Buyurtmalarni boshqarish sahifasi (`/admin/orders`) barcha buyurtmalarni jadvalda ko'rsatadi.

Jadval ustunlari: **Buyurtma #**, **Mijoz**, **Telefon**, **Jami**, **Status**, **Sana**.

Buyurtmalarni status bo'yicha filtrlash uchun dropdown taqdim eting. Status qiymatlari: `Pending` / `Processing` / `Shipped` / `Delivered` / `Cancelled`. Faqat tanlangan statusga mos keladigan buyurtmalar ko'rsatiladi.

Buyurtma tafsilotlari sahifasi (`/admin/orders/{id}`) quyidagi ma'lumotlarni ko'rsatishi kerak.

* Mijoz ma'lumotlari: Ismi, Telefoni, Yetkazib berish manzili
* Buyurtma elementlari: Mahsulot (mahsulot nomining o'sha paytdagi holati - snapshot), birlik narxi, miqdori, oraliq summa
* Kupon: Qo'llanilgan kupon kodi va chegirma miqdori. Agar kupon ishlatilmagan bo'lsa, **"None"** deb ko'rsating.
* Jami: Yakuniy to'lov miqdori
* **Buyurtma statusi** dropdown (`Pending` / `Processing` / `Shipped` / `Delivered` / `Cancelled`) va Saqlash tugmasi. Muvaffaqiyatli saqlangandan so'ng, xuddi shu sahifada **"Order status updated."** xabarini ko'rsating.
* Status **`Cancelled`** ga o'zgartirilganda, buyurtmadagi barcha mahsulotlarning zaxirasini (stock) buyurtma qilingan miqdorda qayta tiklang. Agar buyurtma allaqachon `Cancelled` bo'lsa va status qaytadan `Cancelled` ga o'zgartirilsa, zaxirani ikkinchi marta tiklamang.

---

### **3. Ommaviy sahifalar**

#### 3.1 Bosh sahifa — Mahsulotlar ro'yxati

Bosh sahifa (`/`) statusi **`Active`** bo'lgan mahsulotlarni kartalar ko'rinishida ko'rsatadi. Har bir kartada mahsulot rasmi (agar rasm bo'lmasa, kulrang placeholder maydonini ko'rsating), mahsulot nomi, kategoriya nomi va narxi ko'rsatiladi.

Har bir sahifada **12 ta elementni** sahifalash (pagination) bilan ko'rsating. Eng oxirgi ro'yxatdan o'tganlar birinchi bo'lib ko'rinsin.

Kategoriyalar bo'yicha filtrlash havolalarini taqdim eting. Kategoriyani bosganda faqat o'sha kategoriyadagi **`Active`** mahsulotlar ko'rsatiladi. Sahifalashda joriy kategoriya filtri saqlanib qolishi kerak.

Kalit so'z bilan qidirish maydonini taqdim eting. Mahsulotlar nomi bo'yicha qidiriladi. Kategoriya filtri va kalit so'z qidiruvi bir vaqtning o'zida qo'llanilishi kerak. Sahifalashda qidiruv shartlari saqlanib qolishi kerak.

#### 3.2 Mahsulot tafsilotlari

Mahsulot tafsilotlari sahifasiga `/products/{id}` manzili orqali kirish mumkin. Mavjud bo'lmagan ID-lar yoki statusi **`Hidden`** bo'lgan mahsulotlar uchun 404 sahifasini qaytaring.

Mahsulot tafsilotlari sahifasida mahsulot rasmi (rasm bo'lmasa kulrang placeholder), mahsulot nomi, kategoriya nomi, narxi, zaxira miqdori va tavsifi ko'rsatiladi.

Zaxirasi 0 bo'lgan yoki statusi **`Out of Stock`** bo'lgan mahsulotlarda **"Out of Stock"** xabari ko'rsatiladi va sotib olish tugmasi faolsizlantiriladi.

Zaxirasi 1 yoki undan ortiq va statusi **`Active`** bo'lgan mahsulotlarda miqdor kiritish maydoni (`<input type="number">`, min 1, max joriy zaxira) va **"Buy Now"** tugmasi ko'rsatiladi. Tugma bosilganda mahsulot ID-si va miqdori query string sifatida uzatiladi va buyurtma qilish (checkout) sahifasiga o'tiladi (`/checkout?product_id={id}&quantity={n}`).

#### 3.3 Buyurtmani rasmiylashtirish (Checkout)

Checkout sahifasi (`/checkout`) query stringdan `product_id` va `quantity` ni o'qiydi va buyurtma qisqacha mazmunini ko'rsatadi (mahsulot nomi, birlik narxi, miqdori, oraliq summa). Server quyidagi shartlarni tekshirishi va agar ulardan birortasi bajarilmasa, bosh sahifaga (`/`) yo'naltirishi kerak:

* `product_id` bo'yicha mahsulot mavjud emas
* Mahsulot statusi **`Hidden`** yoki **`Out of Stock`**
* Zaxira 0 ga teng
* `quantity` 1 dan kichik yoki joriy zaxiradan ko'p

Sahifada xaridorning ismi, telefoni va yetkazib berish manzili uchun forma mavjud. Barcha maydonlar majburiy va buyurtmalar faqat **server tomonidagi tekshiruvdan** o'tgandan keyingina qayta ishlanadi.

**Coupon Code** kiritish maydoni va **"Apply"** tugmasi mavjud. "Apply" bosilganda, `/checkout/coupon` endpointiga quyidagi formatda POST so'rovini yuborish uchun `fetch` dan foydalaning va sahifani to'liq yangilamasdan natijani aks ettiring. Ushbu endpoint ishtirokchi tomonidan amalga oshirilishi kerak.

* So'rov tanasi (JSON): `{ "coupon_code": "ABCD1234", "subtotal": 30000 }`
* Muvaffaqiyatli javob (JSON): `{ "valid": true, "discount_type": "fixed"|"percent", "discount_value": 3000, "discount_amount": 3000, "final_amount": 27000 }`
* Xato javob (JSON): `{ "valid": false, "message": "error message" }`

* To'g'ri kupon: Chegirmani hisoblang va sahifada yangilangan oraliq summa, chegirma va jami summani ko'rsating.
  * `Fixed` chegirma: Chegirma qiymatini to'g'ridan-to'g'ri oraliq summadan ayiring.
  * `Percent` chegirma: `oraliq summa × chegirma stavkasi / 100` ni ayiring. Har qanday o'nlik kasrni pastga qarab yaxlitlang (floor).
  * Yakuniy summa 0 dan pastga tushmasligi kerak.
* Noto'g'ri kupon: Har bir holat uchun tegishli xato xabarini ko'rsating.
  * Kod mavjud emas: **"Invalid coupon code."**
  * Kupon muddati o'tgan: **"This coupon has expired."**
  * Foydalanish limiti tugagan: **"This coupon has reached its usage limit."**
  * Minimal buyurtma miqdoridan past: **"This coupon requires a minimum order of {n}."**

Buyurtmani yuborishda quyidagilarni **yagona tranzaksiya** sifatida bajaring.
1. Buyurtma ma'lumotlarini `orders` jadvaliga saqlang (xaridor ismi, telefoni, yetkazib berish manzili, qo'llanilgan kupon ID-si — bo'lmasa NULL, chegirma miqdori — bo'lmasa 0, yakuniy to'lov miqdori).
2. Buyurtma elementlari ma'lumotlarini `order_items` jadvaliga saqlang (buyurtma ID, mahsulot ID, **mahsulot nomi snapshot**, miqdori, birlik narxi). Mahsulot nomi buyurtma berilgan paytdagi holatida saqlanishi kerak, shunda mahsulot keyinchalik o'chirilsa yoki nomi o'zgartirilsa ham to'g'ri ko'rinadi.
3. Buyurtma qilingan miqdorni mahsulot zaxirasidan (`stock`) chegirib tashlang.
4. Agar kupon ishlatilgan bo'lsa, kuponning `remaining_uses` qiymatini 1 taga kamaytiring.

Agar so'ralgan miqdor uchun zaxira yetarli bo'lmasa, tranzaksiyani bekor qiling (roll back) va **"Not enough stock."** xabarini ko'rsating.

Buyurtma muvaffaqiyatli yuborilgandan so'ng, buyurtma yakunlangan sahifasiga (`/orders/{id}/complete`) yo'naltiring. Ushbu sahifa autentifikatsiyasiz ochiq bo'ladi va Buyurtma raqami, buyurtma elementlari tafsilotlari, chegirma va jami summani ko'rsatadi.

#### 3.4 Layout (Maket)

Barcha ommaviy sahifalar sarlavha (header) va foter (footer) qismlarini o'z ichiga oladi. Sarlavhada sayt nomi (**ShopPress**) va bosh sahifaga havola mavjud. Har bir sahifada uning mazmunini aks ettiruvchi tegishli `<title>` tegi bo'lishi kerak.

Ma'muriy panel sahifalarida sidebar yoki yuqori navigatsiya mavjud. Navigatsiya menyusi elementlari: **Dashboard**, **Products**, **Categories**, **Coupons**, **Orders**.

---

## 4. Topshirish bo'yicha ko'rsatmalar

Barcha natijalarni serverdagi `/module_b/` katalogiga yuklang.

---

## 5. Baholash sxemasi — Jami 25 ball

| Element | Mezon | Ballar |
| :--- | :--- | :---: |
| **A. Autentifikatsiya** | Kirish/chiqish, sessiya himoyasi, ruxsatsiz kirishni qayta yo'naltirish | 2 |
| **B. Dashboard** | 4 ta statistika kartasi (inglizcha yorliqlar), oxirgi 5 ta buyurtma ro'yxati | 1 |
| **C. Mahsulotlar boshqaruvi** | Ro'yxat (qidiruv bilan), ro'yxatdan o'tkazish (ixtiyoriy rasm), tahrirlash (eski rasmni saqlash/almashtirish+o'chirish), o'chirish (agar buyurtma tarixi bo'lsa rad etish + rasm faylini o'chirish) | 5 |
| **D. Kategoriyalar boshqaruvi** | Qo'shish (takrorlanishni tekshirish), tahrirlash, o'chirish (mahsulotlar biriktirilgan bo'lsa rad etish) | 2 |
| **E. Kuponlar boshqaruvi** | Ro'yxatdan o'tkazish (to'liq tekshiruv), ro'yxat (qolgan foydalanishlarni ko'rsatish), o'chirish | 3 |
| **F. Buyurtmalar boshqaruvi** | Ro'yxat (status filtri), batafsil ko'rinish, statusni yangilash, "Cancelled" holatida zaxirani tiklash (takroriy tiklashni oldini olish) | 4 |
| **G. Ommaviy — Mahsulotlar ro'yxati** | Faol mahsulotlarni filtrlash, kategoriya filtri, kalit so'z qidiruvi, sahifalash (shartlarni saqlash) | 3 |
| **H. Ommaviy — Mahsulot tafsilotlari** | Mahsulot ma'lumotlarini ko'rsatish, "Out of Stock" holatini qayta ishlash (zaxira 0 yoki "Out of Stock" statusi), miqdor kiritish va checkoutga o'tish | 1 |
| **I. Ommaviy — Checkout** | Kirish tekshiruvi (Out of Stock/Hidden/zaxiradan ko'p miqdor), kuponni qo'llash (fetch + chegirma hisobi), tranzaksiya (buyurtmani saqlash + zaxirani kamaytirish + kuponni kamaytirish), buyurtma yakunlangan sahifasi | 3 |
| **J. Validation** | Server tomonida rasm hajmi/kengaytmasini tekshirish, checkout formasi maydonlarini tekshirish | 1 |
| **Jami** | | **25** |