feature/research-di #1

Merged
behnam merged 37 commits from feature/research-di into develop 2024-11-21 15:50:19 +00:00
10 changed files with 88 additions and 147 deletions
Showing only changes of commit 8b79d9aa4b - Show all commits

View File

@ -1,73 +0,0 @@
import { sql } from '@/bootstrap/db/db';
import {
Revenue,
Invoice,
Customer,
} from './definitions';
import { formatCurrency } from './utils';
import postgres from 'postgres';
import { connection } from 'next/server';
export async function fetchRevenue() {
// This is equivalent to in fetch(..., {cache: 'no-store'}).
connection()
try {
// Artificially delay a response for demo purposes.
// Don't do this in production :)
console.log('Fetching revenue data...');
await new Promise((resolve) => setTimeout(resolve, 3000));
const data = await sql`SELECT * FROM revenue`;
console.log('Data fetch completed after 3 seconds.');
return data as postgres.RowList<Revenue[]>;
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch revenue data.');
}
}
export async function fetchCardData() {
// This is equivalent to in fetch(..., {cache: 'no-store'}).
connection()
try {
// You can probably combine these into a single SQL query
// However, we are intentionally splitting them to demonstrate
// how to initialize multiple queries in parallel with JS.
const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
const invoiceStatusPromise = sql`SELECT
id,
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
FROM invoices`;
const data = await Promise.all([
invoiceCountPromise,
customerCountPromise,
invoiceStatusPromise,
]);
const invoices = data[0] as postgres.RowList<Invoice[]>
const customres = data[1] as postgres.RowList<Customer[]>
const invoiceStatus = data[2] as postgres.RowList<({id: string; paid: string, pending: string})[]>
const numberOfInvoices = Number(invoices.count ?? '0');
const numberOfCustomers = Number(customres.count ?? '0');
const totalPaidInvoices = formatCurrency(Number(invoiceStatus.at(0)?.paid ?? '0'));
const totalPendingInvoices = formatCurrency(Number(invoiceStatus.at(0)?.pending ?? '0'));
return {
numberOfCustomers,
numberOfInvoices,
totalPaidInvoices,
totalPendingInvoices,
};
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch card data.');
}
}

View File

@ -1,26 +0,0 @@
export type Customer = {
id: string;
name: string;
email: string;
image_url: string;
};
export type Invoice = {
id: string; // Will be created on the database
customer_id: string;
amount: number; // Stored in cents
status: 'pending' | 'paid';
date: string;
};
export type Revenue = {
month: string;
revenue: number;
};
export type InvoiceForm = {
id: string;
customer_id: string;
amount: number;
status: 'pending' | 'paid';
};

View File

@ -1,4 +1,4 @@
import { Revenue } from './definitions';
import Revenue from "@/feature/core/revenue/domain/entity/revenue";
export const formatCurrency = (amount: number) => {
return (amount / 100).toLocaleString('en-US', {
@ -7,20 +7,6 @@ export const formatCurrency = (amount: number) => {
});
};
export const formatDateToLocal = (
dateStr: string,
locale: string = 'en-US',
) => {
const date = new Date(dateStr);
const options: Intl.DateTimeFormatOptions = {
day: 'numeric',
month: 'short',
year: 'numeric',
};
const formatter = new Intl.DateTimeFormat(locale, options);
return formatter.format(date);
};
export const generateYAxis = (revenue: Revenue[]) => {
// Calculate what labels we need to display on the y-axis
// based on highest record and in 1000s
@ -34,36 +20,3 @@ export const generateYAxis = (revenue: Revenue[]) => {
return { yAxisLabels, topLabel };
};
export const generatePagination = (currentPage: number, totalPages: number) => {
// If the total number of pages is 7 or less,
// display all pages without any ellipsis.
if (totalPages <= 7) {
return Array.from({ length: totalPages }, (_, i) => i + 1);
}
// If the current page is among the first 3 pages,
// show the first 3, an ellipsis, and the last 2 pages.
if (currentPage <= 3) {
return [1, 2, 3, '...', totalPages - 1, totalPages];
}
// If the current page is among the last 3 pages,
// show the first 2, an ellipsis, and the last 3 pages.
if (currentPage >= totalPages - 2) {
return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages];
}
// If the current page is somewhere in the middle,
// show the first page, an ellipsis, the current page and its neighbors,
// another ellipsis, and the last page.
return [
1,
'...',
currentPage - 1,
currentPage,
currentPage + 1,
'...',
totalPages,
];
};

View File

@ -9,6 +9,8 @@ import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
import { DependencyContainer } from "tsyringe";
import { summaryInfoModuleKey } from "@/feature/core/summary-info/domain/summary-info-module-key";
import getSummaryInfoDi from "@/feature/core/summary-info/data/module/summary-info-di";
import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
import getRevenueDi from "@/feature/core/revenue/data/module/revenue-di";
export default function serverDi(module: string): DependencyContainer {
const getDi = {
@ -17,6 +19,7 @@ export default function serverDi(module: string): DependencyContainer {
[customerInvoiceModuleKey]: getCustomerInvoiceDi,
[invoiceModuleKey]: getInvoiceDi,
[summaryInfoModuleKey]: getSummaryInfoDi,
[revenueModuleKey]: getRevenueDi,
}[module]
if (!getDi) throw new Error("Server Di didn't found for module: " + module)

View File

@ -0,0 +1,10 @@
import di from "@/bootstrap/di/init-di"
import RevenueDbRepo from "@/feature/core/revenue/data/repo/revenue-db-repo"
import { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo"
export default function getRevenueDi() {
const revenueDi = di.createChildContainer()
revenueDi.register(revenueRepoKey, RevenueDbRepo)
return revenueDi
}

View File

@ -0,0 +1,43 @@
import { sql } from "@/bootstrap/db/db";
import Revenue from "@/feature/core/revenue/domain/entity/revenue";
import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
import { connection } from "next/server";
import postgres from "postgres";
export type RevenueDbResponse = {
month: string;
revenue: number;
};
export default class RevenueDbRepo implements RevenueRepo {
async fetchRevenues(): Promise<Revenue[]> {
// This is equivalent to in fetch(..., {cache: 'no-store'}).
connection()
try {
// Artificially delay a response for demo purposes.
// Don't do this in production :)
await new Promise((resolve) => setTimeout(resolve, 3000));
const data = await sql`SELECT * FROM revenue` as postgres.RowList<RevenueDbResponse[]>;
console.log('Data fetch completed after 3 seconds.');
return this.revenuesDto(data);
} catch (error) {
console.error('Database Error:', error);
throw new Error('Failed to fetch revenue data.');
}
}
private revenuesDto(dbResponse: RevenueDbResponse[]): Revenue[] {
return dbResponse.map((dbRevenue) => this.revenueDto(dbRevenue))
}
private revenueDto(dbResponse: RevenueDbResponse): Revenue {
return new Revenue({
month: dbResponse.month,
revenue: dbResponse.revenue
})
}
}

View File

@ -0,0 +1,14 @@
export default class Revenue {
month: string;
revenue: number;
constructor(
{
month,
revenue
}: Revenue
) {
this.month = month
this.revenue = revenue
}
}

View File

@ -0,0 +1,7 @@
import Revenue from "@/feature/core/revenue/domain/entity/revenue";
export default interface RevenueRepo {
fetchRevenues(): Promise<Revenue[]>
}
export const revenueRepoKey = "revenueRepoKey"

View File

@ -0,0 +1 @@
export const revenueModuleKey = "RevenueModuleKey"

View File

@ -0,0 +1,9 @@
import serverDi from "@/feature/common/server-di";
import Revenue from "@/feature/core/revenue/domain/entity/revenue";
import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
export default function fetchRevenuesUsecase(): Promise<Revenue[]> {
const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueModuleKey)
return repo.fetchRevenues()
}