KAAA Tender Management System
Complete Work Guide

Table of Contents

  1. System Overview & Architecture
  2. Installation & Configuration
  3. User Roles & Permissions Matrix
  4. Complete Workflow Processes
    1. Project Management Workflow
    2. Tender Creation & Management
    3. Vendor Management
    4. Bid Submission Process
    5. Comparative Analysis
    6. Board Review & Decision
    7. Award Management
  5. Technical Architecture
  6. Database Schema Reference
  7. API & tRPC Router Documentation
  8. Frontend Component Guide
  9. Maintenance & Administration
  10. Troubleshooting & FAQ
  11. Deployment Guide
  12. Appendices

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

1.3 Technology Stack

LayerTechnologyVersionPurpose
Frontend FrameworkNext.js (App Router)15.2.3React-based full-stack framework with SSR/SSG
LanguageTypeScript5.8.2Type-safe JavaScript development
DatabasePostgreSQL14+Relational database for persistent storage
ORMPrisma6.6.0Type-safe database client and migrations
API LayertRPC11.0.0End-to-end type-safe API procedures
AuthenticationNextAuth.js5.0.0-beta.25JWT-based auth with role management
UI FrameworkTailwind CSS4.0.15Utility-first CSS framework
UI Componentsshadcn/ui + Base UI4.1.0 / 1.3.0Pre-built accessible component library
State ManagementTanStack React Query5.69.0Server state management and caching
ValidationZod3.24.2Schema validation and type inference
Email ServiceNodemailer8.0.4Email notifications and alerts
Export ToolsjsPDF, xlsx4.2.1 / 0.18.5PDF 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

RequirementMinimum VersionRecommendedCheck Command
Node.jsv18.0.0v20 LTSnode --version
npmv8.0.0v11+npm --version
PostgreSQLv14v16psql --version
Gitv2.30v2.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:

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

npm run dev

Server starts at http://localhost:3000 with Turbopack for fast refresh.

2.3 Verifying Installation

CheckExpected ResultCommand
Dependencies installednode_modules/ directory existsls node_modules | head
Prisma Client generatedsrc/generated/prisma/ existsls src/generated/prisma
Database connectedNo connection errors in terminalCheck dev server output
Page loadsNext.js welcome or login pageOpen localhost:3000
TypeScript validNo type errorsnpm 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

AreaPermissions
User ManagementCreate, edit, delete any user; assign any role; activate/deactivate users
System SettingsModify system-wide settings; configure board quorum; manage fiscal years
ProjectsFull CRUD access to all projects; override any restrictions
TendersCreate, publish, cancel, re-tender; override status changes
VendorsApprove/reject vendor registrations; manage verification status
BidsView all bids; override approvals/rejections; access all versions
BoardConfigure board members; set required quorum; view all decisions
AuditFull access to audit logs; export audit reports
DatabaseDirect database access through Prisma Studio; raw queries

PROJECT_MANAGER

AreaPermissions
ProjectsCreate and manage own projects; assign team members
ItemsAdd items to projects; create project-specific items; set quantities
Tender BundlesCreate bundles; add items; set estimated rates
Tender NoticesCreate and publish tenders; set deadlines; define eligibility
VendorsInvite vendors to tenders; view vendor profiles
BidsView submitted bids; approve/reject bid submissions
AnalysisGenerate comparative analysis; prepare board submissions
ReportsView project reports; export bid comparisons

ADMIN_STAFF

AreaPermissions
ProjectsView all projects; assist with data entry; update statuses
VendorsRegister new vendors; verify documents; update vendor status
TendersAssist with tender creation; manage invitations
BidsView bids; assist with approval workflow; track submissions
NotificationsSend notifications; manage communication logs
ReportsGenerate and export reports; maintain documentation

FINANCE_OFFICER

AreaPermissions
BudgetView project budgets; track expenditures; approve financial aspects
BidsReview bid amounts; validate financial compliance
RatesSet and update item rates; manage fiscal year rates
AwardsReview award amounts; validate financial approvals
ReportsFinancial reports; budget vs. actual analysis; export to Excel

BOARD_MEMBER

AreaPermissions
AnalysisView comparative analysis; download bid details
DecisionsVote on tenders (Approve/Reject/Return); add remarks
AwardsView award recommendations; review vendor qualifications
ReportsView board-specific reports; track decision history

VENDOR

AreaPermissions
ProfileView and update own profile; upload verification documents
TendersView invited tenders; download tender documents
BidsSubmit bids (Draft/Pending); edit until deadline; withdraw bids
TrackingTrack bid status; view award results; download award letters

3.3 Permission Matrix Summary

FeatureSAPMASFOBMVE
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

  1. Log in as SUPER_ADMIN or PROJECT_MANAGER
  2. From the dashboard, click on "Projects" in the sidebar
  3. Click the "Create New Project" button (top right)

Step 2: Fill Project Details

FieldRequiredDescriptionExample
Project CodeYesUnique identifierPRJ-2026-001
Project NameYesDescriptive nameHighway Extension Phase 3
DescriptionNoDetailed scopeExtension of NH-45 from km 120-150
DivisionNoAdministrative divisionNorth Division
ProvinceNoGeographic provinceBagmati
BudgetYesTotal project budget (NPR)50,000,000
Fiscal YearYesSelect from active fiscal years2083/84
Project TypeNoCategory of projectRoad Construction

Step 3: Save and Verify

  1. Click "Save Project"
  2. Verify the project appears in the project list
  3. Note the auto-generated Project ID for reference

4.1.3 Adding Items to a Project

Method A: Add from Master Catalog

  1. Open the project → "Items" tab
  2. Click "Add Existing Item"
  3. Search/filter items by category
  4. 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
  5. Click "Add to Project"

Method B: Create Project-Specific Items

  1. Open the project → "Items" tab
  2. Click "Create New Item"
  3. Fill in item details:
    • Code: Unique project item code
    • Name: Item description
    • Unit: Measurement unit (kg, m³, hrs, etc.)
    • Weight: Analysis weight (optional)
  4. Save the item (it's linked to this project only)

4.2 Tender Creation & Management

4.2.1 Tender Types

TypeDescriptionUse CaseBundle Type
PROJECTTender for a specific projectSingle construction project with defined itemsTenderBundle
CATEGORYTender for a category of items across projectsCommon items needed by multiple projects (e.g., cement, steel)CategoryBundle

4.2.2 Creating a Project Tender

Step 1: Create Tender Bundle

  1. Navigate to Project → "Tender Bundles" tab
  2. Click "Create Bundle"
  3. Enter bundle details:
    • Code: Unique bundle identifier (e.g., TB-2026-001)
    • Name: Descriptive name
    • Description: Scope of work
  4. Add items from the project to the bundle
  5. Set estimated rates and quantities for each item
  6. Save the bundle (status: DRAFT)

Step 2: Create Tender Notice

  1. Navigate to "Tender Notices" section
  2. Click "Create Tender Notice"
  3. Fill in the form:
    FieldDescription
    Reference NoUnique tender reference (auto-generated)
    TitleClear, descriptive title
    DescriptionDetailed scope and requirements
    Tender TypePROJECT or CATEGORY
    BundleLink to created bundle
    Fiscal YearSelect active fiscal year
    EligibilityCriteria vendors must meet
    Submission DeadlineDate and time (UTC)
    Auto-Approve BidsIf enabled, bids auto-approve (use with caution)
  4. Save as DRAFT

Step 3: Publish the Tender

  1. Review all tender details
  2. Click "Publish Tender"
  3. Confirm the publication
  4. Status changes to PUBLISHED
  5. 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

  1. Navigate to "Category Bundles" section
  2. 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
  3. Create Tender Notice linked to the Category Bundle
  4. 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)

  1. Log in as ADMIN_STAFF or SUPER_ADMIN
  2. Navigate to "Vendors" → "Register New Vendor"
  3. Fill vendor details:
    FieldRequiredDescription
    NameYesCompany name
    EmailYesPrimary contact email (unique)
    PhoneNoContact number
    VAT/PANYesTax registration number (unique)
    AddressNoRegistered address
    Contact PersonNoRepresentative name
  4. Request document uploads (VAT cert, PAN cert, registration docs)
  5. Set initial status to PENDING
  6. Save vendor record

Document Verification

  1. Open the vendor record
  2. Review uploaded documents
  3. Verify document authenticity
  4. Update verificationStatus:
    • PENDING → Still reviewing
    • VERIFIED → Documents authentic
    • REJECTED → Documents invalid (specify reason)
  5. Update status:
    • APPROVED → Vendor can participate in tenders
    • REJECTED → Vendor cannot participate
    • DEACTIVATED → Temporary suspension

4.3.3 Inviting Vendors to Tenders

  1. Open the published Tender Notice
  2. Navigate to "Invited Vendors" tab
  3. Click "Invite Vendors"
  4. Filter vendors by:
    • Verification status (show only VERIFIED)
    • Category expertise
    • Previous performance
  5. Select vendors from the list
  6. Click "Send Invitations"
  7. System sends email notifications automatically
Email Notification: Vendors receive an email with:

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

  1. Vendor logs into the Vendor Portal
  2. Navigate to "My Tenders" or "Invited Tenders"
  3. Click on the tender reference to view details
  4. Review all tender information carefully

Step 2: Prepare the Bid

  1. Click "Prepare Bid" or "Edit Bid"
  2. For each item in the bundle:
    • Enter Quoted Rate (rate per unit)
    • System calculates Total Amount = Quoted Rate × Quantity
  3. Review the bid summary:
    FieldCalculation
    Total AmountSum of all (Quoted Rate × Quantity)
    Item CountNumber of items in bid
    Weighted ScoreCalculated during analysis
  4. Save as DRAFT to continue later

Step 3: Submit the Bid

  1. Review all quoted rates
  2. Confirm total amount is within budget
  3. Click "Submit Bid"
  4. Confirm submission (cannot edit after submission deadline)
  5. Bid status changes to SUBMITTED
  6. Receive confirmation email
Important:

4.4.3 Bid Status Flow

StatusDescriptionWho Sets It
DRAFTVendor is preparing the bidVendor
PENDING_APPROVALBid submitted, awaiting internal approvalSystem (auto)
SUBMITTEDBid approved and officially submittedAdmin/PM
WITHDRAWNVendor withdrew the bidVendor
APPROVEDBid approved for analysisAdmin/PM
REJECTEDBid rejected (with reason)Admin/PM

4.4.4 Internal Bid Approval

  1. Admin/PM receives notification of new bid submission
  2. Navigate to Tender → "Bid Submissions"
  3. Review each bid:
    • Check all items have quoted rates
    • Verify total amount is reasonable
    • Check submission timing (before deadline)
  4. Take action:
    • Approve: Bid moves to APPROVED status for analysis
    • Reject: Provide reason, bid status = REJECTED
  5. 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

  1. Navigate to Tender → "Bid Submissions"
  2. Ensure submission deadline has passed
  3. Verify all bids are either APPROVED or REJECTED
  4. Only APPROVED bids are included in analysis

Step 2: Generate Analysis

  1. Click "Generate Comparative Analysis"
  2. System compiles data:
    • All approved bids
    • Item-wise comparison of quoted rates
    • Weighted scoring based on item importance
    • Total amounts per vendor
  3. Analysis is saved with status DRAFT

Step 3: Review Analysis

  1. Open the Comparative Analysis
  2. Review the comparison table:
    ColumnDescription
    ItemItem name and code
    QuantityRequired quantity
    Est. RateEstimated rate (budget)
    Vendor A RateQuoted rate by Vendor A
    Vendor B RateQuoted rate by Vendor B
    ......
    WeightItem importance factor
    Lowest BidderVendor with lowest rate per item
  3. 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

  1. Add analysis recommendations
  2. Specify any special conditions
  3. Click "Submit to Board for Review"
  4. Tender status changes to BOARD_REVIEW
  5. Board members receive notifications

4.5.3 Comparative Analysis Output

The analysis can be exported in multiple formats:

FormatUse Case
PDFPrint-ready reports, official documentation
ExcelDetailed analysis, custom calculations
JSONSystem 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

  1. Board Member logs in (role: BOARD_MEMBER or SUPER_ADMIN)
  2. Navigate to "Board Review" or "My Reviews"
  3. Open the assigned tender analysis
  4. Review all materials:
    • Comparative analysis table
    • Vendor bid details
    • Recommendations from staff
    • Vendor verification status
  5. Cast vote:
    • APPROVED: Recommend awarding to recommended vendor
    • REJECTED: Recommend rejecting all bids (with remarks)
    • RETURNED: Request revision of analysis (with comments)
  6. Add remarks (optional but recommended)
  7. Submit vote (cannot be changed after submission)

4.6.3 Quorum and Decision Rules

ConfigurationDefault ValueDescription
Required Members3Minimum board size for validity
Quorum Count2Minimum votes needed to decide
Decision Calculation:

4.7 Award Management

4.7.1 Award Process

Step 1: Create Award Record

  1. After board approval, navigate to Tender → "Awards"
  2. Click "Create Award Record"
  3. Fill award details:
    FieldDescription
    Award ReferenceUnique award ID (auto-generated)
    VendorWinning vendor (from approved bids)
    Total AmountFinal award amount (from bid)
    Fiscal YearCurrent fiscal year
    Awarded AtDate of award (auto-set)
  4. Upload award letter (PDF) if available
  5. Save award record

Step 2: Generate Award Letter

  1. Open the award record
  2. Click "Generate Award Letter"
  3. System creates PDF with:
    • Tender reference and title
    • Vendor details
    • Award amount
    • Terms and conditions
    • Signature fields
  4. Download or email the letter to vendor

Step 3: Reject Other Bidders

  1. System automatically creates rejection records for non-winning vendors
  2. For each rejected bid:
    • Generate rejection letter
    • Specify reason (e.g., "Not the lowest bidder", "Non-compliant")
    • Upload rejection letter
  3. Notify vendors via email

Step 4: Update Tender Status

  1. Tender status changes to AWARDED
  2. Award details visible in reports
  3. Project status updated to reflect award

4.7.2 Award Rejection and Re-tendering

If all bids are rejected or no suitable bidder:

  1. Set Tender Status to REJECTED or RE_TENDERED
  2. Notify all participating vendors
  3. Review why bids were rejected:
    • Prices too high?
    • Non-compliant specifications?
    • Vendor disqualification?
  4. Create a new tender (possibly with revised specifications)
  5. 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)

EnumValuesPurpose
RoleSUPER_ADMIN, PROJECT_MANAGER, ADMIN_STAFF, FINANCE_OFFICER, BOARD_MEMBER, VENDORUser access levels
TenderStatusDRAFT, PUBLISHED, SUBMISSIONS_RECEIVED, UNDER_ANALYSIS, BOARD_REVIEW, AWARDED, REJECTED, RE_TENDEREDTender lifecycle
BidStatusDRAFT, PENDING_APPROVAL, SUBMITTED, WITHDRAWN, APPROVED, REJECTEDBid lifecycle
VendorStatusPENDING, APPROVED, REJECTED, DEACTIVATEDVendor approval status
VendorVerificationStatusPENDING, VERIFIED, REJECTEDDocument verification
BoardDecisionStatusPENDING, APPROVED, REJECTED, RETURNEDBoard vote options
TenderTypePROJECT, CATEGORYTender categorization
BundleStatusDRAFT, PUBLISHED, AWARDED, CANCELLEDBundle 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

ProcedureTypeInputOutput
getSessionpublicProcedureNoneSession | null
loginpublicProcedure{ email, password }Session
logoutprotectedProcedureNoneSuccess
registerpublicProcedure{ name, email, password, ... }User

project.router.ts

ProcedureTypeInputOutput
listprotectedProcedure{ fiscalYearId? }Project[]
getByIdprotectedProcedure{ id }Project with relations
createadminProcedure{ name, code, budget, ... }Project
updateadminProcedure{ id, data }Project
deletesuperAdminProcedure{ id }Success
addItemadminProcedure{ projectId, itemId, quantity, ... }ProjectItem

tender.router.ts

ProcedureTypeInputOutput
listprotectedProcedure{ status?, type? }TenderNotice[]
getByIdprotectedProcedure{ id }TenderNotice with relations
createadminProcedure{ title, bundleId, deadline, ... }TenderNotice
publishadminProcedure{ id }TenderNotice
inviteVendorsadminProcedure{ tenderId, vendorIds[] }Success
getBidsadminProcedure{ tenderId }BidSubmission[]

vendor.router.ts

ProcedureTypeInputOutput
listadminProcedure{ status?, verification? }Vendor[]
getByIdadminProcedure{ id }Vendor with relations
registeradminProcedure{ name, email, vatPan, ... }Vendor
updateStatusadminProcedure{ id, status, verificationStatus }Vendor
getInvitedTendersvendorProcedureNoneTenderNotice[]

boq.router.ts (Bid Submission)

ProcedureTypeInputOutput
submitBidvendorProcedure{ tenderId, lineItems[] }BidSubmission
updateBidvendorProcedure{ bidId, lineItems[] }BidSubmission
withdrawBidvendorProcedure{ bidId }BidSubmission
approveBidadminProcedure{ bidId }BidSubmission
rejectBidadminProcedure{ bidId, reason }BidSubmission
getMyBidsvendorProcedureNoneBidSubmission[]

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

ScenarioCommandNotes
Development changenpm run db:pushQuick, no migration history
Team developmentnpm run db:generateCreates migration file
Production deploynpm run db:migrateApplies pending migrations
Reset databasenpx prisma migrate resetDevelopment only! Deletes data

Seed Data Management

The seed script (prisma/seed.ts) populates:

# Run seed
npm run db:seed

9.2 User Management

Creating Admin Users

  1. Log in as SUPER_ADMIN
  2. Navigate to "User Management"
  3. Click "Create User"
  4. Fill details and assign role
  5. User receives email with temporary password
  6. User must change password on first login

Resetting User Password

Password reset is handled via NextAuth.js. In development, you can:

  1. Access the database directly via Prisma Studio
  2. Update the user's password hash with a bcrypt hash of a new password
  3. Or implement a "Forgot Password" flow with email reset links

9.3 Audit Log Review

Audit logs track all significant actions:

FieldDescription
userIdWho performed the action
actionWhat was done (e.g., "CREATE_PROJECT")
entityTypeType of object (e.g., "Project")
entityIdID of the affected object
entityNameName for display
changesJSON diff of before/after (if update)
ipAddressSource IP for security tracking
createdAtWhen the action occurred

Access audit logs via: SUPER_ADMIN → "Audit Logs"

9.4 System Settings

Setting KeyDescriptionDefault
board.requiredMembersMinimum board size3
board.quorumCountMinimum votes to decide2
bid.deadlineBufferHoursHours before deadline to lock edits0
notification.enableEmailSend email notificationstrue

10. Troubleshooting & FAQ

10.1 Common Issues

Issue: "Database connection failed"

Symptoms: Server crashes on startup with Prisma errors

Causes & Solutions:

  1. PostgreSQL not running
    # Check PostgreSQL status (macOS)
    brew services list | grep postgres
    
    # Start PostgreSQL
    brew services start postgresql
  2. Wrong DATABASE_URL
    • Check .env file
    • Verify username, password, host, port, database name
    • Test connection: psql "postgresql://user:pass@localhost:5432/dbname"
  3. 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"

  1. Check user role in database:
    npm run db:studio
    # Navigate to User table, check role field
  2. Verify the procedure type:
    • publicProcedure → No auth needed
    • protectedProcedure → Any logged-in user
    • adminProcedure → Admin roles only
  3. Check session is being passed correctly

Issue: "Bid submission fails"

  1. Verify tender status is PUBLISHED
  2. Check submission deadline has not passed
  3. Ensure vendor is invited to the tender
  4. Verify all line items have valid quoted rates
  5. 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

PlatformDifficultyRecommended For
VercelEasyMost deployments, automatic CI/CD
DockerMediumSelf-hosted, custom infrastructure
Node.js ServerMediumVPS or dedicated server
NetlifyEasyStatic 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

  1. Sign up at https://vercel.com
  2. Click "New Project"
  3. Import your Git repository
  4. Vercel detects Next.js automatically

Step 3: Configure Environment Variables

In Vercel project settings, add:

VariableValue
AUTH_SECRETStrong secret (generate: openssl rand -base64 32)
DATABASE_URLProduction PostgreSQL URL (use Vercel Postgres or external)
NODE_ENVproduction
SMTP_*Email service credentials (if using email)

Step 4: Deploy

  1. Click "Deploy"
  2. Vercel runs npm run build automatically
  3. On success, you get a production URL
  4. 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

ItemStatusNotes
Set NODE_ENV=productionCritical for performance
Strong AUTH_SECRETMinimum 32 characters
Database migrations runUse prisma migrate deploy
Database backups configuredAutomated daily backups
Email service configuredTest email sending
HTTPS enabledSSL certificate installed
Error monitoring setupSentry, LogRocket, etc.
Logging configuredCentralized log storage
Rate limiting enabledPrevent abuse

12. Appendices

Appendix A: Environment Variables Reference

VariableRequiredDescriptionExample
AUTH_SECRETYesNextAuth secret for JWT encryptionxyz123... (32+ chars)
DATABASE_URLYesPostgreSQL connection stringpostgresql://user:pass@host:5432/db
NODE_ENVYesEnvironment modedevelopment | production
SMTP_HOSTNoSMTP server hostsmtp.gmail.com
SMTP_PORTNoSMTP server port587
SMTP_USERNoSMTP usernameemail@gmail.com
SMTP_PASSNoSMTP password/app passwordxxxx xxxx xxxx xxxx
SMTP_FROMNoDefault from addressnoreply@kaaa.gov

Appendix B: Keyboard Shortcuts

ShortcutActionContext
Ctrl + SSave current formForms
Ctrl + EnterSubmit formBid submission
EscClose dialog/cancelAll dialogs
Ctrl + NNew record (project, tender, etc.)List pages
F5Refresh dataAll pages

Appendix C: Glossary

TermDefinition
TenderFormal invitation to vendors to submit bids for a project
BidVendor's response to a tender, including quoted rates
BundleGrouping of items for tendering purposes
BOQBill of Quantities - detailed list of materials and labor
Comparative AnalysisSide-by-side comparison of all vendor bids
QuorumMinimum number of board members required to vote
AwardFormal decision to grant contract to a vendor
Fiscal YearFinancial year (e.g., 2083/84 in Nepali calendar)
Master ItemStandard item in the catalog, reusable across projects
Project-Specific ItemCustom item created for a single project

Appendix D: Contact & Support

Technical Support


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.