Tribhuvan University
Institute Of Science and Technology
Hetauda City College, Hetauda
LAB Assignment
E-governance
Submitted By: Submitted to:
Ayush Kumar Acharya Mr. Bikash Phuyal
[Link]. CSIT 6TH Semester Lecturer (Department of IT)
Signature:
Contents
1. INTRODUCTION.......................................................................................2
2. PROJECT STRUCTURE.............................................................................2
3. PROGRAMMING LANGUAGE AND TECHNOLOGIES.............................3
4. DATABASE DESIGN.................................................................................4
4.1 Tables & Fields....................................................................................4
4.2 Relationships / Constraints...............................................................5
4.3 Renewal Logic.....................................................................................5
5. CODE........................................................................................................6
5.1 Frontend...............................................................................................6
5.1.1 [Link].....................................................................6
5.2 Backend..............................................................................................12
5.2.1 [Link] (Routes)...................................................................12
6. ENDPOINTS............................................................................................17
7. OUTPUT / FLOW....................................................................................18
7.1 Register..............................................................................................18
7.2 Login...................................................................................................18
7.3 Add First License..............................................................................19
7.4 Admin License Verification.............................................................20
7.5 User Dashboard................................................................................21
7.5.1 View responsive grid of all their licenses........................................21
7.5.2 Add a new license...........................................................................21
7.5.3 Edit an existing license...................................................................22
7.5.3 Delete License................................................................................22
7.5.4 Initiate Renewal Process After Expiry.............................................23
8. CONCLUSION.........................................................................................24
1
1. INTRODUCTION
The License Renewal System is a web application that streamlines management of
business licenses, including registration, license creation, document upload,
verification by an administrator, and renewal (with a mock payment flow). It is
built with a FastAPI backend (REST APIs, authentication, database management)
and a React + TypeScript + Tailwind CSS frontend offering an intuitive dashboard
for users and an admin verification interface.
Core features:
User authentication via JWT
Business license creation with document/image upload
License verification workflow (admin approve / reject)
Expiry tracking & license renewal
Mock payment modal before renewal
Toast-based feedback and responsive UI
2. PROJECT STRUCTURE
2
3. PROGRAMMING LANGUAGE AND
TECHNOLOGIES
Frontend:
TypeScript
React
Vite
Tailwind CSS
Zustand (state management)
Axios (HTTP)
Backend:
Python
FastAPI
SQLAlchemy
Alembic (migrations)
SQLite
JWT (auth)
Passlib / hashing utilities
Uvicorn
Key Components:
`backend/app/[Link]`: FastAPI initialization, mounting static
uploads, router inclusion, CORS setup.
`backend/app/[Link]`: SQLAlchemy models (User, Business,
License, LicenseType).
3
`backend/app/routes/*.py`: Modular API endpoints (auth, admin,
license operations).
`frontend/src/pages/dashboard/[Link]`: User
dashboard (list, add, edit, renew licenses).
`frontend/src/pages/dashboard/[Link]`: Admin
verification grid with image preview.
`frontend/src/pages/dashboard/[Link]`: Mock
payment prior to renewal.
4. DATABASE DESIGN
SQLite database using SQLAlchemy ORM.
4.1 Tables & Fields
1. users
username (PK, string)
password (hashed string)
role (enum: admin, user)
reg_no (FK -> businesses.reg_no, nullable)
2. businesses
reg_no (PK, int, external business registration number)
business_name (string)
phone_number (string)
3. license_types
name (PK, string)
fee (int)
4. licenses
4
id (PK, autoincrement)
license_number (int)
business_reg_no (FK -> businesses.reg_no)
type (FK -> license_types.name)
expiry_date (date)
document_path (string - stored relative path of uploaded image)
verification_status (enum: pending, verified, unverified)
4.2 Relationships / Constraints
One Business → Many Licenses
One LicenseType → Many Licenses
Admin actions restricted via role-based dependency
(`require_admin`).
4.3 Renewal Logic
A license can be renewed only if `expiry_date < today`; renewal
extends expiry (+5 years) after mock payment approval.
5
5. CODE
5.1 Frontend
5.1.1 [Link]
import axios from "axios";
import { format } from "date-fns";
import { useEffect, useState } from "react";
import { FiEdit2, FiPlus, FiRefreshCw, FiTrash2 } from "react-icons/fi";
import { useAuthStore } from "../../store/authStore";
import { toast } from "../../store/toastStore";
import LicenseModal from "./LicenseModal";
import PaymentModal from "./PaymentModal";
interface License {
id: number;
license_number: number;
type: string;
expiry_date: string;
document_path: string;
verification_status: string;
}
interface LicenseType {
name: string;
fee: number;
}
export default function DashboardPage() {
const token = useAuthStore((s) => [Link]);
6
const user = useAuthStore((s) => [Link]);
const [licenses, setLicenses] = useState<License[]>([]);
const [licenseTypes, setLicenseTypes] = useState<LicenseType[]>([]);
const [loading, setLoading] = useState(true);
const [showModal, setShowModal] = useState(false);
const [editLicense, setEditLicense] = useState<License | null>(null);
const [payLicense, setPayLicense] = useState<License | null>(null);
const api = [Link]({
baseURL: [Link].VITE_API_BASE || "[Link]
headers: { Authorization: token ? `Bearer ${token}` : "" },
});
const fetchAll = async () => {
setLoading(true);
try {
const [licRes, typeRes] = await [Link]([
[Link]<License[]>("/licenses/"),
[Link]<LicenseType[]>("/licenses/license_types"),
]);
setLicenses([Link]);
setLicenseTypes([Link]);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchAll();
}, []);
7
const onDelete = async (l: License) => {
if (!confirm("Delete license?")) return;
try {
await [Link](`/licenses/${[Link]}`);
[Link]("License deleted");
fetchAll();
} catch (e: any) {
[Link]([Link]?.data?.detail || "Delete failed");
}
};
const onRenew = (l: License) => {
setPayLicense(l);
};
return (
<div className="max-w-7xl mx-auto p-6">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-8">
<div>
<h1 className="text-2xl font-bold text-govSecondary">My Licenses</h1>
<p className="text-sm text-slate-600 mt-1">
Business Reg No:{" "}
<span className="font-medium">{user?.business_reg_no}</span>
</p>
</div>
</div>
{loading && <p>Loading...</p>}
8
{!loading && [Link] === 0 && (
<div className="border border-dashed border-slate-300 rounded-lg p-12 text-center bg-
white">
<p className="text-slate-600 mb-4">No licenses yet.</p>
<button
onClick={() => {
setEditLicense(null);
setShowModal(true);
}}
className="btn-primary"
>
<FiPlus /> Add First License
</button>
</div>
)}
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
{[Link]((l) => (
<LicenseCard
key={[Link]}
license={l}
onEdit={() => {
setEditLicense(l);
setShowModal(true);
}}
onDelete={() => onDelete(l)}
onRenew={() => onRenew(l)}
/>
9
))}
{licenses && [Link] > 0 && (
<div
className="card flex flex-col items-center justify-center cursor-pointer group min-h-
[250px]"
onClick={() => {
setEditLicense(null);
setShowModal(true);
}}
>
<div className="text-8xl font-bold text-slate-400 group-hover:text-slate-600
transition">
+
</div>
<p className="text-xl font-bold text-slate-500 mt-2 group-hover:text-slate-700
transition">
Add New License
</p>
</div>
)}
</div>
{showModal && (
<LicenseModal
license={editLicense}
onClose={() => {
setShowModal(false);
}}
onSaved={() => {
10
setShowModal(false);
fetchAll();
}}
licenseTypes={licenseTypes}
/>
)}
{payLicense && (
<PaymentModal
license={payLicense}
licenseTypes={licenseTypes}
onClose={() => setPayLicense(null)}
onPaid={async () => {
try {
await [Link](`/licenses/${[Link]}/renew`);
[Link]("License renewed for 5 years");
setPayLicense(null);
fetchAll();
} catch (e: any) {
[Link]([Link]?.data?.detail || "Renew failed");
}
}}
/>
)}
</div> );
}
11
5.2 Backend
5.2.1 [Link] (Routes)
import uuid
from datetime import date, datetime, timedelta, timezone
from pathlib import Path
from fastapi import APIRouter, File, Form, HTTPException, UploadFile
from ..models import License, LicenseType
from ..schema import VerifyStatusEnum
from ..utils import add_file, db_dependency, user_dependency
router = APIRouter(prefix="/licenses", tags=["Licenses"])
BASE_DIR = Path(__file__).resolve().[Link]
UPLOAD_DIR = BASE_DIR / "uploads" / "licenses"
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
@[Link]("/")
def get_licenses_for_business(user: user_dependency, db: db_dependency):
business_reg_no = user.business_reg_no
licenses = (
[Link](License).filter(License.business_reg_no == business_reg_no).all()
)
return licenses
@[Link]("/add")
async def add_license_for_business(
user: user_dependency,
db: db_dependency,
license_number: int = Form(...),
type: str = Form(...),
12
expiry_date: date = Form(...),
document: UploadFile = File(...),
):
filename = f"{str(uuid.uuid4())}_{[Link]}"
file_path = UPLOAD_DIR / filename
await add_file(document, file_path)
try:
new_license = License(
license_number=license_number,
business_reg_no=user.business_reg_no,
type=type,
expiry_date=expiry_date,
document_path=str(file_path.relative_to(BASE_DIR)),
)
[Link](new_license)
[Link]()
[Link](new_license)
return new_license
except Exception as e:
# cleanup orphaned file
if file_path.exists():
file_path.unlink()
raise HTTPException(500, "Failed to save license")
@[Link]("/{license_id}")
async def update_license(
license_id: int,
user: user_dependency,
13
db: db_dependency,
license_number: int = Form(...),
type: str = Form(...),
document: UploadFile | None = File(None),
):
file_path = None
license_obj = (
[Link](License)
.filter(
[Link] == license_id, License.business_reg_no == user.business_reg_no
)
.first()
)
if not license_obj:
raise HTTPException(404, "License not found")
if document:
file_path = BASE_DIR / license_obj.document_path
await add_file(document, file_path)
try:
license_obj.license_number = license_number
license_obj.type = type
license_obj.verification_status = [Link]
[Link](license_obj)
[Link]()
[Link](license_obj)
return license_obj
except Exception:
14
if file_path.exists():
file_path.unlink()
raise HTTPException(status_code=500, detail="Failed to update license")
@[Link]("/{license_id}", status_code=204)
def delete_license(
license_id: int,
user: user_dependency,
db: db_dependency,
):
license_obj = (
[Link](License)
.filter(
[Link] == license_id,
License.business_reg_no == user.business_reg_no,
)
.first()
)
if not license_obj:
raise HTTPException(status_code=404, detail="License not found")
file_path = BASE_DIR / license_obj.document_path
try:
if file_path.exists():
file_path.unlink()
[Link](license_obj)
[Link]()
except Exception:
15
[Link]()
raise HTTPException(
status_code=500,
detail="Failed to delete license",
)
@[Link]("/{license_id}/renew")
def renew_license(db: db_dependency, user: user_dependency, license_id: int):
license_obj = (
[Link](License)
.filter(
[Link] == license_id, License.business_reg_no == user.business_reg_no
)
.first()
)
if not license_obj:
raise HTTPException(404, "License not found")
if [Link]([Link]).date() < license_obj.expiry_date:
raise HTTPException(400, "License needs to be expired to get renewed")
license_obj.expiry_date = license_obj.expiry_date + timedelta(days=365 * 5)
[Link](license_obj)
[Link]()
[Link](license_obj)
return license_obj
@[Link]("/license_types")
def get_license_types(db: db_dependency):
return [Link](LicenseType).all()
16
6. ENDPOINTS
7. OUTPUT / FLOW
17
7.1 Register
User registers with his/her business information, username and
password
7.2 Login
User logins with his/her registered username and pasword.
7.3 Add First License
User add his/her first license.
18
7.4 Admin License Verification
Admin can similarly login with his/her admin credentials where he/she
is redirected to dashboard after successful login. Admin can see all the
19
licenses posted by user/business with their status (pending, verified,
unverified). Admin can now verify or unverify each license.
7.5 User Dashboard
User can perform following functionalities:
20
7.5.1 View responsive grid of all their licenses
7.5.2 Add a new license
User can a new license by clicking on on the card with plus(+)
sign, where a modal is popped. Now he/she can fill the form and
submit the license as did in 7.3 (Add First License)
21
7.5.3 Edit an existing license
7.5.3 Delete License
User can easily delete license just by clicking on delete button on
license card.
22
7.5.4 Initiate Renewal Process After Expiry
After license is expired, renew button is appeard on card where
user can click the button to initaite renewal process starting with mock
payment. After successful renewal, the expiry date extends by 5 years.
Click On Renew Button
Pay Fee For Renewal
23
License is renewed and expiry date extends by 5
years
8. CONCLUSION
The License Renewal System successfully delivers a secure, role-based platform
for managing business licenses. The FastAPI backend handles authentication,
license operations, and verification, while the React + TypeScript frontend
provides a responsive interface for users and administrators. Core workflows—
license submission, renewal with mock payment, and admin approval—function
smoothly with clear feedback. With its modular design and clean UI, the system is
both maintainable and extensible, ready for future improvements like real payment
integration, expiry reminders, and deployment hardening.
24