KAAA Tender Management System
Complete Work Guide
1. System Overview & Architecture
1.1 Introduction
The KAAA Tender Management System is a comprehensive, web-based platform designed to digitize and streamline the entire tender lifecycle for infrastructure and construction projects. Built on modern web technologies, it provides role-based access for all stakeholders including administrators, project managers, finance officers, board members, and external vendors.
1.2 System Objectives
- Digitize the complete tender process from project inception to contract award
- Provide transparent, auditable workflows for public procurement compliance
- Enable efficient vendor management and bid evaluation
- Generate automated comparative analysis and reports
- Facilitate digital board review and voting processes
- Maintain comprehensive audit trails for accountability
1.3 Technology Stack
| Layer | Technology | Version | Purpose |
| Frontend Framework | Next.js (App Router) | 15.2.3 | React-based full-stack framework with SSR/SSG |
| Language | TypeScript | 5.8.2 | Type-safe JavaScript development |
| Database | PostgreSQL | 14+ | Relational database for persistent storage |
| ORM | Prisma | 6.6.0 | Type-safe database client and migrations |
| API Layer | tRPC | 11.0.0 | End-to-end type-safe API procedures |
| Authentication | NextAuth.js | 5.0.0-beta.25 | JWT-based auth with role management |
| UI Framework | Tailwind CSS | 4.0.15 | Utility-first CSS framework |
| UI Components | shadcn/ui + Base UI | 4.1.0 / 1.3.0 | Pre-built accessible component library |
| State Management | TanStack React Query | 5.69.0 | Server state management and caching |
| Validation | Zod | 3.24.2 | Schema validation and type inference |
| Email Service | Nodemailer | 8.0.4 | Email notifications and alerts |
| Export Tools | jsPDF, xlsx | 4.2.1 / 0.18.5 | PDF and Excel generation |
1.4 High-Level Architecture
┌─────────────────────────────────────────────────────────────┐
│ Browser Client │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Next.js App │ │ React Query │ │ UI (shadcn) │ │
│ │ (Pages) │ │ (State) │ │ + Tailwind │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ tRPC Client │ │
│ └───────┬────────┘ │
└────────────────────────────┼──────────────────────────────┘
│ HTTPS
┌────────────────────────────┼──────────────────────────────┐
│ Next.js Server (API) │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ tRPC Router │ │ NextAuth.js │ │ API Routes │ │
│ │ (Procedures)│ │ (Auth) │ │ (REST) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ┌──────▼────────────────▼────────────────▼──────┐ │
│ │ Business Logic Layer │ │
│ │ (Services: bid, tender, vendor, etc.) │ │
│ └──────────────────────┬────────────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Prisma Client │ │
│ └────────┬────────┘ │
└────────────────────────────┼──────────────────────────────┘
│
┌────────▼────────┐
│ PostgreSQL DB │
│ (Prisma ORM) │
└─────────────────┘
2. Installation & Configuration
2.1 Prerequisites Checklist
| Requirement | Minimum Version | Recommended | Check Command |
| Node.js | v18.0.0 | v20 LTS | node --version |
| npm | v8.0.0 | v11+ | npm --version |
| PostgreSQL | v14 | v16 | psql --version |
| Git | v2.30 | v2.40+ | git --version |
2.2 Complete Installation Steps
Step 1: Clone the Repository
git clone https://github.com/your-org/kaaa-tender.git
cd kaaa-tender
Step 2: Install Node.js Dependencies
npm install
What this does: Installs all packages defined in package.json. The postinstall script automatically runs prisma generate to create the Prisma client.
Step 3: PostgreSQL Database Setup
Create a new database:
# Connect to PostgreSQL as superuser
psql -U postgres
# Create database
CREATE DATABASE kaaa_tender;
# Create a dedicated user (optional but recommended)
CREATE USER kaaa_user WITH PASSWORD 'your_secure_password';
GRANT ALL PRIVILEGES ON DATABASE kaaa_tender TO kaaa_user;
# Exit psql
\q
Step 4: Environment Configuration
Create a .env file in the project root:
# Authentication Secret (generate with: openssl rand -base64 32)
AUTH_SECRET="your-super-secret-key-min-32-characters-long"
# Database Connection
DATABASE_URL="postgresql://kaaa_user:your_secure_password@localhost:5432/kaaa_tender?schema=public"
# Environment
NODE_ENV="development"
# Optional: Email Configuration (for Nodemailer)
# SMTP_HOST="smtp.gmail.com"
# SMTP_PORT="587"
# SMTP_USER="your-email@gmail.com"
# SMTP_PASS="your-app-password"
# SMTP_FROM="noreply@kaaa.gov"
Security Warning:
- Never commit the
.env file to version control
- Use strong, unique secrets in production
- Restrict database user permissions appropriately
Step 5: Database Schema Deployment
For Development:
# Push schema directly (no migration history)
npm run db:push
# Seed with initial data
npm run db:seed
For Production or Team Development:
# Create a migration from schema changes
npm run db:generate
# Apply all pending migrations
npm run db:migrate
Verify database setup:
# Open Prisma Studio to visually inspect the database
npm run db:studio
Step 6: Start Development Server
2.3 Verifying Installation
| Check | Expected Result | Command |
| Dependencies installed | node_modules/ directory exists | ls node_modules | head |
| Prisma Client generated | src/generated/prisma/ exists | ls src/generated/prisma |
| Database connected | No connection errors in terminal | Check dev server output |
| Page loads | Next.js welcome or login page | Open localhost:3000 |
| TypeScript valid | No type errors | npm run typecheck |
3. User Roles & Permissions Matrix
3.1 Role Hierarchy
SUPER_ADMIN
│
┌────────┼────────┐
│ │ │
ADMIN_STAFF PROJECT_MANAGER FINANCE_OFFICER
│ │
│ │
└─────BOARD_MEMBER←─┘
│
│
VENDOR (External)
3.2 Detailed Role Permissions
SUPER_ADMIN
| Area | Permissions |
| User Management | Create, edit, delete any user; assign any role; activate/deactivate users |
| System Settings | Modify system-wide settings; configure board quorum; manage fiscal years |
| Projects | Full CRUD access to all projects; override any restrictions |
| Tenders | Create, publish, cancel, re-tender; override status changes |
| Vendors | Approve/reject vendor registrations; manage verification status |
| Bids | View all bids; override approvals/rejections; access all versions |
| Board | Configure board members; set required quorum; view all decisions |
| Audit | Full access to audit logs; export audit reports |
| Database | Direct database access through Prisma Studio; raw queries |
PROJECT_MANAGER
| Area | Permissions |
| Projects | Create and manage own projects; assign team members |
| Items | Add items to projects; create project-specific items; set quantities |
| Tender Bundles | Create bundles; add items; set estimated rates |
| Tender Notices | Create and publish tenders; set deadlines; define eligibility |
| Vendors | Invite vendors to tenders; view vendor profiles |
| Bids | View submitted bids; approve/reject bid submissions |
| Analysis | Generate comparative analysis; prepare board submissions |
| Reports | View project reports; export bid comparisons |
ADMIN_STAFF
| Area | Permissions |
| Projects | View all projects; assist with data entry; update statuses |
| Vendors | Register new vendors; verify documents; update vendor status |
| Tenders | Assist with tender creation; manage invitations |
| Bids | View bids; assist with approval workflow; track submissions |
| Notifications | Send notifications; manage communication logs |
| Reports | Generate and export reports; maintain documentation |
FINANCE_OFFICER
| Area | Permissions |
| Budget | View project budgets; track expenditures; approve financial aspects |
| Bids | Review bid amounts; validate financial compliance |
| Rates | Set and update item rates; manage fiscal year rates |
| Awards | Review award amounts; validate financial approvals |
| Reports | Financial reports; budget vs. actual analysis; export to Excel |
BOARD_MEMBER
| Area | Permissions |
| Analysis | View comparative analysis; download bid details |
| Decisions | Vote on tenders (Approve/Reject/Return); add remarks |
| Awards | View award recommendations; review vendor qualifications |
| Reports | View board-specific reports; track decision history |
VENDOR
| Area | Permissions |
| Profile | View and update own profile; upload verification documents |
| Tenders | View invited tenders; download tender documents |
| Bids | Submit bids (Draft/Pending); edit until deadline; withdraw bids |
| Tracking | Track bid status; view award results; download award letters |
3.3 Permission Matrix Summary
| Feature | SA | PM | AS | FO | BM | VE |
| Manage Users | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Create Projects | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ |
| Edit All Projects | ✓ | ✓ (own) | ✓ (assist) | ✗ | ✗ | ✗ |
| Create Tenders | ✓ | ✓ | ✓ (assist) | ✗ | ✗ | ✗ |
| Publish Tenders | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ |
| Invite Vendors | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
| Submit Bids | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| Approve Bids | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ |
| Board Vote | ✓ | ✗ | ✗ | ✗ | ✓ | ✗ |
| Set Item Rates | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ |
| View Audit Logs | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
SA=Super Admin, PM=Project Manager, AS=Admin Staff, FO=Finance Officer, BM=Board Member, VE=Vendor
4. Complete Workflow Processes
4.1 Project Management Workflow
4.1.1 Project Lifecycle
┌─────────────┐
│ Project │
│ Initiated │
└──────┬──────┘
│
▼
┌─────────────┐ ┌─────────────┐
│ Define │────▶│ Add Items │
│ Details │ │ & Budget │
└──────┬──────┘ └──────┬──────┘
│ │
└────────┬──────────┘
▼
┌─────────────┐
│ Create │
│ Tender │
│ Bundle │
└──────┬──────┘
│
▼
┌─────────────┐
│ Publish │
│ Tender │
└─────────────┘
4.1.2 Step-by-Step: Creating a Project
Step 1: Navigate to Project Creation
- Log in as SUPER_ADMIN or PROJECT_MANAGER
- From the dashboard, click on "Projects" in the sidebar
- Click the "Create New Project" button (top right)
Step 2: Fill Project Details
| Field | Required | Description | Example |
| Project Code | Yes | Unique identifier | PRJ-2026-001 |
| Project Name | Yes | Descriptive name | Highway Extension Phase 3 |
| Description | No | Detailed scope | Extension of NH-45 from km 120-150 |
| Division | No | Administrative division | North Division |
| Province | No | Geographic province | Bagmati |
| Budget | Yes | Total project budget (NPR) | 50,000,000 |
| Fiscal Year | Yes | Select from active fiscal years | 2083/84 |
| Project Type | No | Category of project | Road Construction |
Step 3: Save and Verify
- Click "Save Project"
- Verify the project appears in the project list
- Note the auto-generated Project ID for reference
4.1.3 Adding Items to a Project
Method A: Add from Master Catalog
- Open the project → "Items" tab
- Click "Add Existing Item"
- Search/filter items by category
- Select items and specify:
- Quantity: Required amount (e.g., 500 bags of cement)
- Estimated Rate: Expected cost per unit
- Weight: Importance factor for analysis (0-1)
- Notes: Any specifications
- Click "Add to Project"
Method B: Create Project-Specific Items
- Open the project → "Items" tab
- Click "Create New Item"
- Fill in item details:
- Code: Unique project item code
- Name: Item description
- Unit: Measurement unit (kg, m³, hrs, etc.)
- Weight: Analysis weight (optional)
- Save the item (it's linked to this project only)
4.2 Tender Creation & Management
4.2.1 Tender Types
| Type | Description | Use Case | Bundle Type |
| PROJECT | Tender for a specific project | Single construction project with defined items | TenderBundle |
| CATEGORY | Tender for a category of items across projects | Common items needed by multiple projects (e.g., cement, steel) | CategoryBundle |
4.2.2 Creating a Project Tender
Step 1: Create Tender Bundle
- Navigate to Project → "Tender Bundles" tab
- Click "Create Bundle"
- Enter bundle details:
- Code: Unique bundle identifier (e.g., TB-2026-001)
- Name: Descriptive name
- Description: Scope of work
- Add items from the project to the bundle
- Set estimated rates and quantities for each item
- Save the bundle (status:
DRAFT)
Step 2: Create Tender Notice
- Navigate to "Tender Notices" section
- Click "Create Tender Notice"
- Fill in the form:
| Field | Description |
| Reference No | Unique tender reference (auto-generated) |
| Title | Clear, descriptive title |
| Description | Detailed scope and requirements |
| Tender Type | PROJECT or CATEGORY |
| Bundle | Link to created bundle |
| Fiscal Year | Select active fiscal year |
| Eligibility | Criteria vendors must meet |
| Submission Deadline | Date and time (UTC) |
| Auto-Approve Bids | If enabled, bids auto-approve (use with caution) |
- Save as
DRAFT
Step 3: Publish the Tender
- Review all tender details
- Click "Publish Tender"
- Confirm the publication
- Status changes to
PUBLISHED
- Tender becomes visible to invited vendors
Note: Once published, most details cannot be edited. Create a new version if changes are needed.
4.2.3 Creating a Category Tender
- Navigate to "Category Bundles" section
- Create a new Category Bundle:
- Select Category (e.g., "Construction Materials")
- Select Fiscal Year
- Add items from the category's master catalog
- Include relevant projects that need these items
- Create Tender Notice linked to the Category Bundle
- Publish the tender
Key Difference: Category tenders aggregate demand across multiple projects, potentially getting better bulk pricing from vendors.
4.3 Vendor Management
4.3.1 Vendor Registration Process
Vendor registers online
│
▼
┌─────────────────┐
│ Submit │
│ Registration │
│ + Documents │
└────────┬────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐
│ Admin Staff │────▶│ Verify │
│ Reviews │ │ Documents │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Set Status: │ │ Set │
│ PENDING/ │ │ Verification: │
│ APPROVED/ │ │ PENDING/ │
│ REJECTED │ │ VERIFIED/ │
│ │ │ REJECTED │
└─────────────────┘ └─────────────────┘
4.3.2 Step-by-Step: Vendor Registration (Admin)
- Log in as ADMIN_STAFF or SUPER_ADMIN
- Navigate to "Vendors" → "Register New Vendor"
- Fill vendor details:
| Field | Required | Description |
| Name | Yes | Company name |
| Email | Yes | Primary contact email (unique) |
| Phone | No | Contact number |
| VAT/PAN | Yes | Tax registration number (unique) |
| Address | No | Registered address |
| Contact Person | No | Representative name |
- Request document uploads (VAT cert, PAN cert, registration docs)
- Set initial status to
PENDING
- Save vendor record
Document Verification
- Open the vendor record
- Review uploaded documents
- Verify document authenticity
- Update
verificationStatus:
PENDING → Still reviewing
VERIFIED → Documents authentic
REJECTED → Documents invalid (specify reason)
- Update
status:
APPROVED → Vendor can participate in tenders
REJECTED → Vendor cannot participate
DEACTIVATED → Temporary suspension
4.3.3 Inviting Vendors to Tenders
- Open the published Tender Notice
- Navigate to "Invited Vendors" tab
- Click "Invite Vendors"
- Filter vendors by:
- Verification status (show only VERIFIED)
- Category expertise
- Previous performance
- Select vendors from the list
- Click "Send Invitations"
- System sends email notifications automatically
Email Notification: Vendors receive an email with:
- Tender reference and title
- Submission deadline
- Link to view tender details
- Instructions for bid submission
4.4 Bid Submission Process
4.4.1 Vendor Bid Submission Workflow
┌─────────────────────────────────┐
│ Vendor logs into Portal │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ Views Invited Tenders │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ Opens Tender Details │
│ - Reads scope │
│ - Downloads documents │
│ - Reviews items & quantities │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ Starts Bid Preparation │
│ - Enters quoted rates │
│ - Reviews total amount │
│ - Saves as DRAFT │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ Submits Bid │
│ Status: SUBMITTED │
└────────────┬────────────────────┘
│
▼
┌─────────────────────────────────┐
│ Awaits Approval │
│ - Can withdraw before deadline │
└─────────────────────────────────┘
4.4.2 Step-by-Step: Vendor Submitting a Bid
Step 1: Access the Tender
- Vendor logs into the Vendor Portal
- Navigate to "My Tenders" or "Invited Tenders"
- Click on the tender reference to view details
- Review all tender information carefully
Step 2: Prepare the Bid
- Click "Prepare Bid" or "Edit Bid"
- For each item in the bundle:
- Enter Quoted Rate (rate per unit)
- System calculates Total Amount = Quoted Rate × Quantity
- Review the bid summary:
| Field | Calculation |
| Total Amount | Sum of all (Quoted Rate × Quantity) |
| Item Count | Number of items in bid |
| Weighted Score | Calculated during analysis |
- Save as
DRAFT to continue later
Step 3: Submit the Bid
- Review all quoted rates
- Confirm total amount is within budget
- Click "Submit Bid"
- Confirm submission (cannot edit after submission deadline)
- Bid status changes to
SUBMITTED
- Receive confirmation email
Important:
- Bids cannot be edited after the submission deadline
- Vendors can withdraw bids before the deadline
- Multiple submissions create new versions (tracked in BidVersion)
4.4.3 Bid Status Flow
| Status | Description | Who Sets It |
DRAFT | Vendor is preparing the bid | Vendor |
PENDING_APPROVAL | Bid submitted, awaiting internal approval | System (auto) |
SUBMITTED | Bid approved and officially submitted | Admin/PM |
WITHDRAWN | Vendor withdrew the bid | Vendor |
APPROVED | Bid approved for analysis | Admin/PM |
REJECTED | Bid rejected (with reason) | Admin/PM |
4.4.4 Internal Bid Approval
- Admin/PM receives notification of new bid submission
- Navigate to Tender → "Bid Submissions"
- Review each bid:
- Check all items have quoted rates
- Verify total amount is reasonable
- Check submission timing (before deadline)
- Take action:
- Approve: Bid moves to
APPROVED status for analysis
- Reject: Provide reason, bid status =
REJECTED
- System notifies vendor of approval/rejection
4.5 Comparative Analysis
4.5.1 Analysis Overview
Once all bids are approved, the system generates a comparative analysis that allows evaluators to compare vendor bids across all items, considering both price and weighted importance.
4.5.2 Step-by-Step: Generating Comparative Analysis
Step 1: Verify Bid Status
- Navigate to Tender → "Bid Submissions"
- Ensure submission deadline has passed
- Verify all bids are either
APPROVED or REJECTED
- Only
APPROVED bids are included in analysis
Step 2: Generate Analysis
- Click "Generate Comparative Analysis"
- System compiles data:
- All approved bids
- Item-wise comparison of quoted rates
- Weighted scoring based on item importance
- Total amounts per vendor
- Analysis is saved with status
DRAFT
Step 3: Review Analysis
- Open the Comparative Analysis
- Review the comparison table:
| Column | Description |
| Item | Item name and code |
| Quantity | Required quantity |
| Est. Rate | Estimated rate (budget) |
| Vendor A Rate | Quoted rate by Vendor A |
| Vendor B Rate | Quoted rate by Vendor B |
| ... | ... |
| Weight | Item importance factor |
| Lowest Bidder | Vendor with lowest rate per item |
- Review scoring data (if configured):
- Price score (lower rate = higher score)
- Weighted total score per vendor
- Recommended vendor (highest score)
Step 4: Submit to Board
- Add analysis recommendations
- Specify any special conditions
- Click "Submit to Board for Review"
- Tender status changes to
BOARD_REVIEW
- Board members receive notifications
4.5.3 Comparative Analysis Output
The analysis can be exported in multiple formats:
| Format | Use Case |
| PDF | Print-ready reports, official documentation |
| Excel | Detailed analysis, custom calculations |
| JSON | System integration, further processing |
4.6 Board Review & Decision
4.6.1 Board Review Process
Comparative Analysis Submitted
│
▼
┌─────────────────────────────┐
│ Board Members Notified │
│ (Email + System Alert) │
└────────────┬────────────────┘
│
▼
┌─────────────────────────────┐
│ Board Accesses Analysis │
│ - Reviews all bids │
│ - Compares rates │
│ - Checks vendor credentials│
└────────────┬────────────────┘
│
▼
┌─────────────────────────────┐
│ Each Member Votes: │
│ - APPROVED │
│ - REJECTED │
│ - RETURNED (needs changes) │
└────────────┬────────────────┘
│
▼
┌─────────────────────────────┐
│ System Checks Quorum │
│ - Required: 3 members │
│ - Quorum: 2 votes │
└────────────┬────────────────┘
│
├── Quorum NOT met ──▶ Wait for more votes
│
└── Quorum met ──▶ Calculate decision
│
├── Majority APPROVED ──▶ Award Tender
├── Majority REJECTED ──▶ Re-tender
└── Majority RETURNED ──▶ Revise Analysis
4.6.2 Board Member: Reviewing and Voting
- Board Member logs in (role:
BOARD_MEMBER or SUPER_ADMIN)
- Navigate to "Board Review" or "My Reviews"
- Open the assigned tender analysis
- Review all materials:
- Comparative analysis table
- Vendor bid details
- Recommendations from staff
- Vendor verification status
- Cast vote:
- APPROVED: Recommend awarding to recommended vendor
- REJECTED: Recommend rejecting all bids (with remarks)
- RETURNED: Request revision of analysis (with comments)
- Add remarks (optional but recommended)
- Submit vote (cannot be changed after submission)
4.6.3 Quorum and Decision Rules
| Configuration | Default Value | Description |
| Required Members | 3 | Minimum board size for validity |
| Quorum Count | 2 | Minimum votes needed to decide |
Decision Calculation:
- Count all votes (APPROVED, REJECTED, RETURNED)
- If
RETURNED votes > 0 → Status = RETURNED
- Else if
APPROVED votes > REJECTED votes → Status = APPROVED
- Else → Status = REJECTED
4.7 Award Management
4.7.1 Award Process
Step 1: Create Award Record
- After board approval, navigate to Tender → "Awards"
- Click "Create Award Record"
- Fill award details:
| Field | Description |
| Award Reference | Unique award ID (auto-generated) |
| Vendor | Winning vendor (from approved bids) |
| Total Amount | Final award amount (from bid) |
| Fiscal Year | Current fiscal year |
| Awarded At | Date of award (auto-set) |
- Upload award letter (PDF) if available
- Save award record
Step 2: Generate Award Letter
- Open the award record
- Click "Generate Award Letter"
- System creates PDF with:
- Tender reference and title
- Vendor details
- Award amount
- Terms and conditions
- Signature fields
- Download or email the letter to vendor
Step 3: Reject Other Bidders
- System automatically creates rejection records for non-winning vendors
- For each rejected bid:
- Generate rejection letter
- Specify reason (e.g., "Not the lowest bidder", "Non-compliant")
- Upload rejection letter
- Notify vendors via email
Step 4: Update Tender Status
- Tender status changes to
AWARDED
- Award details visible in reports
- Project status updated to reflect award
4.7.2 Award Rejection and Re-tendering
If all bids are rejected or no suitable bidder:
- Set Tender Status to
REJECTED or RE_TENDERED
- Notify all participating vendors
- Review why bids were rejected:
- Prices too high?
- Non-compliant specifications?
- Vendor disqualification?
- Create a new tender (possibly with revised specifications)
- Link to original project
5. Technical Architecture
5.1 Application Structure
5.1.1 Next.js App Router Structure
src/app/
├── (dashboard)/ # Protected dashboard routes
│ ├── layout.tsx # Dashboard layout with sidebar/nav
│ ├── page.tsx # Dashboard home/overview
│ ├── projects/ # Project management pages
│ ├── tenders/ # Tender management pages
│ ├── vendors/ # Vendor management pages
│ ├── reports/ # Reports and analytics
│ └── board/ # Board review pages
├── vendor-portal/ # Vendor-facing routes
│ ├── layout.tsx # Vendor portal layout
│ ├── page.tsx # Vendor dashboard
│ ├── tenders/ # Available tenders
│ └── my-bids/ # Vendor's bid history
├── login/
│ └── page.tsx # Login page
├── register/
│ └── page.tsx # Registration page
├── layout.tsx # Root layout (providers, fonts)
└── page.tsx # Root page (redirect to dashboard)
5.1.2 Server Structure (tRPC + Services)
src/server/
├── routers/ # tRPC routers (API endpoints)
│ ├── auth.router.ts # Authentication procedures
│ ├── project.router.ts # Project CRUD
│ ├── tender.router.ts # Tender management
│ ├── vendor.router.ts # Vendor operations
│ ├── boq.router.ts # Bill of Quantities
│ ├── report.router.ts # Reporting procedures
│ ├── notification.router.ts# User notifications
│ └── index.ts # Router aggregation
├── services/ # Business logic layer
│ ├── project.service.ts # Project operations
│ ├── tender.service.ts # Tender operations
│ ├── bid.service.ts # Bid management
│ ├── vendor.service.ts # Vendor operations
│ ├── award.service.ts # Award processing
│ ├── analysis.service.ts # Comparative analysis
│ ├── board.service.ts # Board decisions
│ ├── rate.service.ts # Item rate management
│ ├── category.service.ts # Category operations
│ └── audit.service.ts # Audit logging
├── trpc.ts # tRPC initialization
└── db.ts # Prisma client instance
5.2 tRPC Procedure Types and Middleware
5.2.1 Procedure Hierarchy
// publicProcedure - No authentication required
// Used for: Login, registration, public pages
export const publicProcedure = t.procedure;
// protectedProcedure - Any authenticated user
// Used for: Dashboard access, user profile
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session?.user) throw new TRPCError({ code: "UNAUTHORIZED" });
return next({ ctx: { session: { ...ctx.session, user: ctx.session.user } } });
});
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
// adminProcedure - Admin roles only
// Used for: Project creation, tender management
export const adminProcedure = protectedProcedure.use(
enforceRole(["SUPER_ADMIN", "ADMIN_STAFF", "PROJECT_MANAGER"])
);
// superAdminProcedure - Super admin only
// Used for: User management, system settings
export const superAdminProcedure = protectedProcedure.use(
enforceRole(["SUPER_ADMIN"])
);
// financeProcedure - Finance roles
// Used for: Rate setting, financial approvals
export const financeProcedure = protectedProcedure.use(
enforceRole(["SUPER_ADMIN", "FINANCE_OFFICER"])
);
// boardProcedure - Board members
// Used for: Board review, voting
export const boardProcedure = protectedProcedure.use(
enforceRole(["SUPER_ADMIN", "BOARD_MEMBER"])
);
// vendorProcedure - Vendor only
// Used for: Bid submission, vendor portal
export const vendorProcedure = protectedProcedure.use(
enforceRole(["VENDOR"])
);
5.2.2 Creating a Protected Router
// Example: project.router.ts
import { createTRPCRouter, adminProcedure, protectedProcedure } from "../trpc";
import { z } from "zod";
export const projectRouter = createTRPCRouter({
// Public list (authenticated users only)
list: protectedProcedure.query(async ({ ctx }) => {
return ctx.db.project.findMany({
where: { ... }
});
}),
// Create (admin only)
create: adminProcedure
.input(z.object({
name: z.string().min(3),
code: z.string(),
budget: z.number().positive(),
fiscalYearId: z.string(),
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.project.create({
data: { ...input, createdById: ctx.session.user.id }
});
}),
// Delete (super admin only)
delete: superAdminProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
return ctx.db.project.delete({ where: { id: input.id } });
}),
});
5.3 Data Flow Example
Vendor submits bid:
===================
1. Browser (Vendor Portal)
│
│ Click "Submit Bid"
│
▼
2. tRPC Client (useTRPC hook)
│
│ trpc.bid.submit.mutate({ tenderId, lineItems })
│
▼
3. Next.js API Handler (tRPC)
│
│ Receives request, validates input with Zod
│ Checks authentication (vendorProcedure)
│
▼
4. Bid Router (bid.router.ts)
│
│ Calls bidService.submitBid(...)
│
▼
5. Bid Service (bid.service.ts)
│
│ - Validates tender is PUBLISHED
│ - Validates deadline not passed
│ - Creates BidSubmission record
│ - Creates BidLineItem records
│ - Updates status to SUBMITTED
│ - Logs action to AuditLog
│ - Sends notification to admins
│
▼
6. Prisma Client
│
│ Executes SQL: INSERT INTO BidSubmission...
│
▼
7. PostgreSQL Database
│
│ Data persisted
│
▼
8. Response flows back:
Database → Prisma → Service → Router → tRPC → Client
│
▼
9. UI Updates
│
│ React Query invalidates cache
│ Bid status updates in UI
│ Confirmation message shown
6. Database Schema Reference
6.1 Entity Relationship Overview
The database is designed with PostgreSQL using Prisma ORM. The schema supports the complete tender lifecycle with proper relationships and audit trails.
6.2 Key Models
User Model
model User {
id String @id @default(cuid())
name String
email String @unique
password String // bcryptjs hashed
role Role @default(VENDOR)
isActive Boolean @default(true)
emailVerified DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
accounts Account[]
sessions Session[]
projects Project[] // Projects created by user
comparativeAnalysis ComparativeAnalysis[] // Analysis prepared by user
auditLogs AuditLog[]
boardDecisions BoardDecision[]
notifications Notification[]
categoryBundles CategoryBundle[] // Bundles created by user
bundleRevisions CategoryBundleRevision[]
approvedBids BidSubmission[] @relation("BidApprovals")
rejectedBids BidSubmission[] @relation("BidRejections")
}
Vendor Model
model Vendor {
id String @id @default(cuid())
name String
email String @unique
phone String?
vatPan String @unique
address String?
contactPerson String?
status VendorStatus @default(PENDING)
verificationStatus VendorVerificationStatus @default(PENDING)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
bidSubmissions BidSubmission[]
rejectionRecords RejectionRecord[]
awardRecords AwardRecord[]
tenderInvites TenderVendor[]
documents VendorDocument[]
}
BidSubmission Model
model BidSubmission {
id String @id @default(cuid())
tenderId String
vendorId String
referenceNo String @unique
status BidStatus @default(DRAFT)
totalAmount Decimal? @db.Decimal(15, 2)
submittedAt DateTime?
approvedAt DateTime?
approvedById String?
rejectedAt DateTime?
rejectedById String?
rejectionReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
tender TenderNotice @relation(fields: [tenderId], references: [id])
approvedBy User? @relation("BidApprovals", fields: [approvedById], references: [id])
rejectedBy User? @relation("BidRejections", fields: [rejectedById], references: [id])
vendor Vendor @relation(fields: [vendorId], references: [id])
bidLineItems BidLineItem[]
versions BidVersion[]
@@unique([tenderId, vendorId]) // One bid per vendor per tender
}
TenderNotice Model
model TenderNotice {
id String @id @default(cuid())
referenceNo String @unique
bundleId String?
fiscalYearId String
title String
description String?
eligibility String?
submissionDeadline DateTime
status TenderStatus @default(DRAFT)
tenderType TenderType @default(PROJECT)
projectId String?
categoryBundleId String? @unique
publishedAt DateTime?
autoApproveBids Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
bundle TenderBundle? @relation(fields: [bundleId], references: [id])
categoryBundle CategoryBundle? @relation(fields: [categoryBundleId], references: [id])
fiscalYear FiscalYear @relation(fields: [fiscalYearId], references: [id])
project Project? @relation(fields: [projectId], references: [id])
bidSubmissions BidSubmission[]
comparativeAnalysis ComparativeAnalysis[]
awardRecord AwardRecord?
vendorInvites TenderVendor[]
}
6.3 Enums (Lookup Values)
| Enum | Values | Purpose |
| Role | SUPER_ADMIN, PROJECT_MANAGER, ADMIN_STAFF, FINANCE_OFFICER, BOARD_MEMBER, VENDOR | User access levels |
| TenderStatus | DRAFT, PUBLISHED, SUBMISSIONS_RECEIVED, UNDER_ANALYSIS, BOARD_REVIEW, AWARDED, REJECTED, RE_TENDERED | Tender lifecycle |
| BidStatus | DRAFT, PENDING_APPROVAL, SUBMITTED, WITHDRAWN, APPROVED, REJECTED | Bid lifecycle |
| VendorStatus | PENDING, APPROVED, REJECTED, DEACTIVATED | Vendor approval status |
| VendorVerificationStatus | PENDING, VERIFIED, REJECTED | Document verification |
| BoardDecisionStatus | PENDING, APPROVED, REJECTED, RETURNED | Board vote options |
| TenderType | PROJECT, CATEGORY | Tender categorization |
| BundleStatus | DRAFT, PUBLISHED, AWARDED, CANCELLED | Bundle status |
6.4 Relationship Diagram (Simplified)
User (1) ────> (N) Project
│
│ (1)
▼
TenderBundle (1) ────> (N) TenderBundleItem
│ │
│ (1) │ (N)
▼ ▼
TenderNotice (1) ────> (N) BidSubmission
│
│ (1)
▼
BidLineItem
(per item bid)
User (1) ────> (N) BoardDecision
ComparativeAnalysis (1) ──> (N) BoardDecision
Vendor (1) ────> (N) BidSubmission
FiscalYear (1) ────> (N) TenderNotice
FiscalYear (1) ────> (N) Project
7. API & tRPC Router Documentation
7.1 Router List
auth.router.ts
| Procedure | Type | Input | Output |
| getSession | publicProcedure | None | Session | null |
| login | publicProcedure | { email, password } | Session |
| logout | protectedProcedure | None | Success |
| register | publicProcedure | { name, email, password, ... } | User |
project.router.ts
| Procedure | Type | Input | Output |
| list | protectedProcedure | { fiscalYearId? } | Project[] |
| getById | protectedProcedure | { id } | Project with relations |
| create | adminProcedure | { name, code, budget, ... } | Project |
| update | adminProcedure | { id, data } | Project |
| delete | superAdminProcedure | { id } | Success |
| addItem | adminProcedure | { projectId, itemId, quantity, ... } | ProjectItem |
tender.router.ts
| Procedure | Type | Input | Output |
| list | protectedProcedure | { status?, type? } | TenderNotice[] |
| getById | protectedProcedure | { id } | TenderNotice with relations |
| create | adminProcedure | { title, bundleId, deadline, ... } | TenderNotice |
| publish | adminProcedure | { id } | TenderNotice |
| inviteVendors | adminProcedure | { tenderId, vendorIds[] } | Success |
| getBids | adminProcedure | { tenderId } | BidSubmission[] |
vendor.router.ts
| Procedure | Type | Input | Output |
| list | adminProcedure | { status?, verification? } | Vendor[] |
| getById | adminProcedure | { id } | Vendor with relations |
| register | adminProcedure | { name, email, vatPan, ... } | Vendor |
| updateStatus | adminProcedure | { id, status, verificationStatus } | Vendor |
| getInvitedTenders | vendorProcedure | None | TenderNotice[] |
boq.router.ts (Bid Submission)
| Procedure | Type | Input | Output |
| submitBid | vendorProcedure | { tenderId, lineItems[] } | BidSubmission |
| updateBid | vendorProcedure | { bidId, lineItems[] } | BidSubmission |
| withdrawBid | vendorProcedure | { bidId } | BidSubmission |
| approveBid | adminProcedure | { bidId } | BidSubmission |
| rejectBid | adminProcedure | { bidId, reason } | BidSubmission |
| getMyBids | vendorProcedure | None | BidSubmission[] |
7.2 Using tRPC in Components
// Example: Fetching projects in a component
import { api } from "@/trpc/client";
function ProjectList() {
// Query data
const { data: projects, isLoading } = api.project.list.useQuery({
fiscalYearId: "2026-27"
});
// Mutation
const createProject = api.project.create.useMutation({
onSuccess: () => {
// Refetch projects after creation
utils.project.list.invalidate();
}
});
if (isLoading) return <div>Loading...</div>;
return (
<div>
{projects?.map(project => (
<div key={project.id}>{project.name}</div>
))}
</div>
);
}
8. Frontend Component Guide
8.1 Component Organization
src/components/
├── ui/ # Base shadcn/ui components
│ ├── button.tsx
│ ├── input.tsx
│ ├── dialog.tsx
│ ├── table.tsx
│ └── ...
├── bid/ # Bid-related components
│ ├── BidComparisonTable.tsx
│ ├── BidStatusBadge.tsx
│ ├── VendorBidFilters.tsx
│ ├── VendorInviteButton.tsx
│ ├── BidApprovalCard.tsx
│ └── index.ts
├── items/ # Item management components
│ ├── item-selector.tsx
│ ├── project-specific-item-creator.tsx
│ ├── item-source-badge.tsx
│ ├── sort-dropdown.tsx
│ ├── export-button.tsx
│ └── index.ts
└── ... # Other feature components
8.2 Key Component Patterns
Server Component (Data Fetching)
// src/app/(dashboard)/projects/[id]/page.tsx
import { api } from "@/trpc/server";
export default async function ProjectPage({ params }: { params: { id: string } }) {
// Fetch data on the server
const project = await api.project.getById({ id: params.id });
return (
<div>
<h1>{project.name}</h1>
<p>Budget: {project.budget}</p>
</div>
);
}
Client Component (Interactive)
"use client";
import { api } from "@/trpc/client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
export function CreateBidForm({ tenderId }: { tenderId: string }) {
const [lineItems, setLineItems] = useState([]);
const utils = api.useUtils();
const submitBid = api.boq.submitBid.useMutation({
onSuccess: () => {
utils.boq.getMyBids.invalidate();
}
});
return (
<form onSubmit={(e) => {
e.preventDefault();
submitBid.mutate({ tenderId, lineItems });
}}>
{/* Form fields */}
<Button type="submit" disabled={submitBid.isPending}>
Submit Bid
</Button>
</form>
);
}
8.3 Custom Hooks
// src/lib/hooks/use-projects.ts
import { api } from "@/trpc/client";
export function useProjects(fiscalYearId?: string) {
return api.project.list.useQuery(
{ fiscalYearId },
{ enabled: !!fiscalYearId }
);
}
export function useCreateProject() {
const utils = api.useUtils();
return api.project.create.useMutation({
onSuccess: () => {
utils.project.list.invalidate();
}
});
}
// Usage in component
function ProjectList() {
const { data, isLoading } = useProjects("2026-27");
const createProject = useCreateProject();
// ...
}
9. Maintenance & Administration
9.1 Database Maintenance
Backup Procedures
# Create a backup
pg_dump -U kaaa_user -d kaaa_tender > backup_$(date +%Y%m%d).sql
# Restore from backup
psql -U kaaa_user -d kaaa_tender < backup_20260429.sql
Prisma Migration Best Practices
| Scenario | Command | Notes |
| Development change | npm run db:push | Quick, no migration history |
| Team development | npm run db:generate | Creates migration file |
| Production deploy | npm run db:migrate | Applies pending migrations |
| Reset database | npx prisma migrate reset | Development only! Deletes data |
Seed Data Management
The seed script (prisma/seed.ts) populates:
- Fiscal years
- Categories and project types
- Master item catalog
- Admin users
- System settings
# Run seed
npm run db:seed
9.2 User Management
Creating Admin Users
- Log in as SUPER_ADMIN
- Navigate to "User Management"
- Click "Create User"
- Fill details and assign role
- User receives email with temporary password
- User must change password on first login
Resetting User Password
Password reset is handled via NextAuth.js. In development, you can:
- Access the database directly via Prisma Studio
- Update the user's password hash with a bcrypt hash of a new password
- Or implement a "Forgot Password" flow with email reset links
9.3 Audit Log Review
Audit logs track all significant actions:
| Field | Description |
| userId | Who performed the action |
| action | What was done (e.g., "CREATE_PROJECT") |
| entityType | Type of object (e.g., "Project") |
| entityId | ID of the affected object |
| entityName | Name for display |
| changes | JSON diff of before/after (if update) |
| ipAddress | Source IP for security tracking |
| createdAt | When the action occurred |
Access audit logs via: SUPER_ADMIN → "Audit Logs"
9.4 System Settings
| Setting Key | Description | Default |
| board.requiredMembers | Minimum board size | 3 |
| board.quorumCount | Minimum votes to decide | 2 |
| bid.deadlineBufferHours | Hours before deadline to lock edits | 0 |
| notification.enableEmail | Send email notifications | true |
10. Troubleshooting & FAQ
10.1 Common Issues
Issue: "Database connection failed"
Symptoms: Server crashes on startup with Prisma errors
Causes & Solutions:
- PostgreSQL not running
# Check PostgreSQL status (macOS)
brew services list | grep postgres
# Start PostgreSQL
brew services start postgresql
- Wrong DATABASE_URL
- Check
.env file
- Verify username, password, host, port, database name
- Test connection:
psql "postgresql://user:pass@localhost:5432/dbname"
- Prisma Client not generated
npx prisma generate
Issue: "Port 3000 already in use"
# Find process using port 3000
lsof -i :3000
# Kill the process
kill -9 <PID>
# OR use a different port
npm run dev -- -p 3001
Issue: "TypeScript errors during build"
# Check for type errors
npm run typecheck
# Fix errors reported
# Common issues:
# - Missing null checks
# - Incorrect API response types
# - Prisma model changes not reflected
Issue: "tRPC procedure returns 403 Forbidden"
- Check user role in database:
npm run db:studio
# Navigate to User table, check role field
- Verify the procedure type:
publicProcedure → No auth needed
protectedProcedure → Any logged-in user
adminProcedure → Admin roles only
- Check session is being passed correctly
Issue: "Bid submission fails"
- Verify tender status is
PUBLISHED
- Check submission deadline has not passed
- Ensure vendor is invited to the tender
- Verify all line items have valid quoted rates
- Check vendor status is
APPROVED
10.2 FAQ
Q: Can I change a published tender?
A: No, once a tender is published, most fields cannot be edited. You must create a new version or re-tender.
Q: How do I add a new user role?
A: Update the Role enum in prisma/schema.prisma, run migration, then update the role checks in src/server/trpc.ts.
Q: Can vendors edit submitted bids?
A: Vendors can edit bids that are in DRAFT status. Once SUBMITTED, they can only withdraw (before deadline).
Q: How is the comparative analysis calculated?
A: The system compares quoted rates across vendors, applies item weights, and calculates a weighted score. The formula is customizable in src/server/services/analysis.service.ts.
Q: Can I export data to Excel?
A: Yes, use the export buttons in the UI. The system uses the xlsx library for Excel generation and jsPDF for PDF exports.
Q: How do I configure email notifications?
A: Set SMTP environment variables in .env file. The system uses Nodemailer for sending emails.
Q: What happens if the board doesn't reach quorum?
A: The decision is pending until enough board members vote. Configure quorum requirements in BoardConfig.
11. Deployment Guide
11.1 Deployment Options
| Platform | Difficulty | Recommended For |
| Vercel | Easy | Most deployments, automatic CI/CD |
| Docker | Medium | Self-hosted, custom infrastructure |
| Node.js Server | Medium | VPS or dedicated server |
| Netlify | Easy | Static export (limited features) |
11.2 Deploying to Vercel (Recommended)
Step 1: Push to Git
git add .
git commit -m "Ready for deployment"
git push origin main
Step 2: Import Project in Vercel
- Sign up at https://vercel.com
- Click "New Project"
- Import your Git repository
- Vercel detects Next.js automatically
Step 3: Configure Environment Variables
In Vercel project settings, add:
| Variable | Value |
| AUTH_SECRET | Strong secret (generate: openssl rand -base64 32) |
| DATABASE_URL | Production PostgreSQL URL (use Vercel Postgres or external) |
| NODE_ENV | production |
| SMTP_* | Email service credentials (if using email) |
Step 4: Deploy
- Click "Deploy"
- Vercel runs
npm run build automatically
- On success, you get a production URL
- Run database migrations:
vercel env pull .env.production
npx prisma migrate deploy
11.3 Docker Deployment
Create Dockerfile
# Dockerfile
FROM node:20-alpine AS base
# Install dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci
# Build
COPY . .
RUN npm run build
# Production
FROM node:20-alpine
WORKDIR /app
COPY --from=base /app/.next .next
COPY --from=base /app/node_modules node_modules
COPY --from=base /app/package.json .
COPY --from=base /app/prisma prisma
EXPOSE 3000
CMD ["npm", "start"]
Build and Run
# Build image
docker build -t kaaa-tender .
# Run container
docker run -d \
-p 3000:3000 \
-e DATABASE_URL="postgresql://..." \
-e AUTH_SECRET="..." \
--name kaaa-tender \
kaaa-tender
11.4 Production Checklist
| Item | Status | Notes |
| Set NODE_ENV=production | ☐ | Critical for performance |
| Strong AUTH_SECRET | ☐ | Minimum 32 characters |
| Database migrations run | ☐ | Use prisma migrate deploy |
| Database backups configured | ☐ | Automated daily backups |
| Email service configured | ☐ | Test email sending |
| HTTPS enabled | ☐ | SSL certificate installed |
| Error monitoring setup | ☐ | Sentry, LogRocket, etc. |
| Logging configured | ☐ | Centralized log storage |
| Rate limiting enabled | ☐ | Prevent abuse |
12. Appendices
Appendix A: Environment Variables Reference
| Variable | Required | Description | Example |
| AUTH_SECRET | Yes | NextAuth secret for JWT encryption | xyz123... (32+ chars) |
| DATABASE_URL | Yes | PostgreSQL connection string | postgresql://user:pass@host:5432/db |
| NODE_ENV | Yes | Environment mode | development | production |
| SMTP_HOST | No | SMTP server host | smtp.gmail.com |
| SMTP_PORT | No | SMTP server port | 587 |
| SMTP_USER | No | SMTP username | email@gmail.com |
| SMTP_PASS | No | SMTP password/app password | xxxx xxxx xxxx xxxx |
| SMTP_FROM | No | Default from address | noreply@kaaa.gov |
Appendix B: Keyboard Shortcuts
| Shortcut | Action | Context |
| Ctrl + S | Save current form | Forms |
| Ctrl + Enter | Submit form | Bid submission |
| Esc | Close dialog/cancel | All dialogs |
| Ctrl + N | New record (project, tender, etc.) | List pages |
| F5 | Refresh data | All pages |
Appendix C: Glossary
| Term | Definition |
| Tender | Formal invitation to vendors to submit bids for a project |
| Bid | Vendor's response to a tender, including quoted rates |
| Bundle | Grouping of items for tendering purposes |
| BOQ | Bill of Quantities - detailed list of materials and labor |
| Comparative Analysis | Side-by-side comparison of all vendor bids |
| Quorum | Minimum number of board members required to vote |
| Award | Formal decision to grant contract to a vendor |
| Fiscal Year | Financial year (e.g., 2083/84 in Nepali calendar) |
| Master Item | Standard item in the catalog, reusable across projects |
| Project-Specific Item | Custom item created for a single project |
Appendix D: Contact & Support
Technical Support
- Documentation: Refer to inline code comments and this guide
- Issues: Report bugs via the project's issue tracker
- Technology Docs:
KAAA Tender Management System - Complete Work Guide
Version 0.1.0 | Last Updated: April 2026
Document generated for internal use. Do not distribute without authorization.