# JavaScript yakuniy loyiha: vazifalar ilovasi

> O'rganilganlarni birga qo'llash: modul tuzilishi, holat boshqaruvi, DOM chizish, hodisa delegatsiyasi, localStorage va filtrlash. To'liq ishlaydigan loyiha.

Manba: https://learncode.uz/javascript/yakuniy-loyiha/
Sana: 2026-06-09

Bu darsda butun bo'lim bo'ylab o'rganilganlarni bitta ishlaydigan ilovada
birlashtiramiz: vazifalar ro'yxati — qo'shish, bajarish, o'chirish, filtrlash
va saqlash bilan.

## Loyiha tuzilishi

Haqiqiy loyihada kod [modullarga](https://learncode.uz/javascript/modullar) bo'linadi:

```
todo-app/
├── index.html
└── src/
    ├── main.js     ← ulash va ishga tushirish
    ├── state.js    ← ma'lumot va uning o'zgarishi
    ├── storage.js  ← localStorage bilan ishlash
    └── view.js     ← DOM chizish
```

Asosiy tamoyil — **vazifalarni ajratish**: holatni boshqaruvchi kod DOM'ni
bilmaydi, chizuvchi kod ma'lumotni o'zgartirmaydi.

## Holat qatlami

Ma'lumot va uning o'zgarishi — [sof funksiyalar](https://learncode.uz/javascript/funksional-dasturlash)
bilan:

```js
// state.js
function createTask(text) {
  return {
    id: Date.now() + Math.random().toString(36).slice(2, 7),
    text: text.trim(),
    done: false,
    date: new Date().toISOString(),
  };
}

// Har amal YANGI massiv qaytaradi — asl massiv o'zgarmaydi
const add = (list, text) => [...list, createTask(text)];

const toggle = (list, id) =>
  list.map((v) => (v.id === id ? { ...v, done: !v.done } : v));

const remove = (list, id) => list.filter((v) => v.id !== id);

const filterBy = (list, state) => {
  if (state === "completed") return list.filter((v) => v.done);
  if (state === "active") return list.filter((v) => !v.done);
  return list;
};

// Sinov
let list = [];
list = add(list, "Dars o'qish");
list = add(list, "Kod yozish");
list = add(list, "Dam olish");

console.log("Qo'shildi:", list.length);

list = toggle(list, list[0].id);
console.log("Birinchi bajarildi:", list[0].done);

console.log("Faol:", filterBy(list, "active").length);
console.log("Bajarilgan:", filterBy(list, "completed").length);

list = remove(list, list[2].id);
console.log("O'chirgandan keyin:", list.length);
```

Har funksiya alohida sinaladi — [testlash](https://learncode.uz/javascript/testlash) darsidagi
tamoyil.

## Saqlash qatlami

```js
// storage.js — localStorage bu sahifada ochilmaydi,
// shuning uchun xavfsiz o'rash naqshini ko'rsatamiz.

const KEY = "tasks";

function save(list) {
  try {
    localStorage.setItem(KEY, JSON.stringify(list));
    return true;
  } catch {
    console.log("Saqlab bo'lmadi (joy tugagan yoki ruxsat yo'q)");
    return false;
  }
}

function load() {
  try {
    const text = localStorage.getItem(KEY);
    if (!text) return [];

    const data = JSON.parse(text);
    if (!Array.isArray(data)) return [];

    // Tuzilmani tekshirish — buzilgan ma'lumotdan himoya
    return data.filter(
      (v) => v && typeof v.id === "string" && typeof v.text === "string"
    );
  } catch {
    return [];
  }
}

console.log("save va load — har ikkalasi try/catch bilan o'ralgan");
console.log("load() har doim massiv qaytaradi:", Array.isArray(load()));
```

Uchta himoya: `try/catch`, massiv ekanini tekshirish va har elementning
tuzilmasini tekshirish. [localStorage](https://learncode.uz/javascript/localstorage) darsidagi
qoida.

## To'liq ilova

```js
// ————— Holat —————
let tasks = [];
let currentFilter = "all";

const newId = () => Date.now() + Math.random().toString(36).slice(2, 7);

// ————— Elementlar —————
const input = document.querySelector("#text");
const ul = document.querySelector("#list");
const counter = document.querySelector("#counter");

// ————— Chizish —————
function render() {
  const visible = tasks.filter((v) => {
    if (currentFilter === "active") return !v.done;
    if (currentFilter === "completed") return v.done;
    return true;
  });

  ul.replaceChildren();

  for (const v of visible) {
    const li = document.createElement("li");
    li.dataset.id = v.id;
    li.style.cssText = "display:flex;gap:8px;align-items:center;padding:4px 0";

    const checkbox = document.createElement("input");
    checkbox.type = "checkbox";
    checkbox.checked = v.done;
    checkbox.dataset.action = "toggle";

    // XSS himoyasi: textContent, innerHTML emas
    const span = document.createElement("span");
    span.textContent = v.text;
    span.style.cssText = "flex:1" + (v.done
      ? ";text-decoration:line-through;color:#999" : "");

    const removeBtn = document.createElement("button");
    removeBtn.textContent = "×";
    removeBtn.dataset.action = "remove";

    li.append(checkbox, span, removeBtn);
    ul.append(li);
  }

  const remaining = tasks.filter((v) => !v.done).length;
  counter.textContent = tasks.length === 0
    ? "Vazifa yo'q"
    : remaining + " ta bajarilmagan / jami " + tasks.length;
}

// ————— Amallar —————
function add(text) {
  const clean = text.trim();
  if (!clean) return;
  tasks = [...tasks, { id: newId(), text: clean, done: false }];
  input.value = "";
  render();
}

// ————— Hodisalar (delegatsiya) —————
document.querySelector("#add").addEventListener("click", () => add(input.value));

input.addEventListener("keydown", (e) => {
  if (e.key === "Enter") add(input.value);
});

ul.addEventListener("click", (e) => {
  const action = e.target.dataset.action;
  if (!action) return;

  const id = e.target.closest("li").dataset.id;

  if (action === "toggle") {
    tasks = tasks.map((v) =>
      v.id === id ? { ...v, done: !v.done } : v);
  } else if (action === "remove") {
    tasks = tasks.filter((v) => v.id !== id);
  }
  render();
});

document.querySelector("#filters").addEventListener("click", (e) => {
  if (!e.target.dataset.filter) return;
  currentFilter = e.target.dataset.filter;
  render();
});

// ————— Boshlash —————
add("Dars o'qish");
add("Kod yozish");
add("Dam olish");

console.log("Ilova tayyor —", tasks.length, "ta vazifa");
console.log("Yuqoridagi maydonda sinab ko'ring: qo'shing, belgilang, filtrlang");
```

```html
<div style="font-family:system-ui;max-width:420px">
  <div style="display:flex;gap:6px;margin-bottom:10px">
    <input id="text" placeholder="Yangi vazifa" style="flex:1;padding:6px">
    <button id="add" style="padding:6px 12px">Qo'shish</button>
  </div>
  <div id="filters" style="margin-bottom:10px;display:flex;gap:6px">
    <button data-filter="all" style="padding:4px 10px">Hammasi</button>
    <button data-filter="active" style="padding:4px 10px">Faol</button>
    <button data-filter="completed" style="padding:4px 10px">Bajarilgan</button>
  </div>
  <ul id="list" style="list-style:none;padding:0;margin:0"></ul>
  <p id="counter" style="color:#666;font-size:14px"></p>
</div>
```

Ishlab ko'ring: vazifa qo'shing, belgilang, filtrlarni bosing.

## Qanday naqshlar ishlatildi

| Naqsh | Dars |
|---|---|
| O'zgartirmaslik (`[...massiv]`, `{ ...obyekt }`) | [ES6 sintaksis](https://learncode.uz/javascript/es6-sintaksis) |
| `map`, `filter` bilan holat o'zgarishi | [Massiv metodlari](https://learncode.uz/javascript/massiv-metodlari) |
| Bitta chizish funksiyasi | [DOM yaratish](https://learncode.uz/javascript/dom-yaratish) |
| `textContent` — XSS himoyasi | [Xavfsizlik](https://learncode.uz/javascript/xavfsizlik) |
| Bitta tinglovchi, `data-amal` | [Hodisa delegatsiyasi](https://learncode.uz/javascript/hodisa-delegatsiya) |
| `try/catch` bilan saqlash | [localStorage](https://learncode.uz/javascript/localstorage) |

> **Asosiy naqsh**
>
> Ilovaning yuragi bitta qoida: **holat o'zgaradi → `render()` chaqiriladi**.
>
> DOM hech qachon "qisman" tuzatilmaydi — u har doim joriy holatdan qaytadan
> chiziladi. Shuning uchun ekran va ma'lumot bir-biriga mos kelmay qolmaydi.
> React va Vue kabi kutubxonalar ham xuddi shu g'oyani (ancha
> optimallashtirilgan holda) bajaradi.

## Keyingi qadamlar

Loyihani kengaytirish uchun g'oyalar:

1. **Tahrirlash** — vazifa matnini ikki marta bosib o'zgartirish;
2. **Muddat** — [sana](https://learncode.uz/javascript/sana-vaqt) qo'shish va o'tganini ajratish;
3. **Saralash** — sana yoki muhimlik bo'yicha;
4. **Server** — [fetch](https://learncode.uz/javascript/fetch) bilan API'ga saqlash;
5. **Testlar** — holat funksiyalari uchun [Vitest](https://learncode.uz/javascript/testlash);
6. **Bundler** — [Vite](https://learncode.uz/javascript/bundler) bilan modullarga bo'lish.

## Bo'lim yakuni

Shu bilan JavaScript bo'limi tugadi. Siz o'rgandingiz:

- **Asoslar** — o'zgaruvchi, tur, shart, sikl, funksiya, massiv, obyekt;
- **DOM** — sahifani o'qish, o'zgartirish, element yaratish, hodisalar;
- **Asinxronlik** — callback, Promise, `async/await`, `fetch`;
- **Tuzilma** — klass, meros, modul, prototip;
- **Amaliyot** — testlash, xavfsizlik, tezlik, kod sifati.

> **Bundan keyin nima**
>
> Til asoslari tayyor. Endi ikki yo'nalish ochiladi:
>
> - **Frontend freymvork** — React yoki Vue: komponentlar bilan katta
>   interfeys qurish;
> - **Backend** — Node.js: xuddi shu tilda server yozish.
>
> Ikkalasi ham shu bo'limdagi bilimga tayanadi: modullar, `async/await`,
> massiv metodlari va o'zgartirmaslik tamoyili u yerda har kuni ishlatiladi.

## Amaliy mashq

1. Ilovani o'z faylingizga ko'chirib, ishga tushiring;
2. `localStorage` ni ulang — sahifa yangilanganda saqlanib qolsinmi?
3. "Hammasini tozalash" tugmasini qo'shing;
4. bajarilgan vazifalarni o'chiradigan tugma yasang;
5. holat funksiyalariga test yozing;
6. loyihani modullarga bo'lib, Vite bilan yig'ing.

## Qisqacha xulosa

- Kodni qatlamlarga ajrating: holat, saqlash, ko'rinish.
- Holatni **o'zgartirmang** — yangi massiv/obyekt qaytaring.
- Bitta `render()` funksiyasi butun ko'rinishni holatdan qayta quradi.
- Hodisalarni **delegatsiya** bilan bitta joyda ushlang.
- Foydalanuvchi matni uchun **doim `textContent`**.
- Tashqi ma'lumotni (`localStorage`, API) har doim tekshirib qabul qiling.
