diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts index 45bde22..8fbbde9 100644 --- a/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts +++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices-controller.ts @@ -1,5 +1,5 @@ import fetchCustomerInvoicesUsecase from "@/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase"; -export default function latestInvoicesController() { - return fetchCustomerInvoicesUsecase() +export default async function latestInvoicesController() { + return await fetchCustomerInvoicesUsecase() } \ No newline at end of file diff --git a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx index 09396cd..696af73 100644 --- a/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx +++ b/src/app/dashboard/components/server/latest-invoices/latest-invoices.tsx @@ -2,12 +2,15 @@ import CreateRandomInvoiceContainer from '@/app/dashboard/components/client/crea import latestInvoicesController from '@/app/dashboard/components/server/latest-invoices/latest-invoices-controller'; import { ArrowPathIcon } from '@heroicons/react/24/outline'; import clsx from 'clsx'; +import { isLeft } from 'fp-ts/lib/Either'; import Image from 'next/image'; export default async function LatestInvoices() { const latestInvoices = await latestInvoicesController(); - const invoices = latestInvoices.map((invoice, i) => { + if (isLeft(latestInvoices)) return <div>Error</div> + + const invoices = latestInvoices.right.map((invoice, i) => { return ( <div key={invoice.id} diff --git a/src/feature/common/failures/params-failure.ts b/src/feature/common/failures/params-failure.ts new file mode 100644 index 0000000..4849027 --- /dev/null +++ b/src/feature/common/failures/params-failure.ts @@ -0,0 +1,12 @@ +import BaseFailure from "./base-failure"; + +/** + * Failure for params failure + */ +export default class ParamsFailure extends BaseFailure { + /* ------------------------------- Constructor ------------------------------ */ + constructor() { + super("params"); + } + /* -------------------------------------------------------------------------- */ +} diff --git a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts index a12de93..db036b8 100644 --- a/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts +++ b/src/feature/core/customer-invoice/data/repo/customer-invoice-db-repo.ts @@ -1,7 +1,12 @@ import { sql } from "@/bootstrap/boundaries/db/db"; +import ApiTask from "@/feature/common/data/api-task"; +import { failureOr } from "@/feature/common/failures/failure-helpers"; +import NetworkFailure from "@/feature/common/failures/network-failure"; import { formatCurrency } from "@/feature/common/feature-helpers"; 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 { pipe } from "fp-ts/lib/function"; +import { tryCatch } from "fp-ts/lib/TaskEither"; import postgres from "postgres"; type customerInvoiceDbResponse = { @@ -13,23 +18,25 @@ type customerInvoiceDbResponse = { } export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo { - async fetchList(): Promise<CustomerInvoice[]> { - try { - const data = await sql` - SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id - FROM invoices - JOIN customers ON invoices.customer_id = customers.id - ORDER BY invoices.date DESC - LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>; - - return this.customerInvoicesDto(data) - } catch (error) { - console.error('Database Error:', error); - throw new Error('Failed to fetch the latest invoices.'); - } + fetchList(): ApiTask<CustomerInvoice[]> { + + return pipe( + tryCatch( + async () => { + const response = await sql` + SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id + FROM invoices + JOIN customers ON invoices.customer_id = customers.id + ORDER BY invoices.date DESC + LIMIT 20 ` as postgres.RowList<customerInvoiceDbResponse[]>; + + return this.customerInvoicesDto(response) + }, + (l) => failureOr(l, new NetworkFailure()) + ) + ) } - private customerInvoicesDto(dbCustomers: customerInvoiceDbResponse[]): CustomerInvoice[] { return dbCustomers.map((customer) => this.customerInvoiceDto(customer)); } diff --git a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts index 0a8c993..ab4b486 100644 --- a/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts +++ b/src/feature/core/customer-invoice/domain/i-repo/customer-invoice-repo.ts @@ -1,7 +1,8 @@ +import ApiTask from "@/feature/common/data/api-task" import CustomerInvoice from "@/feature/core/customer-invoice/domain/entity/customer-invoice" export default interface CustomerInvoiceRepo { - fetchList(): Promise<CustomerInvoice[]> + fetchList(): ApiTask<CustomerInvoice[]> } export const customerInvoiceRepoKey = "customerInvoiceRepoKey" \ No newline at end of file diff --git a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts index 5947553..5d2df55 100644 --- a/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts +++ b/src/feature/core/customer-invoice/domain/usecase/fetch-customer-invoices-usecase.ts @@ -1,12 +1,12 @@ -"use server" +import { ApiEither } from "@/feature/common/data/api-task"; import serverDi from "@/feature/common/server-di"; 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 { customerInvoiceModuleKey } from "@/feature/core/customer-invoice/invoice-module-key"; import { connection } from "next/server"; -export default async function fetchCustomerInvoicesUsecase(): Promise<CustomerInvoice[]> { +export default async function fetchCustomerInvoicesUsecase(): Promise<ApiEither<CustomerInvoice[]>> { connection() const repo = serverDi(customerInvoiceModuleKey).resolve<CustomerInvoiceRepo>(customerInvoiceRepoKey) - return repo.fetchList() + return repo.fetchList()() } \ No newline at end of file diff --git a/src/feature/core/customer/domain/i-repo/customer-repo.ts b/src/feature/core/customer/domain/i-repo/customer-repo.ts index d48fdde..86b890e 100644 --- a/src/feature/core/customer/domain/i-repo/customer-repo.ts +++ b/src/feature/core/customer/domain/i-repo/customer-repo.ts @@ -1,7 +1,8 @@ +import ApiTask from "@/feature/common/data/api-task" import Customer from "@/feature/core/customer/domain/entity/customer" export default interface CustomerRepo { - fetchList(query: string): Promise<Customer[]> + fetchList(query: string): ApiTask<Customer[]> fetchCustomersAmount(): Promise<number> } diff --git a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts index 983ac3f..ca5096d 100644 --- a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts +++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts @@ -1,10 +1,11 @@ +import ApiTask from "@/feature/common/data/api-task" import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param" import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status" export default interface InvoiceRepo { fetchAllInvoicesAmount(): Promise<number> fetchInvoicesStatusSummary(): Promise<InvoiceStatusSummary> - createInvoice(params: InvoiceParam): Promise<string> + createInvoice(params: InvoiceParam): ApiTask<string> } export const invoiceRepoKey = "invoiceRepoKey" \ No newline at end of file diff --git a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts index 73a0106..9cfc63d 100644 --- a/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts +++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts @@ -1,19 +1,24 @@ "use server" +import { ApiEither } from "@/feature/common/data/api-task"; +import ParamsFailure from "@/feature/common/failures/params-failure"; import serverDi from "@/feature/common/server-di"; import InvoiceRepo, { invoiceRepoKey } from "@/feature/core/invoice/domain/i-repo/invoice-repo"; import { InvoiceParam, invoiceSchema } from "@/feature/core/invoice/domain/param/invoice-param"; import { invoiceModuleKey } from "@/feature/core/invoice/invoice-module-key"; +import { pipe } from "fp-ts/lib/function"; +import { chain, fromNullable, left, map, right } from "fp-ts/lib/TaskEither"; -export default async function createInvoiceUsecase(params: InvoiceParam): Promise<string | {errorMessage: string}> { - const isParamsValid = invoiceSchema.safeParse(params) - - if (!isParamsValid) { - return { - errorMessage: "Please pass correct params" - } - } +export default async function createInvoiceUsecase(params: InvoiceParam): Promise<ApiEither<string>> { const repo = serverDi(invoiceModuleKey).resolve<InvoiceRepo>(invoiceRepoKey) - return repo.createInvoice(params) - + return pipe( + fromNullable(new ParamsFailure())(params), + map((params) => invoiceSchema.safeParse(params)), + chain((params) => { + const isParamsValid = invoiceSchema.safeParse(params) + if (!isParamsValid.success) left(new ParamsFailure()) + return right(params.data as InvoiceParam) + }), + chain((params) => repo.createInvoice(params)) + )() } \ No newline at end of file