v1

🔧 Extending the System

This guide explains how to add new features to the Dhanam Finance platform, including new API modules, dashboard pages, and mobile app screens.

Adding a New Backend Module

Step 1: Create the Module Structure

src/modules/your-module/
├── route.ts           # Route definitions
├── controller.ts      # Request handlers
├── validation.ts      # Zod schemas (optional)
└── service.ts         # Business logic (optional)

Step 2: Define Routes

// src/modules/your-module/route.ts
import { FastifyInstance } from "fastify";
import { getItems, createItem } from "./controller.js";

export default async function yourModuleRoutes(fastify: FastifyInstance) {
  // List items
  fastify.get(
    "/",
    { preHandler: [fastify.authenticateAdmin, fastify.hasPermission("your-module:view")] },
    getItems
  );

  // Create item
  fastify.post(
    "/",
    { preHandler: [fastify.authenticateAdmin, fastify.hasPermission("your-module:create")] },
    createItem
  );
}

Step 3: Implement Controller

// src/modules/your-module/controller.ts
import { FastifyRequest, FastifyReply } from "fastify";

export async function getItems(request: FastifyRequest, reply: FastifyReply) {
  const db = request.server.mongo.db;
  const items = await db.collection("your_items").find({}).toArray();
  return reply.send({ success: true, data: items });
}

export async function createItem(request: FastifyRequest, reply: FastifyReply) {
  const db = request.server.mongo.db;
  const body = request.body as any;
  
  const result = await db.collection("your_items").insertOne({
    ...body,
    createdAt: new Date(),
    createdBy: request.user.userId
  });
  
  return reply.status(201).send({ 
    success: true, 
    message: "Item created",
    id: result.insertedId
  });
}

Step 4: Register in Server

// src/server.ts
import yourModuleRoutes from "./modules/your-module/route.js";

// ... other imports

server.register(yourModuleRoutes, { prefix: "/admin/your-module" });

Step 5: Add Permissions

Update the roles in the database to include new permissions:

// Add to admin role permissions array
"your-module:view",
"your-module:create",
"your-module:edit",
"your-module:delete"

Adding a New Dashboard Page

Step 1: Create Page Component

// app/(main)/your-module/page.tsx
"use client";

import { useEffect, useState } from "react";
import { PageTitle } from "@/components/page-title";
import { api } from "@/lib/api";

export default function YourModulePage() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await api.get("/admin/your-module");
        setItems(response.data.data);
      } finally {
        setLoading(false);
      }
    }
    fetchData();
  }, []);

  if (loading) return <div>Loading...</div>;

  return (
    <div className="p-6">
      <PageTitle title="Your Module" />
      {/* Your content here */}
    </div>
  );
}

Step 2: Add to Navigation

// components/nav-admin.tsx
// Add to the navigation items array
{
  title: "Your Module",
  url: "/your-module",
  icon: IconName,
  permission: "your-module:view"
}

Step 3: Add API Function

// lib/your-module-api.ts
import { api } from "./api";

export async function getItems() {
  const response = await api.get("/admin/your-module");
  return response.data;
}

export async function createItem(data: any) {
  const response = await api.post("/admin/your-module", data);
  return response.data;
}

Adding a New Mobile Screen

Step 1: Create Screen Widget

// lib/screens/your_screen.dart
import 'package:flutter/material.dart';
import '../services/api_service.dart';

class YourScreen extends StatefulWidget {
  const YourScreen({super.key});

  @override
  State<YourScreen> createState() => _YourScreenState();
}

class _YourScreenState extends State<YourScreen> {
  List<dynamic> items = [];
  bool isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    try {
      final response = await ApiService.getData('/mobile/your-endpoint');
      setState(() {
        items = response['data'];
        isLoading = false;
      });
    } catch (e) {
      setState(() => isLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Your Screen')),
      body: isLoading
          ? const Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: items.length,
              itemBuilder: (context, index) {
                return ListTile(title: Text(items[index]['name']));
              },
            ),
    );
  }
}

Step 2: Add Navigation

// In home_screen.dart or navigation
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => const YourScreen()),
);

Adding a Database Collection

Step 1: Define Indexes in db.ts

// src/plugins/db.ts
// Add after other index definitions

try {
  const db = fastify.mongo.db;
  if (db) {
    const yourCollection = db.collection("your_items");
    await yourCollection.createIndex({ code: 1 }, { unique: true, background: true });
    await yourCollection.createIndex({ status: 1 }, { background: true });
    await yourCollection.createIndex({ createdAt: -1 }, { background: true });
    fastify.log.info("Ensured indexes on your_items collection");
  }
} catch (err) {
  fastify.log.error({ err }, "Failed to ensure your_items indexes");
}

New Feature Checklist

Task Backend Dashboard Mobile
Create route/controller ✅ - -
Add validation schema ✅ ✅ ✅
Register in server ✅ - -
Add permissions ✅ ✅ -
Create UI components - ✅ ✅
Add to navigation - ✅ ✅
Add database indexes ✅ - -
Add audit logging ✅ - -
Write tests ✅ ✅ ✅
Update documentation ✅ ✅ ✅
✅ Best Practices
  • Always add audit logging for critical operations
  • Use Zod for request validation
  • Follow existing code patterns
  • Add appropriate permissions for each endpoint
  • Update this documentation when adding features