From 8b79d9aa4b014341d5fe0755f024a8082eac870a Mon Sep 17 00:00:00 2001 From: behnam Date: Fri, 1 Nov 2024 16:57:12 +0300 Subject: [PATCH] Add fetch revenues module --- src/app/lib/data.ts | 73 ------------------- src/app/lib/definitions.ts | 26 ------- src/app/lib/utils.ts | 49 +------------ src/feature/common/server-di.ts | 3 + .../core/revenue/data/module/revenue-di.ts | 10 +++ .../core/revenue/data/repo/revenue-db-repo.ts | 43 +++++++++++ .../core/revenue/domain/entity/revenue.ts | 14 ++++ .../revenue/domain/i-repo/revenue-repo.ts | 7 ++ .../core/revenue/domain/revenue-module-key.ts | 1 + .../domain/usecase/fetch-revenues-usecase.ts | 9 +++ 10 files changed, 88 insertions(+), 147 deletions(-) delete mode 100644 src/app/lib/data.ts delete mode 100644 src/app/lib/definitions.ts create mode 100644 src/feature/core/revenue/data/module/revenue-di.ts create mode 100644 src/feature/core/revenue/data/repo/revenue-db-repo.ts create mode 100644 src/feature/core/revenue/domain/entity/revenue.ts create mode 100644 src/feature/core/revenue/domain/i-repo/revenue-repo.ts create mode 100644 src/feature/core/revenue/domain/revenue-module-key.ts create mode 100644 src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts diff --git a/src/app/lib/data.ts b/src/app/lib/data.ts deleted file mode 100644 index 6326383..0000000 --- a/src/app/lib/data.ts +++ /dev/null @@ -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; - } 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 - const customres = data[1] as postgres.RowList - 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.'); - } -} diff --git a/src/app/lib/definitions.ts b/src/app/lib/definitions.ts deleted file mode 100644 index b26dd32..0000000 --- a/src/app/lib/definitions.ts +++ /dev/null @@ -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'; -}; diff --git a/src/app/lib/utils.ts b/src/app/lib/utils.ts index b7f7cff..c3184af 100644 --- a/src/app/lib/utils.ts +++ b/src/app/lib/utils.ts @@ -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, - ]; -}; diff --git a/src/feature/common/server-di.ts b/src/feature/common/server-di.ts index b1653f9..7ce0735 100644 --- a/src/feature/common/server-di.ts +++ b/src/feature/common/server-di.ts @@ -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) diff --git a/src/feature/core/revenue/data/module/revenue-di.ts b/src/feature/core/revenue/data/module/revenue-di.ts new file mode 100644 index 0000000..921a2ed --- /dev/null +++ b/src/feature/core/revenue/data/module/revenue-di.ts @@ -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 +} \ No newline at end of file diff --git a/src/feature/core/revenue/data/repo/revenue-db-repo.ts b/src/feature/core/revenue/data/repo/revenue-db-repo.ts new file mode 100644 index 0000000..b29c5d2 --- /dev/null +++ b/src/feature/core/revenue/data/repo/revenue-db-repo.ts @@ -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 { + // 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; + + 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 + }) + } + +} \ No newline at end of file diff --git a/src/feature/core/revenue/domain/entity/revenue.ts b/src/feature/core/revenue/domain/entity/revenue.ts new file mode 100644 index 0000000..290ca32 --- /dev/null +++ b/src/feature/core/revenue/domain/entity/revenue.ts @@ -0,0 +1,14 @@ +export default class Revenue { + month: string; + revenue: number; + + constructor( + { + month, + revenue + }: Revenue + ) { + this.month = month + this.revenue = revenue + } +} \ No newline at end of file diff --git a/src/feature/core/revenue/domain/i-repo/revenue-repo.ts b/src/feature/core/revenue/domain/i-repo/revenue-repo.ts new file mode 100644 index 0000000..be6924b --- /dev/null +++ b/src/feature/core/revenue/domain/i-repo/revenue-repo.ts @@ -0,0 +1,7 @@ +import Revenue from "@/feature/core/revenue/domain/entity/revenue"; + +export default interface RevenueRepo { + fetchRevenues(): Promise +} + +export const revenueRepoKey = "revenueRepoKey" \ No newline at end of file diff --git a/src/feature/core/revenue/domain/revenue-module-key.ts b/src/feature/core/revenue/domain/revenue-module-key.ts new file mode 100644 index 0000000..27150fb --- /dev/null +++ b/src/feature/core/revenue/domain/revenue-module-key.ts @@ -0,0 +1 @@ +export const revenueModuleKey = "RevenueModuleKey" \ No newline at end of file diff --git a/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts new file mode 100644 index 0000000..c9e6068 --- /dev/null +++ b/src/feature/core/revenue/domain/usecase/fetch-revenues-usecase.ts @@ -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 { + const repo = serverDi(revenueModuleKey).resolve(revenueModuleKey) + return repo.fetchRevenues() +} \ No newline at end of file