feature/research-di #1
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,7 +9,7 @@
|
|||||||
!.yarn/plugins
|
!.yarn/plugins
|
||||||
!.yarn/releases
|
!.yarn/releases
|
||||||
!.yarn/versions
|
!.yarn/versions
|
||||||
|
.vscode
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
|
import fetchSummaryInfoUsecase from '@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase';
|
||||||
import {
|
import {
|
||||||
BanknotesIcon,
|
BanknotesIcon,
|
||||||
ClockIcon,
|
ClockIcon,
|
||||||
UserGroupIcon,
|
UserGroupIcon,
|
||||||
InboxIcon,
|
InboxIcon,
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
import { fetchCardData } from '@/app/lib/data';
|
|
||||||
|
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
collected: BanknotesIcon,
|
collected: BanknotesIcon,
|
||||||
@ -14,21 +14,16 @@ const iconMap = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default async function CardWrapper() {
|
export default async function CardWrapper() {
|
||||||
const {
|
const {customersNumber, invoicesNumber, invoicesSummary } = await fetchSummaryInfoUsecase();
|
||||||
numberOfInvoices,
|
|
||||||
numberOfCustomers,
|
|
||||||
totalPaidInvoices,
|
|
||||||
totalPendingInvoices,
|
|
||||||
} = await fetchCardData();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card title="Collected" value={totalPaidInvoices} type="collected" />
|
<Card title="Collected" value={invoicesSummary.paid} type="collected" />
|
||||||
<Card title="Pending" value={totalPendingInvoices} type="pending" />
|
<Card title="Pending" value={invoicesSummary.pending} type="pending" />
|
||||||
<Card title="Total Invoices" value={numberOfInvoices} type="invoices" />
|
<Card title="Total Invoices" value={invoicesNumber} type="invoices" />
|
||||||
<Card
|
<Card
|
||||||
title="Total Customers"
|
title="Total Customers"
|
||||||
value={numberOfCustomers}
|
value={customersNumber}
|
||||||
type="customers"
|
type="customers"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import fetchCustomerInvoicesUsecase from '@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase';
|
||||||
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
import { ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { fetchLatestInvoices } from '@/app/lib/data';
|
|
||||||
export default async function LatestInvoices() {
|
export default async function LatestInvoices() {
|
||||||
const latestInvoices = await fetchLatestInvoices();
|
const latestInvoices = await fetchCustomerInvoicesUsecase();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col md:col-span-4">
|
<div className="flex w-full flex-col md:col-span-4">
|
||||||
@ -26,25 +26,25 @@ export default async function LatestInvoices() {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Image
|
<Image
|
||||||
src={invoice.image_url}
|
src={invoice.customerImageUrl}
|
||||||
alt={`${invoice.name}'s profile picture`}
|
alt={`${invoice.customerName}'s profile picture`}
|
||||||
className="mr-4 rounded-full"
|
className="mr-4 rounded-full"
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
/>
|
/>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="truncate text-sm font-semibold md:text-base">
|
<p className="truncate text-sm font-semibold md:text-base">
|
||||||
{invoice.name}
|
{invoice.customerName}
|
||||||
</p>
|
</p>
|
||||||
<p className="hidden text-sm text-gray-500 sm:block">
|
<p className="hidden text-sm text-gray-500 sm:block">
|
||||||
{invoice.email}
|
{invoice.customerEmail}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
className="truncate text-sm font-medium md:text-base"
|
className="truncate text-sm font-medium md:text-base"
|
||||||
>
|
>
|
||||||
{invoice.amount}
|
{invoice.invoicesAmount}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { generateYAxis } from '@/app/lib/utils';
|
import { generateYAxis } from '@/app/lib/utils';
|
||||||
|
import fetchRevenuesUsecase from '@/feature/core/revenue/domain/usecase/fetch-revenues-usecase';
|
||||||
import { CalendarIcon } from '@heroicons/react/24/outline';
|
import { CalendarIcon } from '@heroicons/react/24/outline';
|
||||||
import { fetchRevenue } from '@/app/lib/data';
|
|
||||||
|
|
||||||
export default async function RevenueChart() {
|
export default async function RevenueChart() {
|
||||||
const revenue = await fetchRevenue();
|
const revenue = await fetchRevenuesUsecase();
|
||||||
|
|
||||||
const chartHeight = 350;
|
const chartHeight = 350;
|
||||||
|
|
||||||
|
27
src/app/dashboard/module/dashboard-app-module.ts
Normal file
27
src/app/dashboard/module/dashboard-app-module.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import di from "@/bootstrap/di/init-di"
|
||||||
|
import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
|
||||||
|
import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
|
||||||
|
import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase";
|
||||||
|
import fetchRevenuesUsecase from "@/feature/core/revenue/domain/usecase/fetch-revenues-usecase";
|
||||||
|
|
||||||
|
export default function dashboardAppModule() {
|
||||||
|
const dashboardDi = di.createChildContainer()
|
||||||
|
|
||||||
|
dashboardDi.register(fetchCustomersUsecase.name, {
|
||||||
|
useValue: fetchCustomersUsecase
|
||||||
|
})
|
||||||
|
|
||||||
|
dashboardDi.register(fetchAllInvoicesAmountUsecase.name, {
|
||||||
|
useValue: fetchAllInvoicesAmountUsecase
|
||||||
|
})
|
||||||
|
dashboardDi.register(fetchAllInvoicesAmountUsecase.name, {
|
||||||
|
useValue: fetchAllInvoicesAmountUsecase
|
||||||
|
})
|
||||||
|
dashboardDi.register(fetchCustomerInvoicesUsecase.name, {
|
||||||
|
useValue: fetchCustomerInvoicesUsecase
|
||||||
|
})
|
||||||
|
dashboardDi.register(fetchRevenuesUsecase.name, {
|
||||||
|
useValue: fetchRevenuesUsecase
|
||||||
|
})
|
||||||
|
return dashboardDi
|
||||||
|
}
|
@ -12,7 +12,10 @@ import getSummaryInfoDi from "@/feature/core/summary-info/data/module/summary-in
|
|||||||
import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
|
import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
|
||||||
import getRevenueDi from "@/feature/core/revenue/data/module/revenue-di";
|
import getRevenueDi from "@/feature/core/revenue/data/module/revenue-di";
|
||||||
|
|
||||||
|
const memoizedDis: Record<string, DependencyContainer> = {}
|
||||||
|
|
||||||
export default function serverDi(module: string): DependencyContainer {
|
export default function serverDi(module: string): DependencyContainer {
|
||||||
|
if (memoizedDis[module]) return memoizedDis[module]
|
||||||
const getDi = {
|
const getDi = {
|
||||||
[testModuleKey]: getTestModule,
|
[testModuleKey]: getTestModule,
|
||||||
[customerKey]: getCustomerDi,
|
[customerKey]: getCustomerDi,
|
||||||
@ -24,5 +27,7 @@ export default function serverDi(module: string): DependencyContainer {
|
|||||||
|
|
||||||
if (!getDi) throw new Error("Server Di didn't found for module: " + module)
|
if (!getDi) throw new Error("Server Di didn't found for module: " + module)
|
||||||
|
|
||||||
return getDi()
|
const di = getDi()
|
||||||
|
memoizedDis[module] = di
|
||||||
|
return di
|
||||||
}
|
}
|
@ -1,16 +1,11 @@
|
|||||||
import di from "@/bootstrap/di/init-di";
|
import di from "@/bootstrap/di/init-di";
|
||||||
|
import CustomerInvoiceDbRepo from "@/feature/core/customer-invoice/data/repo/customer-invoice-db-repo";
|
||||||
import { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
|
import { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
|
||||||
import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase";
|
|
||||||
import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
|
|
||||||
import { DependencyContainer } from "tsyringe";
|
import { DependencyContainer } from "tsyringe";
|
||||||
|
|
||||||
export default function getCustomerInvoiceDi(): DependencyContainer {
|
export default function getCustomerInvoiceDi(): DependencyContainer {
|
||||||
const customerInvoiceDi = di.createChildContainer()
|
const customerInvoiceDi = di.createChildContainer()
|
||||||
|
|
||||||
customerInvoiceDi.register(fetchCustomerInvoicesUsecase.name, {
|
customerInvoiceDi.register(customerInvoiceRepoKey, CustomerInvoiceDbRepo)
|
||||||
useValue: fetchCustomerInvoicesUsecase
|
|
||||||
})
|
|
||||||
|
|
||||||
customerInvoiceDi.register(customerInvoiceRepoKey, CustomerDbRepo)
|
|
||||||
return customerInvoiceDi
|
return customerInvoiceDi
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ import { formatCurrency } from "@/app/lib/utils";
|
|||||||
import { sql } from "@/bootstrap/db/db";
|
import { sql } from "@/bootstrap/db/db";
|
||||||
import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
|
import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
|
||||||
import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
|
import CustomerInvoiceRepo from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
|
||||||
import { connection } from "next/server";
|
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
|
|
||||||
type customerInvoiceDbResponse = {
|
type customerInvoiceDbResponse = {
|
||||||
@ -13,11 +12,8 @@ type customerInvoiceDbResponse = {
|
|||||||
amount: string;
|
amount: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CustomerDbRepo implements CustomerInvoiceRepo {
|
export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
|
||||||
async fetchList(): Promise<CustomerInvoice[]> {
|
async fetchList(): Promise<CustomerInvoice[]> {
|
||||||
// This is equivalent to in fetch(..., {cache: 'no-store'}).
|
|
||||||
connection()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await sql`
|
const data = await sql`
|
||||||
SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
|
SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id
|
||||||
|
@ -3,9 +3,10 @@ import serverDi from "@/feature/common/server-di";
|
|||||||
import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
|
import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice";
|
||||||
import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
|
import CustomerInvoiceRepo, { customerInvoiceRepoKey } from "@/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo";
|
||||||
import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
|
import { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key";
|
||||||
|
import { connection } from "next/server";
|
||||||
|
|
||||||
export default function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
|
export default async function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> {
|
||||||
|
connection()
|
||||||
const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
|
const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey)
|
||||||
|
|
||||||
return repo.fetchList()
|
return repo.fetchList()
|
||||||
}
|
}
|
@ -1,16 +1,11 @@
|
|||||||
import di from "@/bootstrap/di/init-di";
|
import di from "@/bootstrap/di/init-di";
|
||||||
import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
|
import CustomerDbRepo from "@/feature/core/customer/data/repo/customer-db-repo";
|
||||||
import { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
|
import { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
|
||||||
import fetchCustomersUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-usecase";
|
|
||||||
import { DependencyContainer } from "tsyringe";
|
import { DependencyContainer } from "tsyringe";
|
||||||
|
|
||||||
export default function getCustomerDi(): DependencyContainer {
|
export default function getCustomerDi(): DependencyContainer {
|
||||||
const customerDi = di.createChildContainer()
|
const customerDi = di.createChildContainer()
|
||||||
|
|
||||||
customerDi.register(fetchCustomersUsecase.name, {
|
|
||||||
useValue: fetchCustomersUsecase
|
|
||||||
})
|
|
||||||
|
|
||||||
customerDi.register(customerRepoKey, CustomerDbRepo)
|
customerDi.register(customerRepoKey, CustomerDbRepo)
|
||||||
return customerDi
|
return customerDi
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ import serverDi from "@/feature/common/server-di";
|
|||||||
import { customerKey } from "@/feature/core/customer/customer-key";
|
import { customerKey } from "@/feature/core/customer/customer-key";
|
||||||
import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
|
import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
|
||||||
|
|
||||||
export default function fetchCustomersAmountUsecase(): Promise<number> {
|
export default async function fetchCustomersAmountUsecase(): Promise<number> {
|
||||||
const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
|
const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
|
||||||
return repo.fetchCustomersAmount()
|
return repo.fetchCustomersAmount()
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import { customerKey } from "@/feature/core/customer/customer-key";
|
|||||||
import Customer from "@/feature/core/customer/domain/entity/customer";
|
import Customer from "@/feature/core/customer/domain/entity/customer";
|
||||||
import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
|
import CustomerRepo, { customerRepoKey } from "@/feature/core/customer/domain/i-repo/customer-repo";
|
||||||
|
|
||||||
export default function fetchCustomersUsecase(query: string): Promise<Customer[]> {
|
export default async function fetchCustomersUsecase(query: string): Promise<Customer[]> {
|
||||||
const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
|
const repo = serverDi(customerKey).resolve<CustomerRepo>(customerRepoKey)
|
||||||
|
|
||||||
return repo.fetchList(query)
|
return repo.fetchList(query)
|
||||||
|
@ -3,7 +3,7 @@ import serverDi from "@/feature/common/server-di";
|
|||||||
import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
|
import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo";
|
||||||
import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
|
import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
|
||||||
|
|
||||||
export default function fetchAllInvoicesAmountUsecase(): Promise<number> {
|
export default async function fetchAllInvoicesAmountUsecase(): Promise<number> {
|
||||||
const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
|
const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
|
||||||
|
|
||||||
return repo.fetchAllInvoicesAmount()
|
return repo.fetchAllInvoicesAmount()
|
||||||
|
@ -3,7 +3,7 @@ import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-rep
|
|||||||
import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
|
import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
|
||||||
import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
|
import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key";
|
||||||
|
|
||||||
export default function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
|
export default async function fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> {
|
||||||
const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
|
const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey)
|
||||||
return repo.fetchInvoicesStatusSummary()
|
return repo.fetchInvoicesStatusSummary()
|
||||||
}
|
}
|
@ -11,7 +11,6 @@ export type RevenueDbResponse = {
|
|||||||
export default class RevenueDbRepo implements RevenueRepo {
|
export default class RevenueDbRepo implements RevenueRepo {
|
||||||
async fetchRevenues(): Promise<Revenue[]> {
|
async fetchRevenues(): Promise<Revenue[]> {
|
||||||
// This is equivalent to in fetch(..., {cache: 'no-store'}).
|
// This is equivalent to in fetch(..., {cache: 'no-store'}).
|
||||||
connection()
|
|
||||||
try {
|
try {
|
||||||
// Artificially delay a response for demo purposes.
|
// Artificially delay a response for demo purposes.
|
||||||
// Don't do this in production :)
|
// Don't do this in production :)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import serverDi from "@/feature/common/server-di";
|
import serverDi from "@/feature/common/server-di";
|
||||||
import Revenue from "@/feature/core/revenue/domain/entity/revenue";
|
import Revenue from "@/feature/core/revenue/domain/entity/revenue";
|
||||||
import RevenueRepo from "@/feature/core/revenue/domain/i-repo/revenue-repo";
|
import RevenueRepo, { revenueRepoKey } from "@/feature/core/revenue/domain/i-repo/revenue-repo";
|
||||||
import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
|
import { revenueModuleKey } from "@/feature/core/revenue/domain/revenue-module-key";
|
||||||
|
|
||||||
export default function fetchRevenuesUsecase(): Promise<Revenue[]> {
|
export default async function fetchRevenuesUsecase(): Promise<Revenue[]> {
|
||||||
const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueModuleKey)
|
const repo = serverDi(revenueModuleKey).resolve<RevenueRepo>(revenueRepoKey)
|
||||||
return repo.fetchRevenues()
|
return repo.fetchRevenues()
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ import di from "@/bootstrap/di/init-di"
|
|||||||
import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase"
|
import fetchCustomersAmountUsecase from "@/feature/core/customer/domain/usecase/fetch-customers-amount-usecase"
|
||||||
import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase"
|
import fetchAllInvoicesAmountUsecase from "@/feature/core/invoice/domain/usecase/fetch-all-invoices-amount-usecase"
|
||||||
import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary"
|
import fetchInvoicesStatusSummary from "@/feature/core/invoice/domain/usecase/fetch-invoices-status-summary"
|
||||||
import fetchSummaryInfoUsecase from "@/feature/core/summary-info/domain/usecase/fetch-summary-info-usecase"
|
|
||||||
|
|
||||||
export default function getSummaryInfoDi() {
|
export default function getSummaryInfoDi() {
|
||||||
const summaryInfoDi = di.createChildContainer()
|
const summaryInfoDi = di.createChildContainer()
|
||||||
@ -14,7 +13,7 @@ export default function getSummaryInfoDi() {
|
|||||||
useValue: fetchCustomersAmountUsecase
|
useValue: fetchCustomersAmountUsecase
|
||||||
})
|
})
|
||||||
summaryInfoDi.register(fetchInvoicesStatusSummary.name, {
|
summaryInfoDi.register(fetchInvoicesStatusSummary.name, {
|
||||||
useValue: fetchSummaryInfoUsecase
|
useValue: fetchInvoicesStatusSummary
|
||||||
})
|
})
|
||||||
return summaryInfoDi
|
return summaryInfoDi
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user