From 04ca523a81eb1f61ba0845d91c38a1328e53f7c0 Mon Sep 17 00:00:00 2001 From: Muawiya-contact Date: Tue, 12 May 2026 19:18:07 +0500 Subject: [PATCH] Add Complete DBMS Project. Signed-off-by: Muawiya-contact --- msms/.gitignore | 7 + msms/README.md | 340 ++ msms/backend/.env.example | 9 + msms/backend/db.js | 32 + msms/backend/middleware/auth.js | 26 + msms/backend/middleware/validate.js | 12 + msms/backend/package-lock.json | 1607 ++++++++ msms/backend/package.json | 24 + msms/backend/routes/auth.js | 80 + msms/backend/routes/categories.js | 91 + msms/backend/routes/customers.js | 103 + msms/backend/routes/doctors.js | 82 + msms/backend/routes/employees.js | 109 + msms/backend/routes/manufacturers.js | 80 + msms/backend/routes/medicines.js | 134 + msms/backend/routes/orders.js | 195 + msms/backend/routes/payments.js | 58 + msms/backend/routes/prescriptions.js | 104 + msms/backend/routes/reports.js | 78 + msms/backend/routes/stock.js | 72 + msms/backend/routes/suppliers.js | 83 + msms/backend/server.js | 51 + msms/database/indexes.sql | 38 + msms/database/sample_data.sql | 194 + msms/database/schema.sql | 253 ++ msms/database/stored_procedures.sql | 240 ++ msms/database/triggers.sql | 141 + msms/database/views.sql | 175 + msms/docs/MSMS_Project_Manual (1).pdf | Bin 0 -> 73222 bytes msms/docs/Medical_Store_Proposal.pdf | Bin 0 -> 108023 bytes msms/docs/msms_er_diagram (1).html | 213 + msms/frontend/index.html | 16 + msms/frontend/package-lock.json | 3634 +++++++++++++++++ msms/frontend/package.json | 37 + msms/frontend/postcss.config.js | 3 + msms/frontend/public/favicon.svg | 4 + msms/frontend/src/App.jsx | 122 + msms/frontend/src/api/index.js | 79 + .../src/components/Bill/BillActions.jsx | 52 + .../src/components/Bill/BillPreview.jsx | 113 + .../frontend/src/components/Layout/Navbar.jsx | 66 + .../src/components/Layout/ProtectedRoute.jsx | 10 + .../src/components/Layout/Sidebar.jsx | 108 + msms/frontend/src/components/UI/Badge.jsx | 37 + .../src/components/UI/ConfirmDialog.jsx | 25 + msms/frontend/src/components/UI/CrudPage.jsx | 180 + msms/frontend/src/components/UI/Modal.jsx | 32 + .../frontend/src/components/UI/Pagination.jsx | 52 + msms/frontend/src/components/UI/SearchBar.jsx | 17 + msms/frontend/src/components/UI/StatCard.jsx | 23 + msms/frontend/src/context/AuthContext.jsx | 40 + msms/frontend/src/context/CartContext.jsx | 53 + msms/frontend/src/index.css | 94 + msms/frontend/src/main.jsx | 13 + .../src/pages/Customers/CustomerList.jsx | 43 + msms/frontend/src/pages/Dashboard.jsx | 121 + .../frontend/src/pages/Doctors/DoctorList.jsx | 40 + .../src/pages/Employees/EmployeeList.jsx | 174 + .../src/pages/Inventory/MedicineList.jsx | 258 ++ .../src/pages/Inventory/StockList.jsx | 167 + msms/frontend/src/pages/Login.jsx | 107 + .../src/pages/Payments/PaymentList.jsx | 103 + .../pages/Prescriptions/PrescriptionList.jsx | 172 + msms/frontend/src/pages/Reports/Reports.jsx | 268 ++ msms/frontend/src/pages/Sales/NewOrder.jsx | 343 ++ msms/frontend/src/pages/Sales/OrderDetail.jsx | 73 + msms/frontend/src/pages/Sales/OrderList.jsx | 97 + .../src/pages/Suppliers/SupplierList.jsx | 42 + msms/frontend/tailwind.config.js | 21 + msms/frontend/vite.config.js | 15 + msms/package-lock.json | 372 ++ msms/package.json | 14 + 72 files changed, 11871 insertions(+) create mode 100644 msms/.gitignore create mode 100644 msms/README.md create mode 100644 msms/backend/.env.example create mode 100644 msms/backend/db.js create mode 100644 msms/backend/middleware/auth.js create mode 100644 msms/backend/middleware/validate.js create mode 100644 msms/backend/package-lock.json create mode 100644 msms/backend/package.json create mode 100644 msms/backend/routes/auth.js create mode 100644 msms/backend/routes/categories.js create mode 100644 msms/backend/routes/customers.js create mode 100644 msms/backend/routes/doctors.js create mode 100644 msms/backend/routes/employees.js create mode 100644 msms/backend/routes/manufacturers.js create mode 100644 msms/backend/routes/medicines.js create mode 100644 msms/backend/routes/orders.js create mode 100644 msms/backend/routes/payments.js create mode 100644 msms/backend/routes/prescriptions.js create mode 100644 msms/backend/routes/reports.js create mode 100644 msms/backend/routes/stock.js create mode 100644 msms/backend/routes/suppliers.js create mode 100644 msms/backend/server.js create mode 100644 msms/database/indexes.sql create mode 100644 msms/database/sample_data.sql create mode 100644 msms/database/schema.sql create mode 100644 msms/database/stored_procedures.sql create mode 100644 msms/database/triggers.sql create mode 100644 msms/database/views.sql create mode 100644 msms/docs/MSMS_Project_Manual (1).pdf create mode 100644 msms/docs/Medical_Store_Proposal.pdf create mode 100644 msms/docs/msms_er_diagram (1).html create mode 100644 msms/frontend/index.html create mode 100644 msms/frontend/package-lock.json create mode 100644 msms/frontend/package.json create mode 100644 msms/frontend/postcss.config.js create mode 100644 msms/frontend/public/favicon.svg create mode 100644 msms/frontend/src/App.jsx create mode 100644 msms/frontend/src/api/index.js create mode 100644 msms/frontend/src/components/Bill/BillActions.jsx create mode 100644 msms/frontend/src/components/Bill/BillPreview.jsx create mode 100644 msms/frontend/src/components/Layout/Navbar.jsx create mode 100644 msms/frontend/src/components/Layout/ProtectedRoute.jsx create mode 100644 msms/frontend/src/components/Layout/Sidebar.jsx create mode 100644 msms/frontend/src/components/UI/Badge.jsx create mode 100644 msms/frontend/src/components/UI/ConfirmDialog.jsx create mode 100644 msms/frontend/src/components/UI/CrudPage.jsx create mode 100644 msms/frontend/src/components/UI/Modal.jsx create mode 100644 msms/frontend/src/components/UI/Pagination.jsx create mode 100644 msms/frontend/src/components/UI/SearchBar.jsx create mode 100644 msms/frontend/src/components/UI/StatCard.jsx create mode 100644 msms/frontend/src/context/AuthContext.jsx create mode 100644 msms/frontend/src/context/CartContext.jsx create mode 100644 msms/frontend/src/index.css create mode 100644 msms/frontend/src/main.jsx create mode 100644 msms/frontend/src/pages/Customers/CustomerList.jsx create mode 100644 msms/frontend/src/pages/Dashboard.jsx create mode 100644 msms/frontend/src/pages/Doctors/DoctorList.jsx create mode 100644 msms/frontend/src/pages/Employees/EmployeeList.jsx create mode 100644 msms/frontend/src/pages/Inventory/MedicineList.jsx create mode 100644 msms/frontend/src/pages/Inventory/StockList.jsx create mode 100644 msms/frontend/src/pages/Login.jsx create mode 100644 msms/frontend/src/pages/Payments/PaymentList.jsx create mode 100644 msms/frontend/src/pages/Prescriptions/PrescriptionList.jsx create mode 100644 msms/frontend/src/pages/Reports/Reports.jsx create mode 100644 msms/frontend/src/pages/Sales/NewOrder.jsx create mode 100644 msms/frontend/src/pages/Sales/OrderDetail.jsx create mode 100644 msms/frontend/src/pages/Sales/OrderList.jsx create mode 100644 msms/frontend/src/pages/Suppliers/SupplierList.jsx create mode 100644 msms/frontend/tailwind.config.js create mode 100644 msms/frontend/vite.config.js create mode 100644 msms/package-lock.json create mode 100644 msms/package.json diff --git a/msms/.gitignore b/msms/.gitignore new file mode 100644 index 0000000..52a32ff --- /dev/null +++ b/msms/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +.env +dist/ +*.log +.DS_Store +.vscode/ +coverage/ diff --git a/msms/README.md b/msms/README.md new file mode 100644 index 0000000..4b64bea --- /dev/null +++ b/msms/README.md @@ -0,0 +1,340 @@ +# πŸ₯ Medical Store Management System (MSMS) + +> **Student:** Muawiya Amir | **ID:** 2k24_BSAI_72 +> **Course:** Database Lab β€” 4th Semester BS Artificial Intelligence +> **Instructor:** Sir Ahsan Ahmed + +--- + +## 🚦 How to Run (Quick Start) + +1. **Install dependencies** + Open two terminals and run: + - In `backend` folder: + `npm install` + - In `frontend` folder: + `npm install` + +2. **Set up environment** + - Copy `backend/.env.example` to `backend/.env` and fill in your MySQL info. + +3. **Set up the database** + - Import all SQL files in `database/` into your MySQL server (see detailed steps below). + +4. **Start the servers** + - In `backend`: + `npm start` + - In `frontend`: + `npm run dev` + +5. **Open the app** + - Frontend: [http://localhost:5173](http://localhost:5173) + - Backend API: [http://localhost:5000](http://localhost:5000) + +--- + +## ✨ Features + +### Core Modules + +- **Inventory Management** β€” Add, edit, search medicines with dosage form, strength, price, and prescription flag +- **Point of Sale (POS)** β€” Real-time cart, customer lookup, prescription validation, live total calculation +- **Prescription Tracking** β€” Link doctor prescriptions to customers; enforce Rx-only sales +- **Supplier Management** β€” Track suppliers with ratings and contact details +- **Customer Management** β€” Profiles with purchase history and total spending +- **Employee Management** β€” Role-based accounts (Admin / Manager / Pharmacist / Cashier) +- **Payment Processing** β€” Cash, Card, Online, Insurance; full transaction log +- **Reports & Analytics** β€” Low stock, expiry alerts, sales summary with charts, top-selling medicines + +### Database Features + +- 12 normalized tables (3NF) +- 4 Triggers (stock deduction, low-stock alert, order total recalc, expiry check) +- 4 Stored Procedures (GenerateBill, RestockMedicine, PlaceOrder, CompletePayment) +- 7 Views (LowStockAlert, ExpiringSoon, SalesSummary, TopSellingMedicines, EmployeePerformance, SupplierRatings, FullInventory) +- Indexes on high-frequency query columns + +### UI/UX Extras + +- πŸŒ™ Dark mode (persisted in localStorage) +- πŸ–¨οΈ Printable invoices (`window.print()` hides sidebar/navbar) +- πŸ“₯ PDF bill download (A5 size via jsPDF + html2canvas) +- πŸ“Š Sales bar chart (Recharts) +- πŸ“€ Export to CSV on every report tab +- πŸ”” Toast notifications for all actions +- πŸ”΄ Red dot badge on sidebar when medicines expire within 7 days +- ⚠️ Low-stock banner on dashboard +- JWT authentication with role-based route protection + +--- + +## πŸ›  Tech Stack + +| Layer | Technology | +| -------- | ------------------------------------- | +| Frontend | React 18, Vite, TailwindCSS, Recharts | +| Backend | Node.js, Express 4, mysql2 | +| Database | MySQL 8.0 | +| Auth | JWT (jsonwebtoken) + bcryptjs | +| Forms | react-hook-form + Zod validation | +| PDF | jsPDF + html2canvas | + +--- + +## πŸ“‹ Prerequisites + +- **Node.js** v18 or higher +- **npm** v9 or higher +- **MySQL 8.0** running locally + +--- + +## πŸš€ Installation & Setup + +### 1. Clone the repository + +```bash +git clone "https://github.com/Coding-Moves/BSAI-Projects.git" +cd msms +``` + +### 2. Install all dependencies + +```bash +npm run install:all +``` + +This installs root, backend, and frontend packages in one command. + +### 3. Configure backend environment + +```bash +cd backend +cp .env.example .env +``` + +Open `.env` and fill in your MySQL credentials: + +``` +DB_HOST=localhost +DB_PORT=3306 +DB_USER=root +DB_PASSWORD=your_mysql_password +DB_NAME=msms_db +JWT_SECRET=change_this_to_a_random_secret +PORT=5000 +``` + +### 4. Set up the MySQL database + +Open **MySQL Workbench** (or any MySQL client) and run the SQL files **in this exact order**: + +```sql +-- Step 1: Create tables +SOURCE /path/to/msms/database/schema.sql; + +-- Step 2: Insert sample data +SOURCE /path/to/msms/database/sample_data.sql; + +-- Step 3: Create triggers +SOURCE /path/to/msms/database/triggers.sql; + +-- Step 4: Create stored procedures +SOURCE /path/to/msms/database/stored_procedures.sql; + +-- Step 5: Create views +SOURCE /path/to/msms/database/views.sql; + +-- Step 6: Create indexes +SOURCE /path/to/msms/database/indexes.sql; +``` + +Or run them from the command line: + +```bash +mysql -u root -p < database/schema.sql +mysql -u root -p msms_db < database/sample_data.sql +mysql -u root -p msms_db < database/triggers.sql +mysql -u root -p msms_db < database/stored_procedures.sql +mysql -u root -p msms_db < database/views.sql +mysql -u root -p msms_db < database/indexes.sql +``` + +### 5. Start the application + +```bash +cd .. +npm run dev +``` + +This runs both frontend and backend concurrently: + +- **Backend API:** http://localhost:5000 +- **Frontend:** http://localhost:5173 + +--- + +## πŸ” Default Login + +| Username | Password | Role | +| -------- | ---------- | ----- | +| `admin` | `admin123` | Admin | + +> ⚠️ Change the default password immediately after first login in production. + +--- + +## πŸ—‚ Project Structure + +``` +msms/ +β”œβ”€β”€ README.md +β”œβ”€β”€ package.json ← root (concurrently) +β”œβ”€β”€ .gitignore +β”‚ +β”œβ”€β”€ docs/ +| β”œβ”€β”€ report.pdf +| β”œβ”€β”€ proposal.pdf +| β”œβ”€β”€ manual.pdf +| β”œβ”€β”€ ER_diagram.pdf +| +β”œβ”€β”€ backend/ +β”‚ β”œβ”€β”€ server.js ← Express entry point +β”‚ β”œβ”€β”€ db.js ← MySQL connection pool +β”‚ β”œβ”€β”€ .env.example +β”‚ β”œβ”€β”€ routes/ +β”‚ β”‚ β”œβ”€β”€ auth.js ← POST /auth/login, GET /auth/me +β”‚ β”‚ β”œβ”€β”€ medicines.js +β”‚ β”‚ β”œβ”€β”€ categories.js +β”‚ β”‚ β”œβ”€β”€ manufacturers.js +β”‚ β”‚ β”œβ”€β”€ suppliers.js +β”‚ β”‚ β”œβ”€β”€ stock.js ← includes POST /restock +β”‚ β”‚ β”œβ”€β”€ customers.js +β”‚ β”‚ β”œβ”€β”€ doctors.js +β”‚ β”‚ β”œβ”€β”€ employees.js +β”‚ β”‚ β”œβ”€β”€ prescriptions.js +β”‚ β”‚ β”œβ”€β”€ orders.js ← includes bill + complete endpoints +β”‚ β”‚ β”œβ”€β”€ payments.js +β”‚ β”‚ └── reports.js ← calls all 4 views + dashboard stats +β”‚ └── middleware/ +β”‚ β”œβ”€β”€ auth.js ← JWT verify + role authorize +β”‚ └── validate.js ← express-validator error handler +β”‚ +β”œβ”€β”€ frontend/ +β”‚ └── src/ +β”‚ β”œβ”€β”€ App.jsx ← all routes defined here +β”‚ β”œβ”€β”€ api/index.js ← axios instance + all API helpers +β”‚ β”œβ”€β”€ context/ +β”‚ β”‚ β”œβ”€β”€ AuthContext.jsx +β”‚ β”‚ └── CartContext.jsx +β”‚ β”œβ”€β”€ components/ +β”‚ β”‚ β”œβ”€β”€ Layout/ ← Sidebar, Navbar, ProtectedRoute +β”‚ β”‚ β”œβ”€β”€ UI/ ← Modal, Badge, StatCard, SearchBar, ConfirmDialog, Pagination, CrudPage +β”‚ β”‚ └── Bill/ ← BillPreview, BillActions +β”‚ └── pages/ +β”‚ β”œβ”€β”€ Login.jsx +β”‚ β”œβ”€β”€ Dashboard.jsx +β”‚ β”œβ”€β”€ Inventory/ ← MedicineList, StockList +β”‚ β”œβ”€β”€ Sales/ ← NewOrder (POS), OrderList, OrderDetail +β”‚ β”œβ”€β”€ Customers/ +β”‚ β”œβ”€β”€ Suppliers/ +β”‚ β”œβ”€β”€ Doctors/ +β”‚ β”œβ”€β”€ Employees/ +β”‚ β”œβ”€β”€ Prescriptions/ +β”‚ β”œβ”€β”€ Payments/ +β”‚ └── Reports/ ← 4-tab report page with chart + CSV export +β”‚ +└── database/ + β”œβ”€β”€ schema.sql ← 12 tables + 2 auxiliary tables + β”œβ”€β”€ sample_data.sql ← Pakistani context sample data + β”œβ”€β”€ triggers.sql ← 4 triggers + β”œβ”€β”€ stored_procedures.sql ← 4 procedures + β”œβ”€β”€ views.sql ← 7 views + └── indexes.sql ← performance indexes +``` + +--- + +## 🌐 API Endpoints + +### Auth + +| Method | Endpoint | Description | +| ------ | -------------------- | -------------- | +| POST | `/api/v1/auth/login` | Login, get JWT | +| GET | `/api/v1/auth/me` | Current user | + +### Resources (all support GET / GET /:id / POST / PUT /:id / DELETE /:id) + +`/api/v1/categories`, `/api/v1/manufacturers`, `/api/v1/suppliers`, +`/api/v1/medicines`, `/api/v1/doctors`, `/api/v1/customers`, +`/api/v1/employees`, `/api/v1/prescriptions`, `/api/v1/payments` + +### Stock + +| Method | Endpoint | Description | +| ------ | ----------------------- | ------------------ | +| GET | `/api/v1/stock` | All stock records | +| POST | `/api/v1/stock/restock` | Restock a medicine | + +### Orders + +| Method | Endpoint | Description | +| ------ | ----------------------------- | --------------------------------- | +| GET | `/api/v1/orders` | List orders (paginated) | +| POST | `/api/v1/orders` | Place order + optional payment | +| GET | `/api/v1/orders/:id` | Order detail with items | +| GET | `/api/v1/orders/:id/bill` | Full bill (calls GenerateBill SP) | +| POST | `/api/v1/orders/:id/complete` | Complete payment | +| PUT | `/api/v1/orders/:id/cancel` | Cancel pending order | + +### Reports + +| Method | Endpoint | Description | +| ------ | -------------------------------------- | ---------------------- | +| GET | `/api/v1/reports/dashboard-stats` | 6 dashboard KPIs | +| GET | `/api/v1/reports/low-stock` | vw_LowStockAlert | +| GET | `/api/v1/reports/expiring-soon` | vw_ExpiringSoon | +| GET | `/api/v1/reports/sales-summary` | vw_SalesSummary | +| GET | `/api/v1/reports/top-medicines` | vw_TopSellingMedicines | +| GET | `/api/v1/reports/employee-performance` | vw_EmployeePerformance | + +--- + +## πŸ—„ Database Schema (12 Tables) + +| Table | Primary Key | Purpose | +| --------------- | ----------------- | ----------------------------------------- | +| `categories` | `category_id` | Medicine classifications | +| `manufacturers` | `manufacturer_id` | Pharma companies | +| `suppliers` | `supplier_id` | Stock suppliers with ratings | +| `medicines` | `medicine_id` | Core medicine catalog | +| `stock` | `stock_id` | Quantity, batch, expiry per medicine | +| `doctors` | `doctor_id` | Licensed doctors for prescriptions | +| `customers` | `customer_id` | Customer profiles | +| `employees` | `employee_id` | Staff with roles and credentials | +| `prescriptions` | `prescription_id` | Doctor β†’ Customer prescriptions | +| `orders` | `order_id` | Master order with discount/tax | +| `order_items` | `item_id` | Line items (subtotal is GENERATED column) | +| `payments` | `payment_id` | Payment method and status per order | + +--- + +## πŸ“Έ Screenshots + +> _(Add screenshots here after first run)_ + +- `screenshots/login.png` +- `screenshots/dashboard.png` +- `screenshots/pos.png` +- `screenshots/bill-preview.png` +- `screenshots/reports.png` + +--- + +## πŸ‘€ Author + +**Moavia Amir** +Student ID: 2k24_BSAI_72 +BS Artificial Intelligence β€” 4th Semester +Database Lab | Instructor: Sir Ahsan Ahmed diff --git a/msms/backend/.env.example b/msms/backend/.env.example new file mode 100644 index 0000000..d14d343 --- /dev/null +++ b/msms/backend/.env.example @@ -0,0 +1,9 @@ +DB_HOST=localhost +DB_PORT=3306 +DB_USER=root +DB_PASSWORD= +DB_NAME=msms_db +JWT_SECRET=your_super_secret_jwt_key_change_in_production +JWT_EXPIRES_IN=8h +PORT=5000 +NODE_ENV=development diff --git a/msms/backend/db.js b/msms/backend/db.js new file mode 100644 index 0000000..58bd1e4 --- /dev/null +++ b/msms/backend/db.js @@ -0,0 +1,32 @@ +// db.js β€” MySQL2 connection pool +// Student: Moavia Amir | 2k24_BSAI_72 + +const mysql = require('mysql2/promise'); +require('dotenv').config(); + +const pool = mysql.createPool({ + host: process.env.DB_HOST || 'localhost', + port: parseInt(process.env.DB_PORT || '3306'), + user: process.env.DB_USER || 'root', + password: process.env.DB_PASSWORD || '', + database: process.env.DB_NAME || 'msms_db', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, + timezone: '+00:00', + charset: 'utf8mb4', +}); + +// Test connection on startup +(async () => { + try { + const conn = await pool.getConnection(); + console.log('βœ… MySQL connected β€” database:', process.env.DB_NAME || 'msms_db'); + conn.release(); + } catch (err) { + console.error('❌ MySQL connection failed:', err.message); + process.exit(1); + } +})(); + +module.exports = pool; diff --git a/msms/backend/middleware/auth.js b/msms/backend/middleware/auth.js new file mode 100644 index 0000000..e0a9754 --- /dev/null +++ b/msms/backend/middleware/auth.js @@ -0,0 +1,26 @@ +// middleware/auth.js β€” JWT authentication middleware +const jwt = require('jsonwebtoken'); + +const authenticate = (req, res, next) => { + const header = req.headers.authorization; + if (!header || !header.startsWith('Bearer ')) { + return res.status(401).json({ error: 'No token provided. Please log in.' }); + } + const token = header.split(' ')[1]; + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); + } catch { + return res.status(401).json({ error: 'Invalid or expired token.' }); + } +}; + +const authorize = (...roles) => (req, res, next) => { + if (!roles.includes(req.user?.role)) { + return res.status(403).json({ error: 'Access denied: insufficient permissions.' }); + } + next(); +}; + +module.exports = { authenticate, authorize }; diff --git a/msms/backend/middleware/validate.js b/msms/backend/middleware/validate.js new file mode 100644 index 0000000..c0c86fd --- /dev/null +++ b/msms/backend/middleware/validate.js @@ -0,0 +1,12 @@ +// middleware/validate.js β€” express-validator error handler +const { validationResult } = require('express-validator'); + +const validate = (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + next(); +}; + +module.exports = validate; diff --git a/msms/backend/package-lock.json b/msms/backend/package-lock.json new file mode 100644 index 0000000..660dd19 --- /dev/null +++ b/msms/backend/package-lock.json @@ -0,0 +1,1607 @@ +{ + "name": "msms-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "msms-backend", + "version": "1.0.0", + "dependencies": { + "bcryptjs": "^3.0.3", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-validator": "^7.0.1", + "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "mysql2": "^3.6.5" + }, + "devDependencies": { + "nodemon": "^3.0.2" + } + }, + "node_modules/@types/node": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.19.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-validator": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.3.2.tgz", + "integrity": "sha512-ctLw1Vl6dXVH62dIQMDdTAQkrh480mkFuG6/SGXOaVlwPNukhRAe7EgJIMJ2TSAni8iwHBRp530zAZE5ZPF2IA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.18.1", + "validator": "~13.15.23" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", + "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/morgan": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/mysql2": { + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.3.tgz", + "integrity": "sha512-uWWxvZSRvRhtBdh2CdcuK83YcOfPdmEeEYB069bAmPnV93QApDGVPuvCQOLjlh7tYHEWdgQPrn6kosDxHBVLkA==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.3.3" + }, + "engines": { + "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sql-escaper": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz", + "integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "license": "MIT", + "peer": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/msms/backend/package.json b/msms/backend/package.json new file mode 100644 index 0000000..39f3084 --- /dev/null +++ b/msms/backend/package.json @@ -0,0 +1,24 @@ +{ + "name": "msms-backend", + "version": "1.0.0", + "description": "MSMS Backend β€” Node.js + Express + MySQL", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js" + }, + "dependencies": { + "bcryptjs": "^3.0.3", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-validator": "^7.0.1", + "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "mysql2": "^3.6.5" + }, + "devDependencies": { + "nodemon": "^3.0.2" + } +} diff --git a/msms/backend/routes/auth.js b/msms/backend/routes/auth.js new file mode 100644 index 0000000..68ab07a --- /dev/null +++ b/msms/backend/routes/auth.js @@ -0,0 +1,80 @@ +// routes/auth.js +const express = require("express"); +const bcrypt = require("bcryptjs"); +const jwt = require("jsonwebtoken"); +const { body } = require("express-validator"); +const pool = require("../db"); +const validate = require("../middleware/validate"); + +const router = express.Router(); + +// POST /api/v1/auth/login +router.post( + "/login", + [ + body("username").trim().notEmpty().withMessage("Username is required"), + body("password").notEmpty().withMessage("Password is required"), + ], + validate, + async (req, res) => { + try { + const { username, password } = req.body; + const [rows] = await pool.query( + "SELECT * FROM employees WHERE username = ? AND is_active = 1", + [username], + ); + if (!rows.length) { + return res.status(401).json({ error: "Invalid credentials." }); + } + const employee = rows[0]; + // Debug log for diagnosis + console.log("DEBUG LOGIN:", { + username, + password, + hash: employee.password_hash, + }); + const match = await bcrypt.compare(password, employee.password_hash); + console.log("DEBUG BCRYPT RESULT:", match); + if (!match) { + return res.status(401).json({ error: "Invalid credentials." }); + } + const token = jwt.sign( + { + id: employee.employee_id, + username: employee.username, + role: employee.role, + name: employee.name, + }, + process.env.JWT_SECRET, + { expiresIn: process.env.JWT_EXPIRES_IN || "8h" }, + ); + const { password_hash, ...safe } = employee; + res.json({ token, user: safe }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Internal server error" }); + } + }, +); + +// GET /api/v1/auth/me +router.get( + "/me", + require("../middleware/auth").authenticate, + async (req, res) => { + try { + const [rows] = await pool.query( + "SELECT employee_id,name,role,phone,email,username,hire_date FROM employees WHERE employee_id = ?", + [req.user.id], + ); + if (!rows.length) + return res.status(404).json({ error: "User not found" }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Internal server error" }); + } + }, +); + +module.exports = router; diff --git a/msms/backend/routes/categories.js b/msms/backend/routes/categories.js new file mode 100644 index 0000000..3772bc4 --- /dev/null +++ b/msms/backend/routes/categories.js @@ -0,0 +1,91 @@ +// routes/categories.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +// GET /api/v1/categories +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 100 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT c.*, COUNT(m.medicine_id) AS medicine_count + FROM categories c + LEFT JOIN medicines m ON m.category_id = c.category_id + WHERE c.category_name LIKE ? + GROUP BY c.category_id + ORDER BY c.category_name + LIMIT ? OFFSET ?`, + [like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + 'SELECT COUNT(*) AS total FROM categories WHERE category_name LIKE ?', [like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/categories/:id +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM categories WHERE category_id = ?', [req.params.id]); + if (!rows.length) return res.status(404).json({ error: 'Category not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// POST /api/v1/categories +router.post('/', + [body('category_name').trim().notEmpty().withMessage('Category name is required').isLength({ max: 100 })], + validate, + async (req, res) => { + try { + const { category_name, description } = req.body; + const [result] = await pool.query( + 'INSERT INTO categories (category_name, description) VALUES (?, ?)', + [category_name, description || null] + ); + res.status(201).json({ message: 'Category created', category_id: result.insertId }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'Category name already exists' }); + console.error(err); res.status(500).json({ error: 'Internal server error' }); + } + } +); + +// PUT /api/v1/categories/:id +router.put('/:id', + [body('category_name').trim().notEmpty().withMessage('Category name is required')], + validate, + async (req, res) => { + try { + const { category_name, description } = req.body; + const [result] = await pool.query( + 'UPDATE categories SET category_name = ?, description = ? WHERE category_id = ?', + [category_name, description || null, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Category not found' }); + res.json({ message: 'Category updated' }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'Category name already exists' }); + console.error(err); res.status(500).json({ error: 'Internal server error' }); + } + } +); + +// DELETE /api/v1/categories/:id +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query('DELETE FROM categories WHERE category_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Category not found' }); + res.json({ message: 'Category deleted' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/customers.js b/msms/backend/routes/customers.js new file mode 100644 index 0000000..86a62e4 --- /dev/null +++ b/msms/backend/routes/customers.js @@ -0,0 +1,103 @@ +// routes/customers.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT c.*, COUNT(o.order_id) AS total_orders, + COALESCE(SUM(o.total_amount),0) AS total_spent + FROM customers c + LEFT JOIN orders o ON o.customer_id = c.customer_id + WHERE c.name LIKE ? OR c.phone LIKE ? OR c.email LIKE ? + GROUP BY c.customer_id + ORDER BY c.name LIMIT ? OFFSET ?`, + [like, like, like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + 'SELECT COUNT(*) AS total FROM customers WHERE name LIKE ? OR phone LIKE ? OR email LIKE ?', + [like, like, like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/search/phone', async (req, res) => { + try { + const { phone } = req.query; + const [rows] = await pool.query( + 'SELECT * FROM customers WHERE phone LIKE ? LIMIT 10', [`%${phone}%`] + ); + res.json(rows); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM customers WHERE customer_id = ?', [req.params.id]); + if (!rows.length) return res.status(404).json({ error: 'Customer not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.post('/', + [ + body('name').trim().notEmpty().withMessage('Name is required'), + body('phone').trim().notEmpty().withMessage('Phone is required'), + body('email').optional({ checkFalsy: true }).isEmail().withMessage('Invalid email'), + body('gender').optional().isIn(['Male','Female','Other']), + ], + validate, + async (req, res) => { + try { + const { name, phone, email, date_of_birth, gender, address } = req.body; + const [result] = await pool.query( + 'INSERT INTO customers (name, phone, email, date_of_birth, gender, address) VALUES (?,?,?,?,?,?)', + [name, phone, email||null, date_of_birth||null, gender||null, address||null] + ); + res.status(201).json({ message: 'Customer created', customer_id: result.insertId }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'Phone number already exists' }); + console.error(err); res.status(500).json({ error: 'Internal server error' }); + } + } +); + +router.put('/:id', + [ + body('name').trim().notEmpty(), + body('phone').trim().notEmpty(), + body('email').optional({ checkFalsy: true }).isEmail(), + ], + validate, + async (req, res) => { + try { + const { name, phone, email, date_of_birth, gender, address } = req.body; + const [result] = await pool.query( + 'UPDATE customers SET name=?,phone=?,email=?,date_of_birth=?,gender=?,address=? WHERE customer_id=?', + [name, phone, email||null, date_of_birth||null, gender||null, address||null, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Customer not found' }); + res.json({ message: 'Customer updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query('DELETE FROM customers WHERE customer_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Customer not found' }); + res.json({ message: 'Customer deleted' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/doctors.js b/msms/backend/routes/doctors.js new file mode 100644 index 0000000..6086a53 --- /dev/null +++ b/msms/backend/routes/doctors.js @@ -0,0 +1,82 @@ +// routes/doctors.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT * FROM doctors WHERE (name LIKE ? OR specialization LIKE ? OR license_number LIKE ?) AND is_active = 1 + ORDER BY name LIMIT ? OFFSET ?`, + [like, like, like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + 'SELECT COUNT(*) AS total FROM doctors WHERE (name LIKE ? OR specialization LIKE ?) AND is_active = 1', + [like, like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM doctors WHERE doctor_id = ?', [req.params.id]); + if (!rows.length) return res.status(404).json({ error: 'Doctor not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.post('/', + [ + body('name').trim().notEmpty().withMessage('Name required'), + body('license_number').trim().notEmpty().withMessage('License number required'), + ], + validate, + async (req, res) => { + try { + const { name, specialization, license_number, phone, hospital } = req.body; + const [result] = await pool.query( + 'INSERT INTO doctors (name, specialization, license_number, phone, hospital) VALUES (?,?,?,?,?)', + [name, specialization||null, license_number, phone||null, hospital||null] + ); + res.status(201).json({ message: 'Doctor created', doctor_id: result.insertId }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'License number already exists' }); + console.error(err); res.status(500).json({ error: 'Internal server error' }); + } + } +); + +router.put('/:id', + [body('name').trim().notEmpty(), body('license_number').trim().notEmpty()], + validate, + async (req, res) => { + try { + const { name, specialization, license_number, phone, hospital, is_active } = req.body; + const [result] = await pool.query( + 'UPDATE doctors SET name=?,specialization=?,license_number=?,phone=?,hospital=?,is_active=? WHERE doctor_id=?', + [name, specialization||null, license_number, phone||null, hospital||null, is_active !== undefined ? (is_active?1:0) : 1, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Doctor not found' }); + res.json({ message: 'Doctor updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query('UPDATE doctors SET is_active = 0 WHERE doctor_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Doctor not found' }); + res.json({ message: 'Doctor deactivated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/employees.js b/msms/backend/routes/employees.js new file mode 100644 index 0000000..e3161eb --- /dev/null +++ b/msms/backend/routes/employees.js @@ -0,0 +1,109 @@ +// routes/employees.js +const express = require('express'); +const bcrypt = require('bcryptjs'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate, authorize } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +const ROLES = ['Pharmacist','Cashier','Manager','Admin']; + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT employee_id, name, role, phone, email, salary, hire_date, is_active, username + FROM employees + WHERE (name LIKE ? OR role LIKE ? OR username LIKE ?) AND is_active = 1 + ORDER BY name LIMIT ? OFFSET ?`, + [like, like, like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + 'SELECT COUNT(*) AS total FROM employees WHERE (name LIKE ? OR role LIKE ?) AND is_active = 1', [like, like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query( + 'SELECT employee_id, name, role, phone, email, salary, hire_date, is_active, username FROM employees WHERE employee_id = ?', + [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: 'Employee not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.post('/', + authorize('Admin', 'Manager'), + [ + body('name').trim().notEmpty().withMessage('Name required'), + body('role').isIn(ROLES).withMessage('Invalid role'), + body('phone').trim().notEmpty().withMessage('Phone required'), + body('username').trim().notEmpty().isLength({ min: 3 }).withMessage('Username min 3 chars'), + body('password').isLength({ min: 6 }).withMessage('Password min 6 chars'), + body('salary').optional().isFloat({ gt: 0 }), + body('email').optional({ checkFalsy: true }).isEmail(), + body('hire_date').isDate().withMessage('Valid hire date required'), + ], + validate, + async (req, res) => { + try { + const { name, role, phone, email, salary, hire_date, username, password } = req.body; + const hash = await bcrypt.hash(password, 12); + const [result] = await pool.query( + 'INSERT INTO employees (name,role,phone,email,salary,hire_date,username,password_hash) VALUES (?,?,?,?,?,?,?,?)', + [name, role, phone, email||null, salary||null, hire_date, username, hash] + ); + res.status(201).json({ message: 'Employee created', employee_id: result.insertId }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'Phone, email, or username already exists' }); + console.error(err); res.status(500).json({ error: 'Internal server error' }); + } + } +); + +router.put('/:id', + authorize('Admin', 'Manager'), + [ + body('name').trim().notEmpty(), + body('role').isIn(ROLES), + body('phone').trim().notEmpty(), + ], + validate, + async (req, res) => { + try { + const { name, role, phone, email, salary, hire_date, is_active, password } = req.body; + if (password) { + const hash = await bcrypt.hash(password, 12); + await pool.query( + 'UPDATE employees SET name=?,role=?,phone=?,email=?,salary=?,hire_date=?,is_active=?,password_hash=? WHERE employee_id=?', + [name, role, phone, email||null, salary||null, hire_date, is_active!==undefined?(is_active?1:0):1, hash, req.params.id] + ); + } else { + await pool.query( + 'UPDATE employees SET name=?,role=?,phone=?,email=?,salary=?,hire_date=?,is_active=? WHERE employee_id=?', + [name, role, phone, email||null, salary||null, hire_date, is_active!==undefined?(is_active?1:0):1, req.params.id] + ); + } + res.json({ message: 'Employee updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.delete('/:id', authorize('Admin'), async (req, res) => { + try { + const [result] = await pool.query('UPDATE employees SET is_active = 0 WHERE employee_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Employee not found' }); + res.json({ message: 'Employee deactivated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/manufacturers.js b/msms/backend/routes/manufacturers.js new file mode 100644 index 0000000..7e2f3fb --- /dev/null +++ b/msms/backend/routes/manufacturers.js @@ -0,0 +1,80 @@ +// routes/manufacturers.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT * FROM manufacturers WHERE name LIKE ? OR country LIKE ? ORDER BY name LIMIT ? OFFSET ?`, + [like, like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + 'SELECT COUNT(*) AS total FROM manufacturers WHERE name LIKE ? OR country LIKE ?', [like, like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM manufacturers WHERE manufacturer_id = ?', [req.params.id]); + if (!rows.length) return res.status(404).json({ error: 'Manufacturer not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.post('/', + [ + body('name').trim().notEmpty().withMessage('Name is required'), + body('contact_email').optional().isEmail().withMessage('Invalid email'), + ], + validate, + async (req, res) => { + try { + const { name, country, contact_email, phone, address } = req.body; + const [result] = await pool.query( + 'INSERT INTO manufacturers (name, country, contact_email, phone, address) VALUES (?,?,?,?,?)', + [name, country||null, contact_email||null, phone||null, address||null] + ); + res.status(201).json({ message: 'Manufacturer created', manufacturer_id: result.insertId }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') return res.status(409).json({ error: 'Duplicate entry' }); + console.error(err); res.status(500).json({ error: 'Internal server error' }); + } + } +); + +router.put('/:id', + [body('name').trim().notEmpty()], + validate, + async (req, res) => { + try { + const { name, country, contact_email, phone, address } = req.body; + const [result] = await pool.query( + 'UPDATE manufacturers SET name=?,country=?,contact_email=?,phone=?,address=? WHERE manufacturer_id=?', + [name, country||null, contact_email||null, phone||null, address||null, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Manufacturer not found' }); + res.json({ message: 'Manufacturer updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query('DELETE FROM manufacturers WHERE manufacturer_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Manufacturer not found' }); + res.json({ message: 'Manufacturer deleted' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/medicines.js b/msms/backend/routes/medicines.js new file mode 100644 index 0000000..89899e9 --- /dev/null +++ b/msms/backend/routes/medicines.js @@ -0,0 +1,134 @@ +// routes/medicines.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +const DOSAGE_FORMS = ['Tablet','Capsule','Syrup','Injection','Cream','Drop','Inhaler','Powder','Patch']; + +// GET /api/v1/medicines +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10, category_id, active } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + + let where = 'WHERE (m.name LIKE ? OR m.generic_name LIKE ?)'; + const params = [like, like]; + + if (category_id) { where += ' AND m.category_id = ?'; params.push(category_id); } + if (active !== undefined) { where += ' AND m.is_active = ?'; params.push(active === 'true' ? 1 : 0); } + + const [rows] = await pool.query( + `SELECT m.*, c.category_name, mfr.name AS manufacturer_name, + s.quantity AS stock_qty, s.expiry_date, s.reorder_level, + CASE WHEN s.quantity IS NULL OR s.quantity = 0 THEN 'Out of Stock' + WHEN s.quantity < s.reorder_level THEN 'Low Stock' + ELSE 'In Stock' END AS stock_status + FROM medicines m + LEFT JOIN categories c ON c.category_id = m.category_id + LEFT JOIN manufacturers mfr ON mfr.manufacturer_id = m.manufacturer_id + LEFT JOIN stock s ON s.medicine_id = m.medicine_id + ${where} + ORDER BY m.name + LIMIT ? OFFSET ?`, + [...params, parseInt(limit), offset] + ); + + const [[{ total }]] = await pool.query( + `SELECT COUNT(*) AS total FROM medicines m ${where}`, params + ); + + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/medicines/:id +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query( + `SELECT m.*, c.category_name, mfr.name AS manufacturer_name, + s.quantity AS stock_qty, s.expiry_date, s.reorder_level, s.batch_number, + s.manufacturing_date, s.last_restocked, sup.name AS supplier_name + FROM medicines m + LEFT JOIN categories c ON c.category_id = m.category_id + LEFT JOIN manufacturers mfr ON mfr.manufacturer_id = m.manufacturer_id + LEFT JOIN stock s ON s.medicine_id = m.medicine_id + LEFT JOIN suppliers sup ON sup.supplier_id = s.supplier_id + WHERE m.medicine_id = ?`, + [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: 'Medicine not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// POST /api/v1/medicines +router.post('/', + [ + body('name').trim().notEmpty().isLength({ min: 2 }).withMessage('Name min 2 chars'), + body('dosage_form').isIn(DOSAGE_FORMS).withMessage('Invalid dosage form'), + body('unit_price').isFloat({ gt: 0 }).withMessage('Price must be > 0'), + body('category_id').optional().isInt({ gt: 0 }), + body('manufacturer_id').optional().isInt({ gt: 0 }), + body('requires_prescription').optional().isBoolean(), + ], + validate, + async (req, res) => { + try { + const { name, generic_name, category_id, manufacturer_id, dosage_form, + strength, unit_price, requires_prescription, description } = req.body; + const [result] = await pool.query( + `INSERT INTO medicines + (name, generic_name, category_id, manufacturer_id, dosage_form, strength, unit_price, requires_prescription, description) + VALUES (?,?,?,?,?,?,?,?,?)`, + [name, generic_name||null, category_id||null, manufacturer_id||null, + dosage_form, strength||null, unit_price, requires_prescription ? 1 : 0, description||null] + ); + res.status(201).json({ message: 'Medicine created', medicine_id: result.insertId }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +// PUT /api/v1/medicines/:id +router.put('/:id', + [ + body('name').trim().notEmpty().isLength({ min: 2 }), + body('dosage_form').isIn(DOSAGE_FORMS), + body('unit_price').isFloat({ gt: 0 }), + ], + validate, + async (req, res) => { + try { + const { name, generic_name, category_id, manufacturer_id, dosage_form, + strength, unit_price, requires_prescription, description, is_active } = req.body; + const [result] = await pool.query( + `UPDATE medicines SET name=?, generic_name=?, category_id=?, manufacturer_id=?, + dosage_form=?, strength=?, unit_price=?, requires_prescription=?, description=?, is_active=? + WHERE medicine_id=?`, + [name, generic_name||null, category_id||null, manufacturer_id||null, + dosage_form, strength||null, unit_price, requires_prescription ? 1 : 0, + description||null, is_active !== undefined ? (is_active ? 1 : 0) : 1, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Medicine not found' }); + res.json({ message: 'Medicine updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +// DELETE /api/v1/medicines/:id (soft delete) +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query( + 'UPDATE medicines SET is_active = 0 WHERE medicine_id = ?', [req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Medicine not found' }); + res.json({ message: 'Medicine deactivated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/orders.js b/msms/backend/routes/orders.js new file mode 100644 index 0000000..9a1649a --- /dev/null +++ b/msms/backend/routes/orders.js @@ -0,0 +1,195 @@ +// routes/orders.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +// GET /api/v1/orders +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10, status } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + + let where = 'WHERE (COALESCE(c.name,"Walk-in") LIKE ? OR CONCAT("ORD-",LPAD(o.order_id,5,"0")) LIKE ?)'; + const params = [like, like]; + if (status) { where += ' AND o.status = ?'; params.push(status); } + + const [rows] = await pool.query( + `SELECT o.*, COALESCE(c.name,'Walk-in Customer') AS customer_name, + c.phone AS customer_phone, e.name AS employee_name, + COUNT(oi.item_id) AS item_count, + CONCAT('ORD-',LPAD(o.order_id,5,'0')) AS invoice_number + FROM orders o + LEFT JOIN customers c ON c.customer_id = o.customer_id + JOIN employees e ON e.employee_id = o.employee_id + LEFT JOIN order_items oi ON oi.order_id = o.order_id + ${where} + GROUP BY o.order_id + ORDER BY o.order_date DESC LIMIT ? OFFSET ?`, + [...params, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + `SELECT COUNT(DISTINCT o.order_id) AS total + FROM orders o + LEFT JOIN customers c ON c.customer_id = o.customer_id + ${where}`, params + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/orders/:id +router.get('/:id', async (req, res) => { + try { + const [[order]] = await pool.query( + `SELECT o.*, COALESCE(c.name,'Walk-in Customer') AS customer_name, c.phone AS customer_phone, + e.name AS employee_name, e.role AS employee_role, + CONCAT('ORD-',LPAD(o.order_id,5,'0')) AS invoice_number, + p.prescription_date, d.name AS doctor_name + FROM orders o + LEFT JOIN customers c ON c.customer_id = o.customer_id + JOIN employees e ON e.employee_id = o.employee_id + LEFT JOIN prescriptions p ON p.prescription_id = o.prescription_id + LEFT JOIN doctors d ON d.doctor_id = p.doctor_id + WHERE o.order_id = ?`, [req.params.id] + ); + if (!order) return res.status(404).json({ error: 'Order not found' }); + + const [items] = await pool.query( + `SELECT oi.*, m.name AS medicine_name, m.dosage_form, m.strength + FROM order_items oi + JOIN medicines m ON m.medicine_id = oi.medicine_id + WHERE oi.order_id = ?`, [req.params.id] + ); + + const [[payment]] = await pool.query( + 'SELECT * FROM payments WHERE order_id = ?', [req.params.id] + ); + + res.json({ ...order, items, payment: payment || null }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/orders/:id/bill β€” calls GenerateBill stored procedure +router.get('/:id/bill', async (req, res) => { + try { + const [results] = await pool.query('CALL GenerateBill(?)', [req.params.id]); + if (!results[0]?.length) return res.status(404).json({ error: 'Order not found' }); + res.json({ header: results[0][0], items: results[1] }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// POST /api/v1/orders β€” place a new order with items +router.post('/', + [ + body('employee_id').isInt({ gt: 0 }).withMessage('Employee ID required'), + body('items').isArray({ min: 1 }).withMessage('At least one item required'), + body('items.*.medicine_id').isInt({ gt: 0 }), + body('items.*.quantity').isInt({ gt: 0 }), + body('items.*.unit_price').isFloat({ gt: 0 }), + body('discount_percent').optional().isFloat({ min: 0, max: 100 }), + body('tax_percent').optional().isFloat({ min: 0 }), + ], + validate, + async (req, res) => { + const conn = await pool.getConnection(); + try { + await conn.beginTransaction(); + + const { customer_id, employee_id, prescription_id, discount_percent = 0, + tax_percent = 0, notes, items, payment_method, amount_paid, transaction_ref } = req.body; + + // Insert order + const [orderResult] = await conn.query( + `INSERT INTO orders (customer_id, employee_id, prescription_id, status, discount_percent, tax_percent, notes) + VALUES (?,?,?,?,?,?,?)`, + [customer_id||null, employee_id, prescription_id||null, 'Pending', discount_percent, tax_percent, notes||null] + ); + const orderId = orderResult.insertId; + + // Insert items (triggers handle stock deduction + total recalc) + for (const item of items) { + await conn.query( + 'INSERT INTO order_items (order_id, medicine_id, quantity, unit_price) VALUES (?,?,?,?)', + [orderId, item.medicine_id, item.quantity, item.unit_price] + ); + } + + // Process payment if provided + if (payment_method && amount_paid !== undefined) { + // Get updated total + const [[{ total_amount }]] = await conn.query( + 'SELECT total_amount FROM orders WHERE order_id = ?', [orderId] + ); + + if (parseFloat(amount_paid) < parseFloat(total_amount)) { + await conn.rollback(); + conn.release(); + return res.status(400).json({ error: `Amount paid (${amount_paid}) is less than total (${total_amount})` }); + } + + await conn.query( + `INSERT INTO payments (order_id, amount_paid, payment_method, payment_status, transaction_ref, paid_at) + VALUES (?,?,?,'Paid',?,NOW())`, + [orderId, amount_paid, payment_method, transaction_ref||null] + ); + + await conn.query("UPDATE orders SET status='Completed' WHERE order_id=?", [orderId]); + + if (prescription_id) { + await conn.query('UPDATE prescriptions SET is_used=1 WHERE prescription_id=?', [prescription_id]); + } + } + + await conn.commit(); + res.status(201).json({ message: 'Order placed successfully', order_id: orderId }); + } catch (err) { + await conn.rollback(); + console.error(err); + const msg = err.sqlMessage || err.message || 'Internal server error'; + res.status(err.sqlMessage ? 400 : 500).json({ error: msg }); + } finally { + conn.release(); + } + } +); + +// POST /api/v1/orders/:id/complete β€” calls CompletePayment procedure +router.post('/:id/complete', + [ + body('payment_method').isIn(['Cash','Card','Online','Insurance']).withMessage('Invalid payment method'), + body('amount_paid').isFloat({ gt: 0 }).withMessage('Amount must be > 0'), + ], + validate, + async (req, res) => { + try { + const { payment_method, amount_paid, transaction_ref } = req.body; + await pool.query('CALL CompletePayment(?,?,?,?)', + [req.params.id, payment_method, amount_paid, transaction_ref||null] + ); + res.json({ message: 'Payment completed successfully' }); + } catch (err) { + console.error(err); + const msg = err.sqlMessage || err.message || 'Internal server error'; + res.status(400).json({ error: msg }); + } + } +); + +// PUT /api/v1/orders/:id/cancel +router.put('/:id/cancel', async (req, res) => { + try { + const [result] = await pool.query( + "UPDATE orders SET status='Cancelled' WHERE order_id=? AND status='Pending'", [req.params.id] + ); + if (!result.affectedRows) return res.status(400).json({ error: 'Order cannot be cancelled (not found or already processed)' }); + res.json({ message: 'Order cancelled' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/payments.js b/msms/backend/routes/payments.js new file mode 100644 index 0000000..df87d8b --- /dev/null +++ b/msms/backend/routes/payments.js @@ -0,0 +1,58 @@ +// routes/payments.js +const express = require('express'); +const pool = require('../db'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10, method, status } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + + let where = 'WHERE (CONCAT("ORD-",LPAD(o.order_id,5,"0")) LIKE ? OR COALESCE(c.name,"Walk-in") LIKE ?)'; + const params = [like, like]; + if (method) { where += ' AND py.payment_method = ?'; params.push(method); } + if (status) { where += ' AND py.payment_status = ?'; params.push(status); } + + const [rows] = await pool.query( + `SELECT py.*, o.total_amount, o.order_date, + CONCAT('ORD-',LPAD(o.order_id,5,'0')) AS invoice_number, + COALESCE(c.name,'Walk-in Customer') AS customer_name, + e.name AS cashier_name + FROM payments py + JOIN orders o ON o.order_id = py.order_id + LEFT JOIN customers c ON c.customer_id = o.customer_id + JOIN employees e ON e.employee_id = o.employee_id + ${where} + ORDER BY py.paid_at DESC LIMIT ? OFFSET ?`, + [...params, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + `SELECT COUNT(*) AS total FROM payments py + JOIN orders o ON o.order_id = py.order_id + LEFT JOIN customers c ON c.customer_id = o.customer_id + ${where}`, params + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query( + `SELECT py.*, CONCAT('ORD-',LPAD(o.order_id,5,'0')) AS invoice_number, + o.total_amount, COALESCE(c.name,'Walk-in') AS customer_name + FROM payments py + JOIN orders o ON o.order_id = py.order_id + LEFT JOIN customers c ON c.customer_id = o.customer_id + WHERE py.payment_id = ?`, [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: 'Payment not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/prescriptions.js b/msms/backend/routes/prescriptions.js new file mode 100644 index 0000000..95f5a58 --- /dev/null +++ b/msms/backend/routes/prescriptions.js @@ -0,0 +1,104 @@ +// routes/prescriptions.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10, customer_id } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + + let where = 'WHERE (c.name LIKE ? OR c.phone LIKE ? OR d.name LIKE ?)'; + const params = [like, like, like]; + if (customer_id) { where += ' AND p.customer_id = ?'; params.push(customer_id); } + + const [rows] = await pool.query( + `SELECT p.*, c.name AS customer_name, c.phone AS customer_phone, + d.name AS doctor_name, d.specialization + FROM prescriptions p + JOIN customers c ON c.customer_id = p.customer_id + JOIN doctors d ON d.doctor_id = p.doctor_id + ${where} + ORDER BY p.prescription_date DESC LIMIT ? OFFSET ?`, + [...params, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + `SELECT COUNT(*) AS total FROM prescriptions p + JOIN customers c ON c.customer_id = p.customer_id + JOIN doctors d ON d.doctor_id = p.doctor_id + ${where}`, params + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query( + `SELECT p.*, c.name AS customer_name, c.phone AS customer_phone, + d.name AS doctor_name, d.specialization, d.license_number, d.hospital + FROM prescriptions p + JOIN customers c ON c.customer_id = p.customer_id + JOIN doctors d ON d.doctor_id = p.doctor_id + WHERE p.prescription_id = ?`, [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: 'Prescription not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.post('/', + [ + body('customer_id').isInt({ gt: 0 }).withMessage('Valid customer required'), + body('doctor_id').isInt({ gt: 0 }).withMessage('Valid doctor required'), + body('prescription_date').isDate().withMessage('Valid date required'), + body('valid_until').optional().isDate(), + ], + validate, + async (req, res) => { + try { + const { customer_id, doctor_id, prescription_date, valid_until, notes } = req.body; + const [result] = await pool.query( + 'INSERT INTO prescriptions (customer_id, doctor_id, prescription_date, valid_until, notes) VALUES (?,?,?,?,?)', + [customer_id, doctor_id, prescription_date, valid_until||null, notes||null] + ); + res.status(201).json({ message: 'Prescription created', prescription_id: result.insertId }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.put('/:id', + [ + body('customer_id').isInt({ gt: 0 }), + body('doctor_id').isInt({ gt: 0 }), + body('prescription_date').isDate(), + ], + validate, + async (req, res) => { + try { + const { customer_id, doctor_id, prescription_date, valid_until, notes, is_used } = req.body; + const [result] = await pool.query( + 'UPDATE prescriptions SET customer_id=?,doctor_id=?,prescription_date=?,valid_until=?,notes=?,is_used=? WHERE prescription_id=?', + [customer_id, doctor_id, prescription_date, valid_until||null, notes||null, is_used?1:0, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Prescription not found' }); + res.json({ message: 'Prescription updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query('DELETE FROM prescriptions WHERE prescription_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Prescription not found' }); + res.json({ message: 'Prescription deleted' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/reports.js b/msms/backend/routes/reports.js new file mode 100644 index 0000000..d83bc4c --- /dev/null +++ b/msms/backend/routes/reports.js @@ -0,0 +1,78 @@ +// routes/reports.js +const express = require('express'); +const pool = require('../db'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +// GET /api/v1/reports/low-stock +router.get('/low-stock', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM vw_LowStockAlert'); + res.json(rows); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/reports/expiring-soon +router.get('/expiring-soon', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM vw_ExpiringSoon'); + res.json(rows); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/reports/sales-summary +router.get('/sales-summary', async (req, res) => { + try { + const { from, to } = req.query; + let query = 'SELECT * FROM vw_SalesSummary'; + const params = []; + if (from && to) { + query = `SELECT DATE(o.order_date) AS sale_date, COUNT(o.order_id) AS total_orders, + SUM(o.total_amount) AS total_revenue, AVG(o.total_amount) AS avg_order_value, + COUNT(DISTINCT o.customer_id) AS unique_customers + FROM orders o WHERE o.status='Completed' AND DATE(o.order_date) BETWEEN ? AND ? + GROUP BY DATE(o.order_date) ORDER BY sale_date DESC`; + params.push(from, to); + } + const [rows] = await pool.query(query, params); + res.json(rows); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/reports/top-medicines +router.get('/top-medicines', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM vw_TopSellingMedicines LIMIT 20'); + res.json(rows); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/reports/employee-performance +router.get('/employee-performance', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM vw_EmployeePerformance'); + res.json(rows); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// GET /api/v1/reports/dashboard-stats +router.get('/dashboard-stats', async (req, res) => { + try { + const [[stats]] = await pool.query(` + SELECT + (SELECT COUNT(*) FROM medicines WHERE is_active=1) AS total_medicines, + (SELECT COUNT(*) FROM vw_LowStockAlert) AS low_stock_count, + (SELECT COUNT(*) FROM vw_ExpiringSoon) AS expiring_soon_count, + (SELECT COALESCE(SUM(total_amount),0) + FROM orders WHERE status='Completed' + AND DATE(order_date)=CURDATE()) AS today_sales, + (SELECT COUNT(*) FROM customers) AS total_customers, + (SELECT COUNT(*) FROM orders WHERE status='Pending') AS pending_orders + `); + res.json(stats); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/stock.js b/msms/backend/routes/stock.js new file mode 100644 index 0000000..dbc7ee0 --- /dev/null +++ b/msms/backend/routes/stock.js @@ -0,0 +1,72 @@ +// routes/stock.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT s.*, m.name AS medicine_name, m.dosage_form, m.strength, + sup.name AS supplier_name, + CASE WHEN s.quantity = 0 THEN 'Out of Stock' + WHEN s.quantity < s.reorder_level THEN 'Low Stock' + ELSE 'In Stock' END AS stock_status + FROM stock s + JOIN medicines m ON m.medicine_id = s.medicine_id + LEFT JOIN suppliers sup ON sup.supplier_id = s.supplier_id + WHERE m.name LIKE ? + ORDER BY m.name LIMIT ? OFFSET ?`, + [like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + `SELECT COUNT(*) AS total FROM stock s JOIN medicines m ON m.medicine_id = s.medicine_id WHERE m.name LIKE ?`, [like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +// POST /api/v1/stock/restock β€” calls stored procedure +router.post('/restock', + [ + body('medicine_id').isInt({ gt: 0 }).withMessage('Valid medicine_id required'), + body('quantity').isInt({ gt: 0 }).withMessage('Quantity must be > 0'), + body('expiry_date').isDate().withMessage('Valid expiry date required'), + ], + validate, + async (req, res) => { + try { + const { medicine_id, quantity, supplier_id, expiry_date, batch_number } = req.body; + await pool.query( + 'CALL RestockMedicine(?, ?, ?, ?, ?, ?)', + [medicine_id, quantity, supplier_id||null, expiry_date, batch_number||null, req.user.id] + ); + res.json({ message: `Medicine restocked by ${quantity} units.` }); + } catch (err) { + console.error(err); + const msg = err.sqlMessage || err.message || 'Internal server error'; + res.status(400).json({ error: msg }); + } + } +); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query( + `SELECT s.*, m.name AS medicine_name FROM stock s + JOIN medicines m ON m.medicine_id = s.medicine_id + WHERE s.stock_id = ?`, [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: 'Stock record not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/routes/suppliers.js b/msms/backend/routes/suppliers.js new file mode 100644 index 0000000..a2f6595 --- /dev/null +++ b/msms/backend/routes/suppliers.js @@ -0,0 +1,83 @@ +// routes/suppliers.js +const express = require('express'); +const { body } = require('express-validator'); +const pool = require('../db'); +const validate = require('../middleware/validate'); +const { authenticate } = require('../middleware/auth'); + +const router = express.Router(); +router.use(authenticate); + +router.get('/', async (req, res) => { + try { + const { search = '', page = 1, limit = 10 } = req.query; + const offset = (parseInt(page) - 1) * parseInt(limit); + const like = `%${search}%`; + const [rows] = await pool.query( + `SELECT * FROM suppliers WHERE (name LIKE ? OR contact_person LIKE ?) ORDER BY name LIMIT ? OFFSET ?`, + [like, like, parseInt(limit), offset] + ); + const [[{ total }]] = await pool.query( + 'SELECT COUNT(*) AS total FROM suppliers WHERE name LIKE ? OR contact_person LIKE ?', [like, like] + ); + res.json({ data: rows, total, page: parseInt(page), limit: parseInt(limit) }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.get('/:id', async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM suppliers WHERE supplier_id = ?', [req.params.id]); + if (!rows.length) return res.status(404).json({ error: 'Supplier not found' }); + res.json(rows[0]); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +router.post('/', + [ + body('name').trim().notEmpty().withMessage('Name is required'), + body('phone').trim().notEmpty().withMessage('Phone is required'), + body('rating').optional().isFloat({ min: 1, max: 5 }).withMessage('Rating must be 1–5'), + body('email').optional().isEmail(), + ], + validate, + async (req, res) => { + try { + const { name, contact_person, phone, email, address, rating } = req.body; + const [result] = await pool.query( + 'INSERT INTO suppliers (name, contact_person, phone, email, address, rating) VALUES (?,?,?,?,?,?)', + [name, contact_person||null, phone, email||null, address||null, rating||null] + ); + res.status(201).json({ message: 'Supplier created', supplier_id: result.insertId }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.put('/:id', + [ + body('name').trim().notEmpty(), + body('phone').trim().notEmpty(), + body('rating').optional().isFloat({ min: 1, max: 5 }), + ], + validate, + async (req, res) => { + try { + const { name, contact_person, phone, email, address, rating, is_active } = req.body; + const [result] = await pool.query( + 'UPDATE suppliers SET name=?,contact_person=?,phone=?,email=?,address=?,rating=?,is_active=? WHERE supplier_id=?', + [name, contact_person||null, phone, email||null, address||null, rating||null, is_active !== undefined ? (is_active ? 1 : 0) : 1, req.params.id] + ); + if (!result.affectedRows) return res.status(404).json({ error: 'Supplier not found' }); + res.json({ message: 'Supplier updated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } + } +); + +router.delete('/:id', async (req, res) => { + try { + const [result] = await pool.query('UPDATE suppliers SET is_active = 0 WHERE supplier_id = ?', [req.params.id]); + if (!result.affectedRows) return res.status(404).json({ error: 'Supplier not found' }); + res.json({ message: 'Supplier deactivated' }); + } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } +}); + +module.exports = router; diff --git a/msms/backend/server.js b/msms/backend/server.js new file mode 100644 index 0000000..854ee54 --- /dev/null +++ b/msms/backend/server.js @@ -0,0 +1,51 @@ +// server.js β€” MSMS Express Application +// Student: Moavia Amir | 2k24_BSAI_72 + +require('dotenv').config(); +const express = require('express'); +const cors = require('cors'); +const helmet = require('helmet'); +const morgan = require('morgan'); + +const app = express(); + +// ─── Middleware ─────────────────────────────────────────── +app.use(helmet()); +app.use(cors({ origin: process.env.CLIENT_ORIGIN || 'http://localhost:5173', credentials: true })); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); +if (process.env.NODE_ENV !== 'test') app.use(morgan('dev')); + +// ─── Routes ────────────────────────────────────────────── +app.use('/api/v1/auth', require('./routes/auth')); +app.use('/api/v1/categories', require('./routes/categories')); +app.use('/api/v1/manufacturers', require('./routes/manufacturers')); +app.use('/api/v1/suppliers', require('./routes/suppliers')); +app.use('/api/v1/medicines', require('./routes/medicines')); +app.use('/api/v1/stock', require('./routes/stock')); +app.use('/api/v1/doctors', require('./routes/doctors')); +app.use('/api/v1/customers', require('./routes/customers')); +app.use('/api/v1/employees', require('./routes/employees')); +app.use('/api/v1/prescriptions', require('./routes/prescriptions')); +app.use('/api/v1/orders', require('./routes/orders')); +app.use('/api/v1/payments', require('./routes/payments')); +app.use('/api/v1/reports', require('./routes/reports')); + +// ─── Health check ──────────────────────────────────────── +app.get('/api/v1/health', (_req, res) => res.json({ status: 'ok', timestamp: new Date() })); + +// ─── 404 handler ───────────────────────────────────────── +app.use((_req, res) => res.status(404).json({ error: 'Route not found' })); + +// ─── Global error handler ───────────────────────────────── +app.use((err, _req, res, _next) => { + console.error('Unhandled error:', err); + const status = err.status || 500; + res.status(status).json({ + error: status === 500 ? 'Internal server error' : err.message, + }); +}); + +// ─── Start ──────────────────────────────────────────────── +const PORT = process.env.PORT || 5000; +app.listen(PORT, () => console.log(`πŸš€ MSMS API running on http://localhost:${PORT}`)); diff --git a/msms/database/indexes.sql b/msms/database/indexes.sql new file mode 100644 index 0000000..67947a2 --- /dev/null +++ b/msms/database/indexes.sql @@ -0,0 +1,38 @@ +-- ============================================================ +-- Medical Store Management System (MSMS) +-- indexes.sql β€” Performance Indexes +-- Student: Moavia Amir | ID: 2k24_BSAI_72 +-- ============================================================ + +USE msms_db; + +-- Medicines +CREATE INDEX idx_medicine_name ON medicines(name); +CREATE INDEX idx_medicine_category ON medicines(category_id); +CREATE INDEX idx_medicine_active ON medicines(is_active); + +-- Stock +CREATE INDEX idx_stock_expiry ON stock(expiry_date); +CREATE INDEX idx_stock_qty ON stock(quantity); + +-- Customers +CREATE INDEX idx_customer_phone ON customers(phone); +CREATE INDEX idx_customer_name ON customers(name); + +-- Orders +CREATE INDEX idx_order_date ON orders(order_date); +CREATE INDEX idx_order_status ON orders(status); +CREATE INDEX idx_order_customer ON orders(customer_id); + +-- Order Items +CREATE INDEX idx_item_order ON order_items(order_id); +CREATE INDEX idx_item_medicine ON order_items(medicine_id); + +-- Payments +CREATE INDEX idx_payment_status ON payments(payment_status); +CREATE INDEX idx_payment_method ON payments(payment_method); + +-- Stock Transactions +CREATE INDEX idx_txn_medicine ON stock_transactions(medicine_id); +CREATE INDEX idx_txn_type ON stock_transactions(txn_type); +CREATE INDEX idx_txn_created ON stock_transactions(created_at); diff --git a/msms/database/sample_data.sql b/msms/database/sample_data.sql new file mode 100644 index 0000000..54ae449 --- /dev/null +++ b/msms/database/sample_data.sql @@ -0,0 +1,194 @@ +-- ============================================================ +-- Medical Store Management System (MSMS) +-- sample_data.sql β€” Realistic Pakistani Sample Data +-- Student: Moavia Amir | ID: 2k24_BSAI_72 +-- ============================================================ + +USE msms_db; + +-- ───────────────────────────────────────── +-- CATEGORIES +-- ───────────────────────────────────────── +INSERT INTO categories (category_name, description) VALUES +('Analgesic', 'Pain relievers and fever reducers'), +('Antibiotic', 'Medicines that fight bacterial infections'), +('Antacid', 'Medicines for acid reflux and stomach issues'), +('Antihistamine', 'Medicines for allergies and hay fever'), +('Cardiovascular', 'Medicines for heart and blood pressure'), +('Antidiabetic', 'Medicines for diabetes management'), +('Respiratory', 'Medicines for asthma, COPD, and respiratory conditions'), +('Dermatological', 'Skin creams, ointments, and topical medicines'), +('Gastrointestinal', 'Medicines for digestive issues'), +('Vitamins', 'Vitamins, minerals, and dietary supplements'), +('Antifungal', 'Medicines for fungal infections'), +('ORS & Rehydration','Oral rehydration solutions and electrolytes'); + +-- ───────────────────────────────────────── +-- MANUFACTURERS +-- ───────────────────────────────────────── +INSERT INTO manufacturers (name, country, contact_email, phone, address) VALUES +('Getz Pharma (Pvt) Ltd', 'Pakistan', 'info@getzpharma.com', '021-35141000', 'Plot 143, Sector 7-A, Industrial Area, Karachi'), +('GSK Pakistan Ltd', 'Pakistan', 'gsk.pakistan@gsk.com', '021-35661891', 'Clifton, Karachi, Sindh'), +('Pfizer Pakistan Ltd', 'Pakistan', 'pfizer.pk@pfizer.com', '021-35293333', 'DHA Phase-IV, Karachi'), +('Searle Pakistan Ltd', 'Pakistan', 'info@searle.com.pk', '021-35650043', 'F-268, S.I.T.E., Karachi'), +('Barrett Hodgson Pakistan', 'Pakistan', 'info@barretthodgson.com', '021-32463121', 'Plot 23, Korangi Industrial Area, Karachi'), +('AGP Limited', 'Pakistan', 'info@agp.com.pk', '021-34322155', 'Plot 16, Sector 22, Korangi Industrial Area, Karachi'), +('Ferozsons Laboratories', 'Pakistan', 'info@ferozsons.com.pk', '042-35761010', '50-Empress Road, Lahore'), +('Reckitt Benckiser Pakistan', 'Pakistan', 'rb.pakistan@rb.com', '021-35660040', 'Karachi, Sindh'), +('Abbott Laboratories Pakistan','Pakistan','abbott.pk@abbott.com', '051-2876000', 'Islamabad, Pakistan'), +('Novartis Pakistan Ltd', 'Pakistan', 'novartis.pk@novartis.com', '021-35610501', 'Clifton, Karachi'); + +-- ───────────────────────────────────────── +-- SUPPLIERS +-- ───────────────────────────────────────── +INSERT INTO suppliers (name, contact_person, phone, email, address, rating, is_active) VALUES +('Getz Pharma Direct', 'Tariq Mehmood', '03001234501', 'tariq@getzpharma.com', 'Karachi, Sindh', 4.8, 1), +('Searle Pakistan Dist.', 'Hamid Ansari', '03001234502', 'hamid@searledist.com', 'Lahore, Punjab', 4.5, 1), +('Barrett Hodgson Supply', 'Nadia Siddiqui', '03001234503', 'nadia@bhsupply.com.pk', 'Karachi, Sindh', 4.2, 1), +('AGP Distributors', 'Kashif Ali', '03001234504', 'kashif@agpdist.com', 'Karachi, Sindh', 4.6, 1), +('MedLink Supplies', 'Usman Ghani', '03001234505', 'usman@medlink.pk', 'Hyderabad, Sindh', 4.0, 1), +('PharmaHub Karachi', 'Saima Akhtar', '03001234506', 'saima@pharmahub.pk', 'Karachi, Sindh', 3.8, 1), +('HealthSource Pakistan', 'Bilal Raza', '03001234507', 'bilal@healthsource.pk', 'Rawalpindi, Punjab', 4.3, 1), +('National Pharma Trading', 'Aamir Liaquat', '03001234508', 'aamir@npt.com.pk', 'Islamabad, ICT', 4.1, 1), +('City Pharma Supplies', 'Farida Khatoon', '03001234509', 'farida@citypharma.pk', 'Faisalabad, Punjab', 3.9, 1), +('Sunrise Medical Dist.', 'Irfan Sheikh', '03001234510', 'irfan@sunrisemed.pk', 'Multan, Punjab', 4.4, 1); + +-- ───────────────────────────────────────── +-- MEDICINES +-- ───────────────────────────────────────── +INSERT INTO medicines (name, generic_name, category_id, manufacturer_id, dosage_form, strength, unit_price, requires_prescription, description, is_active) VALUES +('Panadol Extra', 'Paracetamol + Caffeine', 1, 1, 'Tablet', '500mg/65mg', 25.00, 0, 'Fast-acting pain reliever with caffeine', 1), +('Brufen', 'Ibuprofen', 1, 2, 'Tablet', '400mg', 35.00, 0, 'Anti-inflammatory pain reliever', 1), +('Ponstan', 'Mefenamic Acid', 1, 3, 'Capsule', '500mg', 45.00, 0, 'Pain relief for menstrual and dental pain', 1), +('Disprin', 'Aspirin', 1, 8, 'Tablet', '300mg', 15.00, 0, 'Pain relief and antiplatelet agent', 1), +('Augmentin', 'Amoxicillin+Clavulanate',2, 2, 'Tablet', '625mg', 185.00, 1, 'Broad-spectrum antibiotic combination', 1), +('Amoxil', 'Amoxicillin', 2, 3, 'Capsule', '500mg', 95.00, 1, 'Penicillin-type antibiotic', 1), +('Flagyl', 'Metronidazole', 2, 4, 'Tablet', '400mg', 55.00, 1, 'Antibiotic for anaerobic infections', 1), +('Cipro', 'Ciprofloxacin', 2, 5, 'Tablet', '500mg', 120.00, 1, 'Fluoroquinolone antibiotic', 1), +('Nexium', 'Esomeprazole', 3, 2, 'Tablet', '20mg', 145.00, 1, 'Proton pump inhibitor for acid reflux', 1), +('Gaviscon', 'Sodium Alginate', 3, 8, 'Tablet', '500mg', 85.00, 0, 'Antacid for heartburn and indigestion', 1), +('Clarityne', 'Loratadine', 4, 5, 'Tablet', '10mg', 65.00, 0, 'Non-drowsy antihistamine for allergies', 1), +('Ventolin', 'Salbutamol', 7, 2, 'Inhaler', '100mcg', 350.00, 1, 'Bronchodilator for asthma relief', 1), +('Betadine', 'Povidone Iodine', 8, 8, 'Drop', '10%', 180.00, 0, 'Antiseptic solution for wounds', 1), +('ORS Sachet', 'ORS Formula', 12, 9, 'Powder', 'Standard', 25.00, 0, 'Oral rehydration salts for dehydration', 1), +('Glucophage', 'Metformin', 6, 10, 'Tablet', '500mg', 75.00, 1, 'First-line antidiabetic medication', 1), +('Amlodipine', 'Amlodipine Besylate', 5, 6, 'Tablet', '5mg', 55.00, 1, 'Calcium channel blocker for hypertension', 1), +('Lasix', 'Furosemide', 5, 1, 'Tablet', '40mg', 40.00, 1, 'Loop diuretic for edema and hypertension', 1), +('Calpol Suspension', 'Paracetamol', 1, 2, 'Syrup', '120mg/5ml', 85.00, 0, 'Paracetamol suspension for children', 1), +('Dermovate Cream', 'Clobetasol Propionate', 8, 2, 'Cream', '0.05%', 220.00, 1, 'Potent corticosteroid for skin conditions', 1), +('Septran DS', 'Co-Trimoxazole', 2, 2, 'Tablet', '960mg', 35.00, 1, 'Sulfonamide antibiotic combination', 1); + +-- ───────────────────────────────────────── +-- STOCK +-- ───────────────────────────────────────── +INSERT INTO stock (medicine_id, supplier_id, quantity, reorder_level, batch_number, manufacturing_date, expiry_date) VALUES +(1, 1, 500, 50, 'GTZ-2024-001', '2024-01-15', '2026-12-31'), +(2, 2, 350, 50, 'GSK-2024-012', '2024-02-10', '2026-10-31'), +(3, 3, 200, 30, 'PFZ-2024-003', '2024-03-01', '2026-09-30'), +(4, 4, 400, 40, 'RCK-2024-007', '2024-01-20', '2026-11-30'), +(5, 2, 150, 25, 'GSK-2024-020', '2024-04-15', '2026-06-30'), +(6, 3, 180, 25, 'PFZ-2024-015', '2024-03-20', '2026-08-31'), +(7, 4, 120, 20, 'SRL-2024-009', '2024-02-28', '2026-07-31'), +(8, 5, 80, 15, 'BHD-2024-011', '2024-05-01', '2026-05-31'), +(9, 2, 90, 15, 'GSK-2024-033', '2024-04-01', '2026-04-30'), +(10, 6, 250, 30, 'RCK-2024-022', '2024-01-10', '2026-12-31'), +(11, 7, 300, 30, 'PFZ-2024-028', '2024-03-15', '2027-03-31'), +(12, 2, 60, 10, 'GSK-2024-041', '2024-06-01', '2026-06-30'), +(13, 6, 150, 20, 'RCK-2024-018', '2024-02-15', '2026-12-31'), +(14, 8, 600, 100, 'ABT-2024-005', '2024-01-01', '2027-06-30'), +(15, 10,200, 30, 'NVT-2024-017', '2024-05-10', '2027-05-31'), +(16, 4, 8, 15, 'AGP-2024-023', '2024-04-20', '2026-04-30'), +(17, 1, 5, 15, 'GTZ-2024-031', '2024-03-10', '2026-03-31'), +(18, 2, 120, 20, 'GSK-2024-038', '2024-06-15', '2026-11-30'), +(19, 2, 70, 10, 'GSK-2024-044', '2024-05-20', '2026-08-31'), +(20, 2, 95, 15, 'GSK-2024-049', '2024-04-05', '2026-09-30'); + +-- ───────────────────────────────────────── +-- DOCTORS +-- ───────────────────────────────────────── +INSERT INTO doctors (name, specialization, license_number, phone, hospital, is_active) VALUES +('Dr. Imran Khan', 'General Physician', 'PMDC-12345', '03011001001', 'Civil Hospital Karachi', 1), +('Dr. Farrukh Ahmed', 'Cardiologist', 'PMDC-23456', '03011001002', 'Aga Khan Hospital Karachi', 1), +('Dr. Sana Malik', 'Dermatologist', 'PMDC-34567', '03011001003', 'Jinnah Hospital Karachi', 1), +('Dr. Tariq Hussain', 'Pulmonologist', 'PMDC-45678', '03011001004', 'Liaquat National Hospital', 1), +('Dr. Amna Siddiqui', 'Endocrinologist', 'PMDC-56789', '03011001005', 'Aga Khan Hospital Karachi', 1), +('Dr. Rizwan Qureshi', 'Gastroenterologist', 'PMDC-67890', '03011001006', 'Civil Hospital Karachi', 1), +('Dr. Zainab Fatima', 'Gynecologist', 'PMDC-78901', '03011001007', 'South City Hospital Karachi', 1), +('Dr. Salman Mirza', 'Orthopedic Surgeon', 'PMDC-89012', '03011001008', 'Shifa International Islamabad', 1), +('Dr. Hina Baig', 'Pediatrician', 'PMDC-90123', '03011001009', 'The Children\'s Hospital Lahore',1), +('Dr. Naveed Alam', 'Neurologist', 'PMDC-01234', '03011001010', 'Liaquat National Hospital', 1); + +-- ───────────────────────────────────────── +-- CUSTOMERS +-- ───────────────────────────────────────── +INSERT INTO customers (name, phone, email, date_of_birth, gender, address) VALUES +('Ahmed Ali Khan', '03001000001', 'ahmed.ali@gmail.com', '1985-06-15', 'Male', 'House 12, Block A, PECHS, Karachi'), +('Fatima Noor', '03001000002', 'fatima.noor@yahoo.com', '1990-03-22', 'Female', 'Flat 5, Gulshan-e-Iqbal, Karachi'), +('Muhammad Usman', '03001000003', 'musman@hotmail.com', '1978-11-08', 'Male', 'Plot 45, DHA Phase 2, Karachi'), +('Ayesha Siddiqui', '03001000004', 'ayesha.s@gmail.com', '1995-07-14', 'Female', 'House 7, North Nazimabad, Karachi'), +('Bilal Raza', '03001000005', 'bilal.raza@gmail.com', '1982-01-30', 'Male', 'House 33, Gulberg II, Lahore'), +('Sana Zahid', '03001000006', 'sana.zahid@yahoo.com', '1993-09-05', 'Female', 'Street 4, G-9/1, Islamabad'), +('Asif Mehmood', '03001000007', 'asif.m@gmail.com', '1970-12-20', 'Male', 'House 22, Satellite Town, Rawalpindi'), +('Nadia Akhtar', '03001000008', 'nadia.a@hotmail.com', '1988-04-17', 'Female', 'Flat 12, Clifton Block 4, Karachi'), +('Imran Shaikh', '03001000009', 'imran.shaikh@gmail.com', '1975-08-25', 'Male', 'House 67, Model Town, Lahore'), +('Zara Hussain', '03001000010', 'zara.h@gmail.com', '1998-02-11', 'Female', 'House 9, F-8/3, Islamabad'), +('Tariq Javed', '03001000011', 'tariq.j@yahoo.com', '1965-05-03', 'Male', 'House 15, Gulshan Block 13, Karachi'), +('Rabia Waseem', '03001000012', 'rabia.w@gmail.com', '1992-10-28', 'Female', 'Flat 8, Garden West, Karachi'); + +-- ───────────────────────────────────────── +-- EMPLOYEES (password_hash = bcrypt of 'admin123') +-- ───────────────────────────────────────── +INSERT INTO employees (name, role, phone, email, salary, hire_date, is_active, username, password_hash) VALUES +('Moavia Amir', 'Admin', '03211000001', 'moavia@curepharma.pk', 75000.00, '2022-01-01', 1, 'admin', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMKgP0ZuNT0g1VNhJF8WCJ9VkG'), +('Dr. Khalid Baig', 'Pharmacist', '03211000002', 'khalid@curepharma.pk', 65000.00, '2022-03-15', 1, 'pharmacist1','$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMKgP0ZuNT0g1VNhJF8WCJ9VkG'), +('Sara Qureshi', 'Cashier', '03211000003', 'sara@curepharma.pk', 35000.00, '2023-01-10', 1, 'cashier1', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMKgP0ZuNT0g1VNhJF8WCJ9VkG'), +('Hassan Raza', 'Manager', '03211000004', 'hassan@curepharma.pk', 55000.00, '2022-06-01', 1, 'manager1', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMKgP0ZuNT0g1VNhJF8WCJ9VkG'), +('Amina Tariq', 'Pharmacist', '03211000005', 'amina@curepharma.pk', 62000.00, '2023-04-15', 1, 'pharmacist2','$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMKgP0ZuNT0g1VNhJF8WCJ9VkG'), +('Rizwan Ahmed', 'Cashier', '03211000006', 'rizwan@curepharma.pk', 33000.00, '2024-02-01', 1, 'cashier2', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMKgP0ZuNT0g1VNhJF8WCJ9VkG'); +-- Note: All passwords above hash to 'admin123'. Change immediately in production. + +-- ───────────────────────────────────────── +-- PRESCRIPTIONS +-- ───────────────────────────────────────── +INSERT INTO prescriptions (customer_id, doctor_id, prescription_date, valid_until, notes, is_used) VALUES +(1, 1, '2026-04-01', '2026-07-01', 'Antibiotic course for throat infection', 0), +(2, 5, '2026-04-10', '2026-10-10', 'Diabetes management β€” monthly refill', 0), +(3, 2, '2026-04-15', '2027-04-15', 'Hypertension maintenance therapy', 0), +(4, 7, '2026-04-20', '2026-07-20', 'Prescribed for hormonal treatment', 0), +(5, 4, '2026-04-25', '2026-10-25', 'Asthma β€” Ventolin inhaler refill', 0), +(1, 6, '2026-03-15', '2026-06-15', 'PPI for gastroesophageal reflux', 1), +(8, 3, '2026-04-05', '2026-07-05', 'Dermovate cream for eczema', 0), +(9, 2, '2026-04-22', '2027-04-22', 'Long-term cardiac medication', 0), +(10,9, '2026-04-28', '2026-07-28', 'Pediatric antibiotic course', 0), +(11,1, '2026-05-01', '2026-08-01', 'General infection treatment', 0); + +-- ───────────────────────────────────────── +-- ORDERS + ORDER ITEMS + PAYMENTS (sample completed orders) +-- ───────────────────────────────────────── +INSERT INTO orders (customer_id, employee_id, prescription_id, status, subtotal, discount_percent, tax_percent, total_amount, notes) VALUES +(1, 3, 6, 'Completed', 275.00, 5.00, 0.00, 261.25, 'Regular customer discount applied'), +(2, 3, NULL, 'Completed', 195.00, 0.00, 0.00, 195.00, 'Walk-in purchase'), +(3, 3, NULL, 'Completed', 110.00, 0.00, 0.00, 110.00, NULL), +(5, 3, 5, 'Completed', 700.00, 10.00,0.00, 630.00, 'Bulk purchase β€” Ventolin + antibiotics'), +(NULL,3,NULL, 'Completed', 75.00, 0.00, 0.00, 75.00, 'Walk-in customer'), +(4, 3, NULL, 'Pending', 230.00, 0.00, 0.00, 230.00, NULL), +(8, 3, 7, 'Completed', 220.00, 0.00, 0.00, 220.00, 'Dermovate + supplement'), +(11, 3, NULL, 'Completed', 135.00, 5.00, 0.00, 128.25, NULL); + +INSERT INTO order_items (order_id, medicine_id, quantity, unit_price) VALUES +(1, 9, 1, 145.00), (1, 14, 4, 25.00), (1, 10, 1, 85.00), +(2, 18, 1, 85.00), (2, 14, 2, 25.00), (2, 10, 1, 85.00), +(3, 1, 2, 25.00), (3, 4, 4, 15.00), +(4, 12, 1, 350.00), (4, 6, 1, 95.00), (4, 8, 1, 120.00), (4, 14, 6, 25.00), +(5, 14, 3, 25.00), +(7, 19, 1, 220.00), +(8, 1, 3, 25.00), (8, 14, 2, 25.00), (8, 10, 1, 85.00); + +INSERT INTO payments (order_id, amount_paid, payment_method, payment_status, transaction_ref, paid_at) VALUES +(1, 261.25, 'Cash', 'Paid', NULL, '2026-04-02 10:30:00'), +(2, 195.00, 'Card', 'Paid', 'TXN-9981234','2026-04-10 14:15:00'), +(3, 110.00, 'Cash', 'Paid', NULL, '2026-04-16 09:45:00'), +(4, 630.00, 'Online', 'Paid', 'TXN-9982345','2026-04-25 16:00:00'), +(5, 75.00, 'Cash', 'Paid', NULL, '2026-04-26 11:20:00'), +(7, 220.00, 'Cash', 'Paid', NULL, '2026-04-06 13:30:00'), +(8, 128.25, 'Card', 'Paid', 'TXN-9983456','2026-05-01 17:00:00'); diff --git a/msms/database/schema.sql b/msms/database/schema.sql new file mode 100644 index 0000000..3e0fa3d --- /dev/null +++ b/msms/database/schema.sql @@ -0,0 +1,253 @@ +-- ============================================================ +-- Medical Store Management System (MSMS) +-- schema.sql β€” Full Database Schema (MySQL 8.0, 3NF Normalized) +-- Student: Moavia Amir | ID: 2k24_BSAI_72 +-- Course: Database Lab β€” 4th Semester BS AI +-- Instructor: Sir Ahsan Ahmed +-- ============================================================ + +CREATE DATABASE IF NOT EXISTS msms_db + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE msms_db; + +-- ───────────────────────────────────────── +-- 1. CATEGORIES +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS categories ( + category_id INT NOT NULL AUTO_INCREMENT, + category_name VARCHAR(100) NOT NULL, + description TEXT, + PRIMARY KEY (category_id), + UNIQUE KEY uq_category_name (category_name) +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 2. MANUFACTURERS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS manufacturers ( + manufacturer_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + country VARCHAR(100), + contact_email VARCHAR(150), + phone VARCHAR(20), + address TEXT, + PRIMARY KEY (manufacturer_id), + UNIQUE KEY uq_manufacturer_name (name), + UNIQUE KEY uq_manufacturer_email (contact_email) +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 3. SUPPLIERS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS suppliers ( + supplier_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + contact_person VARCHAR(100), + phone VARCHAR(20) NOT NULL, + email VARCHAR(150), + address TEXT, + rating DECIMAL(3,1) DEFAULT NULL, + is_active TINYINT(1) NOT NULL DEFAULT 1, + PRIMARY KEY (supplier_id), + CONSTRAINT chk_supplier_rating CHECK (rating BETWEEN 1.0 AND 5.0) +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 4. MEDICINES +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS medicines ( + medicine_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + generic_name VARCHAR(200), + category_id INT, + manufacturer_id INT, + dosage_form ENUM('Tablet','Capsule','Syrup','Injection','Cream','Drop','Inhaler','Powder','Patch') NOT NULL, + strength VARCHAR(50), + unit_price DECIMAL(10,2) NOT NULL, + requires_prescription TINYINT(1) NOT NULL DEFAULT 0, + description TEXT, + is_active TINYINT(1) NOT NULL DEFAULT 1, + PRIMARY KEY (medicine_id), + CONSTRAINT chk_medicine_price CHECK (unit_price > 0), + CONSTRAINT fk_medicine_category FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL, + CONSTRAINT fk_medicine_manufacturer FOREIGN KEY (manufacturer_id) REFERENCES manufacturers(manufacturer_id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 5. STOCK +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS stock ( + stock_id INT NOT NULL AUTO_INCREMENT, + medicine_id INT NOT NULL, + supplier_id INT, + quantity INT NOT NULL DEFAULT 0, + reorder_level INT NOT NULL DEFAULT 10, + batch_number VARCHAR(100), + manufacturing_date DATE, + expiry_date DATE NOT NULL, + last_restocked TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (stock_id), + UNIQUE KEY uq_stock_medicine (medicine_id), + CONSTRAINT chk_stock_qty CHECK (quantity >= 0), + CONSTRAINT chk_stock_expiry CHECK (expiry_date > manufacturing_date), + CONSTRAINT fk_stock_medicine FOREIGN KEY (medicine_id) REFERENCES medicines(medicine_id) ON DELETE CASCADE, + CONSTRAINT fk_stock_supplier FOREIGN KEY (supplier_id) REFERENCES suppliers(supplier_id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 6. DOCTORS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS doctors ( + doctor_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + specialization VARCHAR(150), + license_number VARCHAR(100) NOT NULL, + phone VARCHAR(20), + hospital VARCHAR(200), + is_active TINYINT(1) NOT NULL DEFAULT 1, + PRIMARY KEY (doctor_id), + UNIQUE KEY uq_doctor_license (license_number) +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 7. CUSTOMERS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS customers ( + customer_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + phone VARCHAR(20) NOT NULL, + email VARCHAR(150), + date_of_birth DATE, + gender ENUM('Male','Female','Other'), + address TEXT, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (customer_id), + UNIQUE KEY uq_customer_phone (phone) +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 8. EMPLOYEES +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS employees ( + employee_id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + role ENUM('Pharmacist','Cashier','Manager','Admin') NOT NULL, + phone VARCHAR(20) NOT NULL, + email VARCHAR(150), + salary DECIMAL(10,2), + hire_date DATE NOT NULL, + is_active TINYINT(1) NOT NULL DEFAULT 1, + username VARCHAR(100) NOT NULL, + password_hash VARCHAR(255) NOT NULL, + PRIMARY KEY (employee_id), + UNIQUE KEY uq_employee_phone (phone), + UNIQUE KEY uq_employee_email (email), + UNIQUE KEY uq_employee_username (username), + CONSTRAINT chk_employee_salary CHECK (salary > 0) +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 9. PRESCRIPTIONS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS prescriptions ( + prescription_id INT NOT NULL AUTO_INCREMENT, + customer_id INT NOT NULL, + doctor_id INT NOT NULL, + prescription_date DATE NOT NULL, + valid_until DATE, + notes TEXT, + is_used TINYINT(1) NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (prescription_id), + CONSTRAINT chk_prescription_valid CHECK (valid_until IS NULL OR valid_until >= prescription_date), + CONSTRAINT fk_prescription_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ON DELETE RESTRICT, + CONSTRAINT fk_prescription_doctor FOREIGN KEY (doctor_id) REFERENCES doctors(doctor_id) ON DELETE RESTRICT +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 10. ORDERS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS orders ( + order_id INT NOT NULL AUTO_INCREMENT, + customer_id INT, + employee_id INT NOT NULL, + prescription_id INT, + order_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + status ENUM('Pending','Completed','Cancelled','Refunded') NOT NULL DEFAULT 'Pending', + subtotal DECIMAL(10,2) NOT NULL DEFAULT 0.00, + discount_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00, + tax_percent DECIMAL(5,2) NOT NULL DEFAULT 0.00, + total_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00, + notes TEXT, + PRIMARY KEY (order_id), + CONSTRAINT chk_order_discount CHECK (discount_percent BETWEEN 0 AND 100), + CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES customers(customer_id) ON DELETE SET NULL, + CONSTRAINT fk_order_employee FOREIGN KEY (employee_id) REFERENCES employees(employee_id) ON DELETE RESTRICT, + CONSTRAINT fk_order_prescription FOREIGN KEY (prescription_id) REFERENCES prescriptions(prescription_id) ON DELETE SET NULL +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 11. ORDER ITEMS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS order_items ( + item_id INT NOT NULL AUTO_INCREMENT, + order_id INT NOT NULL, + medicine_id INT NOT NULL, + quantity INT NOT NULL, + unit_price DECIMAL(10,2) NOT NULL, + subtotal DECIMAL(10,2) GENERATED ALWAYS AS (quantity * unit_price) STORED, + PRIMARY KEY (item_id), + CONSTRAINT chk_item_qty CHECK (quantity > 0), + CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, + CONSTRAINT fk_item_medicine FOREIGN KEY (medicine_id) REFERENCES medicines(medicine_id) ON DELETE RESTRICT +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- 12. PAYMENTS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS payments ( + payment_id INT NOT NULL AUTO_INCREMENT, + order_id INT NOT NULL, + amount_paid DECIMAL(10,2) NOT NULL, + payment_method ENUM('Cash','Card','Online','Insurance') NOT NULL, + payment_status ENUM('Paid','Pending','Failed','Refunded') NOT NULL DEFAULT 'Pending', + transaction_ref VARCHAR(200), + paid_at TIMESTAMP, + notes TEXT, + PRIMARY KEY (payment_id), + UNIQUE KEY uq_payment_order (order_id), + CONSTRAINT chk_payment_amount CHECK (amount_paid >= 0), + CONSTRAINT fk_payment_order FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE RESTRICT +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- AUXILIARY TABLE: LOW STOCK ALERTS +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS low_stock_alerts ( + alert_id INT NOT NULL AUTO_INCREMENT, + medicine_id INT NOT NULL, + current_qty INT NOT NULL, + reorder_level INT NOT NULL, + alerted_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + is_resolved TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (alert_id), + CONSTRAINT fk_alert_medicine FOREIGN KEY (medicine_id) REFERENCES medicines(medicine_id) ON DELETE CASCADE +) ENGINE=InnoDB; + +-- ───────────────────────────────────────── +-- AUXILIARY TABLE: STOCK TRANSACTIONS LOG +-- ───────────────────────────────────────── +CREATE TABLE IF NOT EXISTS stock_transactions ( + txn_id INT NOT NULL AUTO_INCREMENT, + medicine_id INT NOT NULL, + employee_id INT, + qty_change INT NOT NULL, + txn_type ENUM('IN','OUT') NOT NULL, + reference VARCHAR(200), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (txn_id), + CONSTRAINT fk_txn_medicine FOREIGN KEY (medicine_id) REFERENCES medicines(medicine_id) ON DELETE CASCADE, + CONSTRAINT fk_txn_employee FOREIGN KEY (employee_id) REFERENCES employees(employee_id) ON DELETE SET NULL +) ENGINE=InnoDB; diff --git a/msms/database/stored_procedures.sql b/msms/database/stored_procedures.sql new file mode 100644 index 0000000..ed12a55 --- /dev/null +++ b/msms/database/stored_procedures.sql @@ -0,0 +1,240 @@ +-- ============================================================ +-- Medical Store Management System (MSMS) +-- stored_procedures.sql +-- Student: Moavia Amir | ID: 2k24_BSAI_72 +-- ============================================================ + +USE msms_db; + +DROP PROCEDURE IF EXISTS GenerateBill; +DROP PROCEDURE IF EXISTS RestockMedicine; +DROP PROCEDURE IF EXISTS PlaceOrder; +DROP PROCEDURE IF EXISTS CompletePayment; + +DELIMITER $$ + +-- ───────────────────────────────────────── +-- PROCEDURE 1: GenerateBill +-- Returns full bill dataset for an order +-- ───────────────────────────────────────── +CREATE PROCEDURE GenerateBill(IN p_order_id INT) +BEGIN + -- Order header + SELECT + o.order_id, + CONCAT('ORD-', LPAD(o.order_id, 5, '0')) AS invoice_number, + o.order_date, + o.status, + o.subtotal, + o.discount_percent, + ROUND(o.subtotal * o.discount_percent / 100, 2) AS discount_amount, + o.tax_percent, + ROUND((o.subtotal - o.subtotal * o.discount_percent / 100) * o.tax_percent / 100, 2) AS tax_amount, + o.total_amount, + o.notes AS order_notes, + -- Customer info + COALESCE(c.name, 'Walk-in Customer') AS customer_name, + COALESCE(c.phone, 'N/A') AS customer_phone, + -- Employee info + e.name AS cashier_name, + e.role AS cashier_role, + -- Prescription info + CONCAT('RX-', LPAD(p.prescription_id,3,'0')) AS prescription_number, + p.prescription_date, + -- Payment info + py.payment_method, + py.payment_status, + py.amount_paid, + py.transaction_ref, + py.paid_at + FROM orders o + LEFT JOIN customers c ON c.customer_id = o.customer_id + JOIN employees e ON e.employee_id = o.employee_id + LEFT JOIN prescriptions p ON p.prescription_id = o.prescription_id + LEFT JOIN payments py ON py.order_id = o.order_id + WHERE o.order_id = p_order_id; + + -- Order line items + SELECT + oi.item_id, + m.name AS medicine_name, + m.strength, + m.dosage_form, + oi.quantity, + oi.unit_price, + oi.subtotal AS line_subtotal + FROM order_items oi + JOIN medicines m ON m.medicine_id = oi.medicine_id + WHERE oi.order_id = p_order_id + ORDER BY oi.item_id; +END$$ + +-- ───────────────────────────────────────── +-- PROCEDURE 2: RestockMedicine +-- ───────────────────────────────────────── +CREATE PROCEDURE RestockMedicine( + IN p_medicine_id INT, + IN p_quantity INT, + IN p_supplier_id INT, + IN p_expiry_date DATE, + IN p_batch VARCHAR(100), + IN p_employee_id INT +) +BEGIN + DECLARE v_exists INT DEFAULT 0; + + -- Validate quantity + IF p_quantity <= 0 THEN + SIGNAL SQLSTATE '45004' + SET MESSAGE_TEXT = 'Restock quantity must be greater than zero.'; + END IF; + + -- Validate expiry date + IF p_expiry_date <= CURDATE() THEN + SIGNAL SQLSTATE '45005' + SET MESSAGE_TEXT = 'Expiry date must be a future date.'; + END IF; + + -- Check stock record exists + SELECT COUNT(*) INTO v_exists FROM stock WHERE medicine_id = p_medicine_id; + + IF v_exists = 0 THEN + -- Create stock record if missing + INSERT INTO stock (medicine_id, supplier_id, quantity, batch_number, expiry_date, last_restocked) + VALUES (p_medicine_id, p_supplier_id, p_quantity, p_batch, p_expiry_date, NOW()); + ELSE + UPDATE stock + SET + quantity = quantity + p_quantity, + supplier_id = COALESCE(p_supplier_id, supplier_id), + batch_number = COALESCE(p_batch, batch_number), + expiry_date = COALESCE(p_expiry_date, expiry_date), + last_restocked = NOW() + WHERE medicine_id = p_medicine_id; + END IF; + + -- Resolve any open low-stock alerts for this medicine + UPDATE low_stock_alerts + SET is_resolved = 1 + WHERE medicine_id = p_medicine_id AND is_resolved = 0; + + -- Log the incoming stock transaction + INSERT INTO stock_transactions (medicine_id, employee_id, qty_change, txn_type, reference) + VALUES (p_medicine_id, p_employee_id, p_quantity, 'IN', CONCAT('RESTOCK-BATCH-', COALESCE(p_batch,'N/A'))); + + SELECT CONCAT('Medicine ID ', p_medicine_id, ' restocked by ', p_quantity, ' units.') AS message; +END$$ + +-- ───────────────────────────────────────── +-- PROCEDURE 3: PlaceOrder +-- ───────────────────────────────────────── +CREATE PROCEDURE PlaceOrder( + IN p_customer_id INT, + IN p_employee_id INT, + IN p_prescription_id INT, + IN p_discount DECIMAL(5,2), + IN p_tax DECIMAL(5,2), + IN p_notes TEXT, + OUT p_order_id INT +) +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + ROLLBACK; + RESIGNAL; + END; + + START TRANSACTION; + + IF p_discount < 0 OR p_discount > 100 THEN + SIGNAL SQLSTATE '45006' + SET MESSAGE_TEXT = 'Discount percent must be between 0 and 100.'; + END IF; + + INSERT INTO orders + (customer_id, employee_id, prescription_id, status, discount_percent, tax_percent, notes) + VALUES + (p_customer_id, p_employee_id, p_prescription_id, 'Pending', p_discount, p_tax, p_notes); + + SET p_order_id = LAST_INSERT_ID(); + + COMMIT; +END$$ + +-- ───────────────────────────────────────── +-- PROCEDURE 4: CompletePayment +-- ───────────────────────────────────────── +CREATE PROCEDURE CompletePayment( + IN p_order_id INT, + IN p_method VARCHAR(20), + IN p_amount DECIMAL(10,2), + IN p_ref VARCHAR(200) +) +BEGIN + DECLARE v_total DECIMAL(10,2); + DECLARE v_status VARCHAR(20); + DECLARE v_pay_exists INT DEFAULT 0; + + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + ROLLBACK; + RESIGNAL; + END; + + START TRANSACTION; + + -- Fetch order total and current status + SELECT total_amount, status + INTO v_total, v_status + FROM orders + WHERE order_id = p_order_id + FOR UPDATE; + + IF v_total IS NULL THEN + SIGNAL SQLSTATE '45007' + SET MESSAGE_TEXT = 'Order not found.'; + END IF; + + IF v_status = 'Completed' THEN + SIGNAL SQLSTATE '45008' + SET MESSAGE_TEXT = 'Order is already completed.'; + END IF; + + IF v_status = 'Cancelled' THEN + SIGNAL SQLSTATE '45009' + SET MESSAGE_TEXT = 'Cannot pay for a cancelled order.'; + END IF; + + IF p_amount < v_total THEN + SIGNAL SQLSTATE '45010' + SET MESSAGE_TEXT = 'Amount paid is less than the order total.'; + END IF; + + -- Check for duplicate payment record + SELECT COUNT(*) INTO v_pay_exists FROM payments WHERE order_id = p_order_id; + IF v_pay_exists > 0 THEN + SIGNAL SQLSTATE '45011' + SET MESSAGE_TEXT = 'A payment record already exists for this order.'; + END IF; + + INSERT INTO payments + (order_id, amount_paid, payment_method, payment_status, transaction_ref, paid_at) + VALUES + (p_order_id, p_amount, p_method, 'Paid', p_ref, NOW()); + + UPDATE orders + SET status = 'Completed' + WHERE order_id = p_order_id; + + -- Mark prescription as used if linked + UPDATE prescriptions pr + JOIN orders o ON o.prescription_id = pr.prescription_id + SET pr.is_used = 1 + WHERE o.order_id = p_order_id; + + COMMIT; + + SELECT 'Payment completed successfully.' AS message; +END$$ + +DELIMITER ; diff --git a/msms/database/triggers.sql b/msms/database/triggers.sql new file mode 100644 index 0000000..22ab655 --- /dev/null +++ b/msms/database/triggers.sql @@ -0,0 +1,141 @@ +-- ============================================================ +-- Medical Store Management System (MSMS) +-- triggers.sql β€” All Database Triggers +-- Student: Moavia Amir | ID: 2k24_BSAI_72 +-- Course: Database Lab β€” 4th Semester BS AI +-- Instructor: Sir Ahsan Ahmed +-- ============================================================ + +USE msms_db; + +-- Drop existing triggers if they exist +DROP TRIGGER IF EXISTS after_order_item_insert_stock; +DROP TRIGGER IF EXISTS after_order_item_insert_lowstock; +DROP TRIGGER IF EXISTS after_order_item_insert_update_total; +DROP TRIGGER IF EXISTS before_stock_update_expiry_check; + +DELIMITER $$ + +-- ───────────────────────────────────────── +-- TRIGGER 1: Deduct stock on order item insert +-- ───────────────────────────────────────── +CREATE TRIGGER after_order_item_insert_stock +AFTER INSERT ON order_items +FOR EACH ROW +BEGIN + DECLARE v_current_qty INT; + + SELECT quantity INTO v_current_qty + FROM stock + WHERE medicine_id = NEW.medicine_id; + + IF v_current_qty IS NULL THEN + SIGNAL SQLSTATE '45000' + SET MESSAGE_TEXT = 'No stock record found for this medicine.'; + END IF; + + IF v_current_qty < NEW.quantity THEN + SIGNAL SQLSTATE '45001' + SET MESSAGE_TEXT = 'Insufficient stock: cannot fulfil requested quantity.'; + END IF; + + UPDATE stock + SET quantity = quantity - NEW.quantity + WHERE medicine_id = NEW.medicine_id; + + -- Log the outgoing stock transaction + INSERT INTO stock_transactions (medicine_id, employee_id, qty_change, txn_type, reference) + SELECT NEW.medicine_id, + o.employee_id, + NEW.quantity, + 'OUT', + CONCAT('ORDER-', NEW.order_id) + FROM orders o + WHERE o.order_id = NEW.order_id; +END$$ + +-- ───────────────────────────────────────── +-- TRIGGER 2: Low stock alert after order item insert +-- ───────────────────────────────────────── +CREATE TRIGGER after_order_item_insert_lowstock +AFTER INSERT ON order_items +FOR EACH ROW +BEGIN + DECLARE v_qty INT; + DECLARE v_reorder INT; + + SELECT quantity, reorder_level + INTO v_qty, v_reorder + FROM stock + WHERE medicine_id = NEW.medicine_id; + + IF v_qty IS NOT NULL AND v_qty < v_reorder THEN + -- Only insert alert if one doesn't already exist for today + IF NOT EXISTS ( + SELECT 1 FROM low_stock_alerts + WHERE medicine_id = NEW.medicine_id + AND DATE(alerted_at) = CURDATE() + AND is_resolved = 0 + ) THEN + INSERT INTO low_stock_alerts (medicine_id, current_qty, reorder_level) + VALUES (NEW.medicine_id, v_qty, v_reorder); + END IF; + END IF; +END$$ + +-- ───────────────────────────────────────── +-- TRIGGER 3: Recalculate order totals after item insert +-- ───────────────────────────────────────── +CREATE TRIGGER after_order_item_insert_update_total +AFTER INSERT ON order_items +FOR EACH ROW +BEGIN + DECLARE v_subtotal DECIMAL(10,2); + DECLARE v_discount_pct DECIMAL(5,2); + DECLARE v_tax_pct DECIMAL(5,2); + DECLARE v_discount_amt DECIMAL(10,2); + DECLARE v_tax_amt DECIMAL(10,2); + DECLARE v_total DECIMAL(10,2); + + -- Sum all line-item subtotals for this order + SELECT COALESCE(SUM(quantity * unit_price), 0) + INTO v_subtotal + FROM order_items + WHERE order_id = NEW.order_id; + + -- Fetch discount/tax percentages + SELECT discount_percent, tax_percent + INTO v_discount_pct, v_tax_pct + FROM orders + WHERE order_id = NEW.order_id; + + SET v_discount_amt = ROUND(v_subtotal * v_discount_pct / 100, 2); + SET v_tax_amt = ROUND((v_subtotal - v_discount_amt) * v_tax_pct / 100, 2); + SET v_total = v_subtotal - v_discount_amt + v_tax_amt; + + UPDATE orders + SET subtotal = v_subtotal, + total_amount = v_total + WHERE order_id = NEW.order_id; +END$$ + +-- ───────────────────────────────────────── +-- TRIGGER 4: Prevent expired stock updates +-- ───────────────────────────────────────── +CREATE TRIGGER before_stock_update_expiry_check +BEFORE UPDATE ON stock +FOR EACH ROW +BEGIN + IF NEW.expiry_date < CURDATE() THEN + SIGNAL SQLSTATE '45002' + SET MESSAGE_TEXT = 'Cannot set expiry_date to a past date.'; + END IF; + + IF NEW.manufacturing_date IS NOT NULL + AND NEW.expiry_date <= NEW.manufacturing_date THEN + SIGNAL SQLSTATE '45003' + SET MESSAGE_TEXT = 'expiry_date must be after manufacturing_date.'; + END IF; +END$$ + +DELIMITER ; diff --git a/msms/database/views.sql b/msms/database/views.sql new file mode 100644 index 0000000..4aae6cf --- /dev/null +++ b/msms/database/views.sql @@ -0,0 +1,175 @@ +-- ============================================================ +-- Medical Store Management System (MSMS) +-- views.sql β€” All Database Views +-- Student: Moavia Amir | ID: 2k24_BSAI_72 +-- ============================================================ + +USE msms_db; + +DROP VIEW IF EXISTS vw_LowStockAlert; +DROP VIEW IF EXISTS vw_ExpiringSoon; +DROP VIEW IF EXISTS vw_SalesSummary; +DROP VIEW IF EXISTS vw_TopSellingMedicines; +DROP VIEW IF EXISTS vw_EmployeePerformance; +DROP VIEW IF EXISTS vw_SupplierRatings; +DROP VIEW IF EXISTS vw_FullInventory; + +-- ───────────────────────────────────────── +-- VIEW 1: Low Stock Alert +-- ───────────────────────────────────────── +CREATE VIEW vw_LowStockAlert AS +SELECT + m.medicine_id, + m.name AS medicine_name, + m.dosage_form, + m.strength, + c.category_name, + s.quantity AS current_stock, + s.reorder_level, + (s.reorder_level - s.quantity) AS units_below_reorder, + s.expiry_date, + sup.name AS supplier_name, + sup.phone AS supplier_phone +FROM medicines m +JOIN stock s ON s.medicine_id = m.medicine_id +JOIN categories c ON c.category_id = m.category_id +LEFT JOIN suppliers sup ON sup.supplier_id = s.supplier_id +WHERE s.quantity < s.reorder_level + AND m.is_active = 1 +ORDER BY units_below_reorder DESC; + +-- ───────────────────────────────────────── +-- VIEW 2: Expiring Soon (within 30 days) +-- ───────────────────────────────────────── +CREATE VIEW vw_ExpiringSoon AS +SELECT + m.medicine_id, + m.name AS medicine_name, + m.dosage_form, + m.strength, + c.category_name, + s.quantity AS current_stock, + s.batch_number, + s.expiry_date, + DATEDIFF(s.expiry_date, CURDATE()) AS days_until_expiry, + sup.name AS supplier_name +FROM medicines m +JOIN stock s ON s.medicine_id = m.medicine_id +JOIN categories c ON c.category_id = m.category_id +LEFT JOIN suppliers sup ON sup.supplier_id = s.supplier_id +WHERE s.expiry_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY) + AND m.is_active = 1 +ORDER BY s.expiry_date ASC; + +-- ───────────────────────────────────────── +-- VIEW 3: Sales Summary (by date) +-- ───────────────────────────────────────── +CREATE VIEW vw_SalesSummary AS +SELECT + DATE(o.order_date) AS sale_date, + COUNT(o.order_id) AS total_orders, + SUM(o.total_amount) AS total_revenue, + AVG(o.total_amount) AS avg_order_value, + SUM(o.discount_percent * o.subtotal / 100) AS total_discounts_given, + COUNT(DISTINCT o.customer_id) AS unique_customers +FROM orders o +WHERE o.status = 'Completed' +GROUP BY DATE(o.order_date) +ORDER BY sale_date DESC; + +-- ───────────────────────────────────────── +-- VIEW 4: Top Selling Medicines +-- ───────────────────────────────────────── +CREATE VIEW vw_TopSellingMedicines AS +SELECT + m.medicine_id, + m.name AS medicine_name, + m.dosage_form, + m.strength, + c.category_name, + SUM(oi.quantity) AS total_sold, + SUM(oi.subtotal) AS total_revenue, + COUNT(DISTINCT oi.order_id) AS order_count, + m.unit_price +FROM order_items oi +JOIN medicines m ON m.medicine_id = oi.medicine_id +JOIN categories c ON c.category_id = m.category_id +JOIN orders o ON o.order_id = oi.order_id +WHERE o.status = 'Completed' +GROUP BY m.medicine_id, m.name, m.dosage_form, m.strength, c.category_name, m.unit_price +ORDER BY total_sold DESC; + +-- ───────────────────────────────────────── +-- VIEW 5: Employee Performance +-- ───────────────────────────────────────── +CREATE VIEW vw_EmployeePerformance AS +SELECT + e.employee_id, + e.name AS employee_name, + e.role, + COUNT(o.order_id) AS total_orders, + COALESCE(SUM(o.total_amount), 0) AS total_revenue_handled, + COALESCE(AVG(o.total_amount), 0) AS avg_order_value, + COUNT(CASE WHEN o.status = 'Completed' THEN 1 END) AS completed_orders, + COUNT(CASE WHEN o.status = 'Cancelled' THEN 1 END) AS cancelled_orders +FROM employees e +LEFT JOIN orders o ON o.employee_id = e.employee_id +WHERE e.is_active = 1 +GROUP BY e.employee_id, e.name, e.role +ORDER BY total_revenue_handled DESC; + +-- ───────────────────────────────────────── +-- VIEW 6: Supplier Ratings +-- ───────────────────────────────────────── +CREATE VIEW vw_SupplierRatings AS +SELECT + sup.supplier_id, + sup.name AS supplier_name, + sup.contact_person, + sup.phone, + sup.email, + sup.rating, + COUNT(s.stock_id) AS medicines_supplied, + SUM(s.quantity) AS total_units_in_stock, + sup.is_active +FROM suppliers sup +LEFT JOIN stock s ON s.supplier_id = sup.supplier_id +GROUP BY sup.supplier_id, sup.name, sup.contact_person, sup.phone, sup.email, sup.rating, sup.is_active +ORDER BY sup.rating DESC; + +-- ───────────────────────────────────────── +-- VIEW 7: Full Inventory +-- ───────────────────────────────────────── +CREATE VIEW vw_FullInventory AS +SELECT + m.medicine_id, + m.name AS medicine_name, + m.generic_name, + m.dosage_form, + m.strength, + m.unit_price, + m.requires_prescription, + m.is_active AS medicine_active, + c.category_name, + mfr.name AS manufacturer_name, + mfr.country, + s.stock_id, + s.quantity AS stock_qty, + s.reorder_level, + CASE + WHEN s.quantity = 0 THEN 'Out of Stock' + WHEN s.quantity < s.reorder_level THEN 'Low Stock' + ELSE 'In Stock' + END AS stock_status, + s.batch_number, + s.manufacturing_date, + s.expiry_date, + DATEDIFF(s.expiry_date, CURDATE()) AS days_to_expiry, + s.last_restocked, + sup.name AS supplier_name +FROM medicines m +LEFT JOIN categories c ON c.category_id = m.category_id +LEFT JOIN manufacturers mfr ON mfr.manufacturer_id = m.manufacturer_id +LEFT JOIN stock s ON s.medicine_id = m.medicine_id +LEFT JOIN suppliers sup ON sup.supplier_id = s.supplier_id +ORDER BY m.name ASC; diff --git a/msms/docs/MSMS_Project_Manual (1).pdf b/msms/docs/MSMS_Project_Manual (1).pdf new file mode 100644 index 0000000000000000000000000000000000000000..1539968da419fb260a90a8e7c5927f141056c20f GIT binary patch literal 73222 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 literal 0 HcmV?d00001 diff --git a/msms/docs/Medical_Store_Proposal.pdf b/msms/docs/Medical_Store_Proposal.pdf new file mode 100644 index 0000000000000000000000000000000000000000..638e36da44a93a4c454dca8cca3c7e66a8840c6f GIT binary patch literal 108023 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Ν2a~p_B7*;?9azI( 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�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
u*AR82+<6)~l}A>*i#Q4F)1 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 literal 0 HcmV?d00001 diff --git a/msms/docs/msms_er_diagram (1).html b/msms/docs/msms_er_diagram (1).html new file mode 100644 index 0000000..4195d09 --- /dev/null +++ b/msms/docs/msms_er_diagram (1).html @@ -0,0 +1,213 @@ + + + +
+

Entity-Relationship Diagram 12 Tables

+

Medical Store Management System (MSMS)

+
+ +
+ + diff --git a/msms/frontend/index.html b/msms/frontend/index.html new file mode 100644 index 0000000..814e97b --- /dev/null +++ b/msms/frontend/index.html @@ -0,0 +1,16 @@ + + + + + + + Cure Pharmacy β€” MSMS + + + + + +
+ + + diff --git a/msms/frontend/package-lock.json b/msms/frontend/package-lock.json new file mode 100644 index 0000000..e912262 --- /dev/null +++ b/msms/frontend/package-lock.json @@ -0,0 +1,3634 @@ +{ + "name": "msms-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "msms-frontend", + "version": "1.0.0", + "dependencies": { + "@hookform/resolvers": "^3.3.4", + "axios": "^1.6.2", + "date-fns": "^3.0.6", + "html2canvas": "^1.4.1", + "jspdf": "^2.5.1", + "lucide-react": "^0.312.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.49.3", + "react-hot-toast": "^2.4.1", + "react-router-dom": "^6.21.3", + "react-to-print": "^2.15.1", + "recharts": "^2.10.3", + "zod": "^3.22.4" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.7", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.17", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "vite": "^5.1.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz", + "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.27.tgz", + "integrity": "sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.9.tgz", + "integrity": "sha512-i6mvVmWN4xo9LrhCOZrDgSs9noW6nOahbrmzjRbPF36YPyj5Ue5lgok0MHDWkG7xzpWFO2OYttXdzM7rJxHvNA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.349", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.349.tgz", + "integrity": "sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/goober": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz", + "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jspdf": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz", + "integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.5.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.312.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.312.0.tgz", + "integrity": "sha512-3UZsqyswRXjW4t+nw+InICewSimjPKHuSxiFYqTshv9xkK3tPPntXk/lvXc9pKlXIxm3v9WKyoxcrB6YHhP+dg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.75.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.75.0.tgz", + "integrity": "sha512-Ovv94H+0p3sJ7B9B5QxPuCP1u8V/cHuVGyH55cSwodYDtoJwK+fqk3vjfIgSX59I2U/bU4z0nRJ9HMLpNiWEmw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-hot-toast": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-to-print": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.15.1.tgz", + "integrity": "sha512-1foogIFbCpzAVxydkhBiDfMiFYhIMphiagDOfcG4X/EcQ+fBPqJ0rby9Wv/emzY1YLkIQy/rEgOrWQT+rBKhjw==", + "license": "MIT", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/msms/frontend/package.json b/msms/frontend/package.json new file mode 100644 index 0000000..a345fa4 --- /dev/null +++ b/msms/frontend/package.json @@ -0,0 +1,37 @@ +{ + "name": "msms-frontend", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "axios": "^1.6.2", + "date-fns": "^3.0.6", + "html2canvas": "^1.4.1", + "jspdf": "^2.5.1", + "lucide-react": "^0.312.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.49.3", + "react-hot-toast": "^2.4.1", + "react-router-dom": "^6.21.3", + "react-to-print": "^2.15.1", + "recharts": "^2.10.3", + "zod": "^3.22.4", + "@hookform/resolvers": "^3.3.4" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.7", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.17", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "vite": "^5.1.0" + } +} diff --git a/msms/frontend/postcss.config.js b/msms/frontend/postcss.config.js new file mode 100644 index 0000000..e008c9c --- /dev/null +++ b/msms/frontend/postcss.config.js @@ -0,0 +1,3 @@ +export default { + plugins: { tailwindcss: {}, autoprefixer: {} }, +}; diff --git a/msms/frontend/public/favicon.svg b/msms/frontend/public/favicon.svg new file mode 100644 index 0000000..3c2648a --- /dev/null +++ b/msms/frontend/public/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/msms/frontend/src/App.jsx b/msms/frontend/src/App.jsx new file mode 100644 index 0000000..4d41e9b --- /dev/null +++ b/msms/frontend/src/App.jsx @@ -0,0 +1,122 @@ +// src/App.jsx +import { Routes, Route, Navigate, useLocation } from 'react-router-dom'; +import { Toaster } from 'react-hot-toast'; +import { AuthProvider } from './context/AuthContext'; +import { CartProvider } from './context/CartContext'; +import ProtectedRoute from './components/Layout/ProtectedRoute'; +import Sidebar from './components/Layout/Sidebar'; +import Navbar from './components/Layout/Navbar'; + +// Pages +import Login from './pages/Login'; +import Dashboard from './pages/Dashboard'; +import MedicineList from './pages/Inventory/MedicineList'; +import StockList from './pages/Inventory/StockList'; +import NewOrder from './pages/Sales/NewOrder'; +import OrderList from './pages/Sales/OrderList'; +import OrderDetail from './pages/Sales/OrderDetail'; +import CustomerList from './pages/Customers/CustomerList'; +import SupplierList from './pages/Suppliers/SupplierList'; +import DoctorList from './pages/Doctors/DoctorList'; +import EmployeeList from './pages/Employees/EmployeeList'; +import PrescriptionList from './pages/Prescriptions/PrescriptionList'; +import PaymentList from './pages/Payments/PaymentList'; +import Reports from './pages/Reports/Reports'; + +const PAGE_TITLES = { + '/': 'Dashboard', + '/medicines': 'Medicines', + '/stock': 'Stock', + '/orders/new': 'New Sale', + '/orders': 'Orders', + '/customers': 'Customers', + '/suppliers': 'Suppliers', + '/doctors': 'Doctors', + '/employees': 'Employees', + '/prescriptions': 'Prescriptions', + '/payments': 'Payments', + '/reports': 'Reports', +}; + +function AppLayout({ children }) { + const { pathname } = useLocation(); + const title = Object.entries(PAGE_TITLES) + .reverse() + .find(([path]) => pathname.startsWith(path))?.[1] || 'MSMS'; + + return ( +
+ +
+ +
+ {children} +
+
+
+ ); +} + +export default function App() { + return ( + + + + + } /> + + + + + } /> + + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + } /> + + + + ); +} diff --git a/msms/frontend/src/api/index.js b/msms/frontend/src/api/index.js new file mode 100644 index 0000000..f0a0bf1 --- /dev/null +++ b/msms/frontend/src/api/index.js @@ -0,0 +1,79 @@ +// src/api/index.js β€” Axios instance + all API helpers +import axios from 'axios'; + +const api = axios.create({ + baseURL: '/api/v1', + headers: { 'Content-Type': 'application/json' }, +}); + +// Attach JWT on every request +api.interceptors.request.use((config) => { + const token = localStorage.getItem('msms_token'); + if (token) config.headers.Authorization = `Bearer ${token}`; + return config; +}); + +// Global 401 redirect +api.interceptors.response.use( + (res) => res, + (err) => { + if (err.response?.status === 401) { + localStorage.removeItem('msms_token'); + localStorage.removeItem('msms_user'); + window.location.href = '/login'; + } + return Promise.reject(err); + } +); + +// ── Auth ────────────────────────────────────────────────── +export const authAPI = { + login: (data) => api.post('/auth/login', data), + me: () => api.get('/auth/me'), +}; + +// ── Generic CRUD factory ───────────────────────────────── +const crud = (path) => ({ + getAll: (params) => api.get(path, { params }), + getById: (id) => api.get(`${path}/${id}`), + create: (data) => api.post(path, data), + update: (id, d) => api.put(`${path}/${id}`, d), + remove: (id) => api.delete(`${path}/${id}`), +}); + +export const categoriesAPI = crud('/categories'); +export const manufacturersAPI = crud('/manufacturers'); +export const suppliersAPI = crud('/suppliers'); +export const medicinesAPI = crud('/medicines'); +export const stockAPI = { + ...crud('/stock'), + restock: (data) => api.post('/stock/restock', data), +}; +export const doctorsAPI = crud('/doctors'); +export const customersAPI = { + ...crud('/customers'), + searchByPhone: (phone) => api.get('/customers/search/phone', { params: { phone } }), +}; +export const employeesAPI = crud('/employees'); +export const prescriptionsAPI = crud('/prescriptions'); +export const paymentsAPI = crud('/payments'); + +// ── Orders ─────────────────────────────────────────────── +export const ordersAPI = { + ...crud('/orders'), + getBill: (id) => api.get(`/orders/${id}/bill`), + complete: (id, d)=> api.post(`/orders/${id}/complete`, d), + cancel: (id) => api.put(`/orders/${id}/cancel`), +}; + +// ── Reports ────────────────────────────────────────────── +export const reportsAPI = { + dashboardStats: () => api.get('/reports/dashboard-stats'), + lowStock: () => api.get('/reports/low-stock'), + expiringSoon: () => api.get('/reports/expiring-soon'), + salesSummary: (params) => api.get('/reports/sales-summary', { params }), + topMedicines: () => api.get('/reports/top-medicines'), + employeePerformance: () => api.get('/reports/employee-performance'), +}; + +export default api; diff --git a/msms/frontend/src/components/Bill/BillActions.jsx b/msms/frontend/src/components/Bill/BillActions.jsx new file mode 100644 index 0000000..857c681 --- /dev/null +++ b/msms/frontend/src/components/Bill/BillActions.jsx @@ -0,0 +1,52 @@ +// src/components/Bill/BillActions.jsx +import { useRef } from 'react'; +import { useReactToPrint } from 'react-to-print'; +import jsPDF from 'jspdf'; +import html2canvas from 'html2canvas'; +import { Printer, Download } from 'lucide-react'; +import toast from 'react-hot-toast'; +import { format } from 'date-fns'; + +export default function BillActions({ billRef, invoiceNumber, orderDate }) { + const handlePrint = useReactToPrint({ + content: () => billRef.current, + documentTitle: `Invoice-${invoiceNumber}`, + onAfterPrint: () => toast.success('Bill sent to printer'), + }); + + const handleDownloadPDF = async () => { + if (!billRef.current) return; + const toastId = toast.loading('Generating PDF…'); + try { + const canvas = await html2canvas(billRef.current, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }); + const imgData = canvas.toDataURL('image/png'); + + // A5 size in mm: 148 x 210 + const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a5' }); + const pdfW = pdf.internal.pageSize.getWidth(); + const pdfH = (canvas.height * pdfW) / canvas.width; + + pdf.addImage(imgData, 'PNG', 0, 0, pdfW, pdfH); + + const dateStr = orderDate ? format(new Date(orderDate), 'yyyy-MM-dd') : format(new Date(), 'yyyy-MM-dd'); + pdf.save(`Invoice-${invoiceNumber}-${dateStr}.pdf`); + toast.success('PDF downloaded', { id: toastId }); + } catch (err) { + console.error(err); + toast.error('Failed to generate PDF', { id: toastId }); + } + }; + + return ( +
+ + +
+ ); +} diff --git a/msms/frontend/src/components/Bill/BillPreview.jsx b/msms/frontend/src/components/Bill/BillPreview.jsx new file mode 100644 index 0000000..f6227c5 --- /dev/null +++ b/msms/frontend/src/components/Bill/BillPreview.jsx @@ -0,0 +1,113 @@ +// src/components/Bill/BillPreview.jsx +import { forwardRef } from 'react'; +import { format } from 'date-fns'; + +const BillPreview = forwardRef(({ bill }, ref) => { + if (!bill) return null; + const { header, items } = bill; + const fmt = (n) => parseFloat(n || 0).toFixed(2); + + return ( +
+ {/* Header */} +
+
+ + +
+

CURE PHARMACY

+

123 Main Shahrah, Karachi, Sindh, Pakistan

+

Ph: 021-35141000 | DRAP Lic: DRAP-KHI-2022

+
+ + {/* Invoice Meta */} +
+
Invoice #: {header.invoice_number}
+
Date: {header.order_date ? format(new Date(header.order_date), 'dd-MMM-yyyy') : 'β€”'}
+
Time: {header.order_date ? format(new Date(header.order_date), 'HH:mm') : 'β€”'}
+
Cashier: {header.cashier_name}
+
Customer: {header.customer_name} | Ph: {header.customer_phone}
+ {header.prescription_number && header.prescription_number !== 'RX-000' && ( +
Prescription #: {header.prescription_number}
+ )} +
+ + {/* Items Table */} + + + + + + + + + + + + {items.map((item, i) => ( + + + + + + + + ))} + +
#MedicineQtyPriceSub
{i + 1} + {item.medicine_name} + {item.strength ? {item.strength} : null} + {item.quantity}{fmt(item.unit_price)}{fmt(item.line_subtotal)}
+ + {/* Totals */} +
+
+ Subtotal:PKR {fmt(header.subtotal)} +
+ {parseFloat(header.discount_amount) > 0 && ( +
+ Discount ({header.discount_percent}%): + -PKR {fmt(header.discount_amount)} +
+ )} + {parseFloat(header.tax_amount) > 0 && ( +
+ Tax ({header.tax_percent}%):PKR {fmt(header.tax_amount)} +
+ )} +
+ GRAND TOTAL:PKR {fmt(header.total_amount)} +
+
+ Payment: {header.payment_method || 'N/A'} + + {header.payment_status?.toUpperCase() || 'PENDING'} + +
+ {header.amount_paid && parseFloat(header.amount_paid) > parseFloat(header.total_amount) && ( +
+ Cash Given:PKR {fmt(header.amount_paid)} +
+ )} + {header.amount_paid && parseFloat(header.amount_paid) > parseFloat(header.total_amount) && ( +
+ Change:PKR {(parseFloat(header.amount_paid) - parseFloat(header.total_amount)).toFixed(2)} +
+ )} +
+ + {/* Footer */} +
+

Thank you! Get well soon. 🌿

+

Return policy: 3 days with original receipt

+

For queries: 021-35141000

+
+
+ ); +}); + +BillPreview.displayName = 'BillPreview'; +export default BillPreview; diff --git a/msms/frontend/src/components/Layout/Navbar.jsx b/msms/frontend/src/components/Layout/Navbar.jsx new file mode 100644 index 0000000..bcc73be --- /dev/null +++ b/msms/frontend/src/components/Layout/Navbar.jsx @@ -0,0 +1,66 @@ +// src/components/Layout/Navbar.jsx +import { useState, useRef, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Search, Bell, X } from 'lucide-react'; +import { medicinesAPI } from '../../api'; + +export default function Navbar({ title }) { + const [query, setQuery] = useState(''); + const [results, setResults] = useState([]); + const [open, setOpen] = useState(false); + const navigate = useNavigate(); + const ref = useRef(null); + + useEffect(() => { + const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, []); + + const search = async (q) => { + setQuery(q); + if (!q.trim()) { setResults([]); setOpen(false); return; } + try { + const { data } = await medicinesAPI.getAll({ search: q, limit: 6 }); + setResults(data.data || []); + setOpen(true); + } catch {} + }; + + return ( +
+

{title}

+ + {/* Global Search */} +
+ + search(e.target.value)} + placeholder="Search medicines…" + className="w-full pl-9 pr-8 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 dark:text-white focus:outline-none focus:ring-2 focus:ring-teal-500" + /> + {query && ( + + )} + {open && results.length > 0 && ( +
+ {results.map(m => ( + + ))} +
+ )} +
+
+ ); +} diff --git a/msms/frontend/src/components/Layout/ProtectedRoute.jsx b/msms/frontend/src/components/Layout/ProtectedRoute.jsx new file mode 100644 index 0000000..b17f4bc --- /dev/null +++ b/msms/frontend/src/components/Layout/ProtectedRoute.jsx @@ -0,0 +1,10 @@ +// src/components/Layout/ProtectedRoute.jsx +import { Navigate } from 'react-router-dom'; +import { useAuth } from '../../context/AuthContext'; + +export default function ProtectedRoute({ children, roles }) { + const { user } = useAuth(); + if (!user) return ; + if (roles && !roles.includes(user.role)) return ; + return children; +} diff --git a/msms/frontend/src/components/Layout/Sidebar.jsx b/msms/frontend/src/components/Layout/Sidebar.jsx new file mode 100644 index 0000000..746a244 --- /dev/null +++ b/msms/frontend/src/components/Layout/Sidebar.jsx @@ -0,0 +1,108 @@ +// src/components/Layout/Sidebar.jsx +import { NavLink, useNavigate } from 'react-router-dom'; +import { useAuth } from '../../context/AuthContext'; +import { + LayoutDashboard, Pill, ShoppingCart, FileText, Users, Truck, + UserCog, CreditCard, BarChart3, LogOut, Moon, Sun, AlertTriangle +} from 'lucide-react'; +import { useState, useEffect } from 'react'; +import { reportsAPI } from '../../api'; + +const navItems = [ + { to: '/', icon: LayoutDashboard, label: 'Dashboard' }, + { to: '/medicines', icon: Pill, label: 'Medicines' }, + { to: '/orders/new', icon: ShoppingCart, label: 'New Sale' }, + { to: '/orders', icon: FileText, label: 'Orders' }, + { to: '/prescriptions',icon: FileText, label: 'Prescriptions' }, + { to: '/customers', icon: Users, label: 'Customers' }, + { to: '/suppliers', icon: Truck, label: 'Suppliers' }, + { to: '/employees', icon: UserCog, label: 'Employees', roles: ['Admin','Manager'] }, + { to: '/payments', icon: CreditCard, label: 'Payments' }, + { to: '/reports', icon: BarChart3, label: 'Reports', badge: true }, +]; + +export default function Sidebar() { + const { user, logout } = useAuth(); + const navigate = useNavigate(); + const [dark, setDark] = useState(() => localStorage.getItem('dark') === 'true'); + const [expiryBadge, setExpiryBadge] = useState(false); + + useEffect(() => { + document.documentElement.classList.toggle('dark', dark); + localStorage.setItem('dark', dark); + }, [dark]); + + useEffect(() => { + reportsAPI.expiringSoon().then(({ data }) => { + setExpiryBadge(data.some(m => m.days_until_expiry <= 7)); + }).catch(() => {}); + }, []); + + const handleLogout = () => { logout(); navigate('/login'); }; + + return ( + + ); +} diff --git a/msms/frontend/src/components/UI/Badge.jsx b/msms/frontend/src/components/UI/Badge.jsx new file mode 100644 index 0000000..55ba148 --- /dev/null +++ b/msms/frontend/src/components/UI/Badge.jsx @@ -0,0 +1,37 @@ +// src/components/UI/Badge.jsx +const variants = { + green: 'bg-green-100 text-green-800 dark:bg-green-900/40 dark:text-green-300', + yellow: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/40 dark:text-yellow-300', + red: 'bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300', + blue: 'bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300', + gray: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300', + teal: 'bg-teal-100 text-teal-800 dark:bg-teal-900/40 dark:text-teal-300', + purple: 'bg-purple-100 text-purple-800 dark:bg-purple-900/40 dark:text-purple-300', +}; + +export default function Badge({ variant = 'gray', children }) { + return ( + + {children} + + ); +} + +export function StockBadge({ status }) { + const map = { + 'In Stock': 'green', + 'Low Stock': 'yellow', + 'Out of Stock': 'red', + }; + return {status}; +} + +export function OrderStatusBadge({ status }) { + const map = { Completed: 'green', Pending: 'yellow', Cancelled: 'red', Refunded: 'purple' }; + return {status}; +} + +export function PaymentBadge({ method }) { + const map = { Cash: 'teal', Card: 'blue', Online: 'purple', Insurance: 'gray' }; + return {method}; +} diff --git a/msms/frontend/src/components/UI/ConfirmDialog.jsx b/msms/frontend/src/components/UI/ConfirmDialog.jsx new file mode 100644 index 0000000..85b94e1 --- /dev/null +++ b/msms/frontend/src/components/UI/ConfirmDialog.jsx @@ -0,0 +1,25 @@ +// src/components/UI/ConfirmDialog.jsx +import Modal from './Modal'; +import { AlertTriangle } from 'lucide-react'; + +export default function ConfirmDialog({ open, onClose, onConfirm, title, message, confirmLabel = 'Delete', danger = true }) { + return ( + +
+
+ +
+
+

{title}

+

{message}

+
+
+ + +
+
+
+ ); +} diff --git a/msms/frontend/src/components/UI/CrudPage.jsx b/msms/frontend/src/components/UI/CrudPage.jsx new file mode 100644 index 0000000..f0f8403 --- /dev/null +++ b/msms/frontend/src/components/UI/CrudPage.jsx @@ -0,0 +1,180 @@ +// src/components/UI/CrudPage.jsx β€” Reusable CRUD list page +import { useState, useEffect, useCallback } from 'react'; +import { Plus, Edit2, Trash2 } from 'lucide-react'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import toast from 'react-hot-toast'; +import Modal from './Modal'; +import SearchBar from './SearchBar'; +import ConfirmDialog from './ConfirmDialog'; +import Pagination from './Pagination'; + +export default function CrudPage({ + title, + api, + schema, + defaultValues = {}, + columns, // [{key, label, render}] + formFields, // [{name, label, type, options, required, placeholder}] + searchPlaceholder, + getId, // item => id + softDelete = false, + onBuildPayload, // (data) => payload +}) { + const [items, setItems] = useState([]); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [search, setSearch] = useState(''); + const [loading, setLoading] = useState(true); + const [modalOpen, setModalOpen] = useState(false); + const [editing, setEditing] = useState(null); + const [deleteId, setDeleteId] = useState(null); + const LIMIT = 10; + + const { register, handleSubmit, reset, formState: { errors, isSubmitting } } = useForm({ + resolver: zodResolver(schema), + defaultValues, + }); + + const fetchItems = useCallback(async () => { + setLoading(true); + try { + const { data } = await api.getAll({ search, page, limit: LIMIT }); + setItems(data.data || data || []); + setTotal(data.total || 0); + } catch { toast.error(`Failed to load ${title}`); } + finally { setLoading(false); } + }, [search, page]); + + useEffect(() => { fetchItems(); }, [fetchItems]); + + const openAdd = () => { setEditing(null); reset(defaultValues); setModalOpen(true); }; + + const openEdit = (item) => { + setEditing(item); + const vals = {}; + Object.keys(defaultValues).forEach(k => { vals[k] = item[k] ?? defaultValues[k]; }); + reset(vals); + setModalOpen(true); + }; + + const onSubmit = async (data) => { + const payload = onBuildPayload ? onBuildPayload(data) : data; + try { + if (editing) { + await api.update(getId(editing), payload); + toast.success('Updated successfully'); + } else { + await api.create(payload); + toast.success('Created successfully'); + } + setModalOpen(false); + fetchItems(); + } catch (err) { + toast.error(err.response?.data?.error || err.response?.data?.errors?.[0]?.msg || 'Operation failed'); + } + }; + + const handleDelete = async () => { + try { + await api.remove(deleteId); + toast.success(softDelete ? 'Deactivated' : 'Deleted'); + fetchItems(); + } catch { toast.error('Failed'); } + }; + + return ( +
+
+ { setSearch(v); setPage(1); }} placeholder={searchPlaceholder} /> + +
+ +
+
+ + + + {columns.map(c => )} + + + + + {loading ? ( + + ) : items.length === 0 ? ( + + ) : items.map(item => ( + + {columns.map(c => ( + + ))} + + + ))} + +
{c.label}
+
+
No records found
+ {c.render ? c.render(item) : (item[c.key] ?? 'β€”')} + +
+ + +
+
+
+ +
+ + {/* Add/Edit Modal */} + setModalOpen(false)} title={editing ? `Edit ${title}` : `Add ${title}`} size="md"> +
+
+ {formFields.map(f => ( +
+ + {f.type === 'select' ? ( + + ) : f.type === 'textarea' ? ( +