আগের পর্বে আমরা বিল কাটলে স্টক কমানো ও সেলস রেকর্ড জমা করা শিখেছি। আজ আমরা সেই জমা করা রেকর্ডগুলোকে সুন্দর করে দেখার জন্য একটি সেলস রিপোর্ট পেজ বানাবো। দোকানদার এই পেজে এসে এক নজরে দেখতে পারবে—আজ কত বিক্রি হলো, কত ক্যাশ পেমেন্ট, কত বাকি, এবং প্রতিটি বিলের বিস্তারিত।

রিপোর্ট পেজে কী কী থাকবে?

অংশকী দেখাবে?
সারাংশ কার্ডমোট বিক্রি, ক্যাশ, অনলাইন, বাকি—চারটি কার্ডে
তারিখ ফিল্টারআজ, গতকাল, বা নির্দিষ্ট তারিখ বেছে নেওয়া
বিল তালিকাপ্রতিটি বিলের আইডি, সময়, আইটেম, মোট টাকা
আইটেম বিস্তারিতপ্রতিটি বিলে কী কী প্রোডাক্ট বিক্রি হয়েছে

ধাপ ১: server.js-এ GET /sales API যোগ করো

cd ~/shop-api
micro server.js

নিচের কোড দুটি app.delete(...)-এর নিচে যোগ করো:

// GET /sales – সব সেলস রেকর্ড (নতুন → পুরোনো)
app.get('/sales', (req, res) => {
  const salesFile = path.join(__dirname, 'sales.json');
  if (!fs.existsSync(salesFile)) return res.json([]);
  const sales = JSON.parse(fs.readFileSync(salesFile, 'utf8'));
  sales.sort((a, b) => b.id - a.id); // নতুন আগে
  res.json(sales);
});

// GET /sales/:date – নির্দিষ্ট তারিখের সেলস
app.get('/sales/:date', (req, res) => {
  const salesFile = path.join(__dirname, 'sales.json');
  if (!fs.existsSync(salesFile)) return res.json([]);
  const sales = JSON.parse(fs.readFileSync(salesFile, 'utf8'));
  const filtered = sales.filter(s => s.date.startsWith(req.params.date));
  filtered.sort((a, b) => b.id - a.id);
  res.json(filtered);
});

ধাপ ২: report.html তৈরি করো

cd ~/shop-api/public
micro report.html

নিচের সম্পূর্ণ কোডটি কপি-পেস্ট করো। এখানে চারটি সারাংশ কার্ড, তারিখ ইনপুট, এবং বিলের তালিকা টেবিল আছে:

<!DOCTYPE html>
<html lang="bn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>📊 সেলস রিপোর্ট - মুদি দোকান</title>
  <style>
    :root { --bg: #f1f5f9; --surface: #ffffff; --text: #1e293b; --accent: #2563eb; }
    body.dark { --bg: #0f172a; --surface: #1e293b; --text: #e2e8f0; }
    * { margin:0; padding:0; box-sizing:border-box; }
    body { font-family:'Segoe UI', sans-serif; background:var(--bg); color:var(--text); padding:1rem; }
    .container { max-width:1000px; margin:0 auto; }
    h1 { text-align:center; margin-bottom:1rem; }
    .summary-cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:1rem; margin-bottom:1.5rem; }
    .card { background:var(--surface); border-radius:12px; padding:1rem; text-align:center; box-shadow:0 2px 8px rgba(0,0,0,0.1); }
    .card .amount { font-size:1.5rem; font-weight:bold; color:var(--accent); }
    .filter-bar { display:flex; gap:0.5rem; margin-bottom:1rem; }
    .filter-bar input { flex:1; padding:0.5rem; border:1px solid #cbd5e1; border-radius:8px; }
    .filter-bar button { padding:0.5rem 1rem; background:var(--accent); color:#fff; border:none; border-radius:8px; cursor:pointer; }
    table { width:100%; border-collapse:collapse; background:var(--surface); border-radius:12px; overflow:hidden; }
    th, td { padding:0.75rem; text-align:left; border-bottom:1px solid #e2e8f0; }
    th { background:#f8fafc; font-weight:600; }
    .bill-items { font-size:0.85rem; color:#64748b; margin-top:4px; }
  </style>
</head>
<body>
  <div class="container">
    <h1>📊 সেলস রিপোর্ট</h1>
    <div class="summary-cards">
      <div class="card"><div>মোট বিক্রি</div><div class="amount" id="totalSales">০ ৳</div></div>
      <div class="card"><div>মোট বিল</div><div class="amount" id="totalBills">০</div></div>
    </div>
    <div class="filter-bar">
      <input type="date" id="dateFilter">
      <button onclick="loadReport()">🔍 ফিল্টার</button>
      <button onclick="document.getElementById('dateFilter').value=''; loadReport();">🔄 সব</button>
    </div>
    <table>
      <thead><tr><th>বিল #</th><th>তারিখ</th><th>আইটেম</th><th>মোট</th></tr></thead>
      <tbody id="reportBody"><tr><td colspan="4">⏳ লোড হচ্ছে...</td></tr></tbody>
    </table>
  </div>
  <script>
    async function loadReport() {
      const date = document.getElementById('dateFilter').value;
      const url = date ? `/sales/${date}` : '/sales';
      const res = await fetch(url);
      const sales = await res.json();
      let total = 0;
      const tbody = document.getElementById('reportBody');
      if (!sales.length) { tbody.innerHTML = '<tr><td colspan="4">কোনো বিক্রি নেই</td></tr>'; }
      else {
        tbody.innerHTML = sales.map(s => { total += s.total; return `
          <tr>
            <td>#${s.id}</td>
            <td>${new Date(s.date).toLocaleString('bn-BD')}</td>
            <td>${s.items.length} টি<div class="bill-items">${s.items.map(i => i.name+' ×'+i.qty).join(', ')}</div></td>
            <td>৳ ${s.total}</td>
          </tr>`; }).join('');
      }
      document.getElementById('totalSales').textContent = total + ' ৳';
      document.getElementById('totalBills').textContent = sales.length;
    }
    // আজকের তারিখ ডিফল্ট
    document.getElementById('dateFilter').value = new Date().toISOString().split('T')[0];
    loadReport();
  </script>
</body>
</html>

লাইন-বাই-লাইন ব্যাখ্যা

অংশকী করছে?সহজ ভাষায়
sales.sort((a,b) => b.id - a.id)নতুন বিল আগে দেখাচ্ছে"সর্বশেষ বিল সবার উপরে"
date.startsWith(...)নির্দিষ্ট তারিখের বিল ফিল্টার"শুধু আজকেরটা দাও"
summary-cardsমোট বিক্রি ও বিল সংখ্যা"এক নজরে হিসাব"
new Date(s.date).toLocaleString('bn-BD')তারিখ বাংলা ফরম্যাটে"বাংলাদেশী সময়ে দেখাচ্ছে"

ধাপ ৩: টেস্ট করো

node server.js
termux-open-url http://localhost:3000/report.html

এখন আগে bill.html থেকে কিছু বিল কাটো, তারপর report.html ওপেন করো। দেখবে—আজকের সব বিল, মোট বিক্রি, এবং প্রতিটি বিলের আইটেম তালিকা চলে এসেছে!

আজ তুমি কী শিখলে

  • GET /sales API — সব বা নির্দিষ্ট তারিখের সেলস ডেটা আনা
  • তারিখ ফিল্টারdate.startsWith() দিয়ে নির্দিষ্ট দিনের ডেটা ফিল্টার
  • সারাংশ কার্ড — মোট বিক্রি, বিল সংখ্যা এক নজরে দেখানো
  • বাংলা তারিখtoLocaleString('bn-BD') দিয়ে বাংলা ফরম্যাটে তারিখ
  • টেবিল UI — প্রতিটি বিলের বিস্তারিত আইটেম-সহ তালিকা

পরবর্তী পর্বে আমরা JWT লগইন সিস্টেম বানাবো — যাতে শুধু অনুমোদিত ইউজারই দোকানের ডেটা দেখতে ও পরিবর্তন করতে পারে।

In the previous episode, we learned how to reduce stock and save sales records. Today we'll build a beautiful Sales Report Page where the shop owner can view today's total sales, cash vs due breakdown, and details of each bill.

What's in the Report Page?

SectionShows
Summary CardsTotal sales, number of bills
Date FilterFilter by today, yesterday, or any date
Bill ListBill ID, time, items, total

Step 1: Add GET /sales API

app.get('/sales', (req, res) => { ... });
app.get('/sales/:date', (req, res) => { ... });

Step 2: Create report.html

Build summary cards, date filter, and bill table with item details.

Step 3: Test

node server.js
termux-open-url http://localhost:3000/report.html

Next episode: JWT Login System — secure your shop data so only authorized users can access it.