+
{invoices}
diff --git a/src/app/dashboard/module/dashboard-app-module.ts b/src/app/dashboard/module/dashboard-app-module.ts
index a3611c1..60721eb 100644
--- a/src/app/dashboard/module/dashboard-app-module.ts
+++ b/src/app/dashboard/module/dashboard-app-module.ts
@@ -1,9 +1,13 @@
import CreateRandomInvoiceButtonVM from "@/app/dashboard/vm/create-random-invoice-button-vm";
import di from "@/bootstrap/di/init-di"
+import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
export default function dashboardAppModule() {
const dashboardDi = di.createChildContainer()
+ dashboardDi.register(createInvoiceUsecase.name, {
+ useValue: createInvoiceUsecase
+ })
dashboardDi.register(CreateRandomInvoiceButtonVM, CreateRandomInvoiceButtonVM)
return dashboardDi
}
diff --git a/src/app/dashboard/vm/create-random-invoice-button-vm.ts b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
index 3bbf802..11a258e 100644
--- a/src/app/dashboard/vm/create-random-invoice-button-vm.ts
+++ b/src/app/dashboard/vm/create-random-invoice-button-vm.ts
@@ -1,15 +1,36 @@
import ButtonVm from "@/app/components/button/button-vm";
+import useThrottle from "@/bootstrap/helpers/hooks/use-throttle";
import BaseVM from "@/bootstrap/helpers/vm/base-vm";
+import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
+import createInvoiceUsecase from "@/feature/core/invoice/domain/usecase/create-invoice-usecase";
+import { faker } from "@faker-js/faker";
export default class CreateRandomInvoiceButtonVM extends BaseVM {
+ private createInvoice: typeof createInvoiceUsecase
+
+ constructor() {
+ super()
+ this.createInvoice = this.di.resolve(createInvoiceUsecase.name)
+ }
+
useVM(): ButtonVm {
+ const throttledOnClick = useThrottle(this.onClickHandler.bind(this), 5000)
return {
props: {
- title: "Button Title"
+ title: "Create Random Invoice"
},
- onClick: () => {
- console.log('clicked');
- }
+ onClick: throttledOnClick
}
}
+
+ onClickHandler() {
+ const fakedParams: InvoiceParam = {
+ amount: faker.number.int({
+ min: 1,
+ max: 10
+ }),
+ status: "paid"
+ }
+ this.createInvoice(fakedParams)
+ }
}
\ No newline at end of file
diff --git a/src/bootstrap/helpers/hooks/use-throttle.ts b/src/bootstrap/helpers/hooks/use-throttle.ts
new file mode 100644
index 0000000..ff2344f
--- /dev/null
+++ b/src/bootstrap/helpers/hooks/use-throttle.ts
@@ -0,0 +1,18 @@
+"use client"
+
+import { useEffect, useRef } from "react"
+
+/**
+ *
+ * @param callback
+ * @param time In miliseconds
+ */
+export default function useThrottle(callback: T, time: number = 2000) {
+ const lastRun = useRef(Date.now())
+
+ return function() {
+ if (Date.now() - lastRun.current <= time) return;
+ lastRun.current = Date.now()
+ return callback()
+ }
+}
\ No newline at end of file
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 ba6eff5..9c19809 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
@@ -20,7 +20,7 @@ export default class CustomerInvoiceDbRepo implements CustomerInvoiceRepo {
FROM invoices
JOIN customers ON invoices.customer_id = customers.id
ORDER BY invoices.date DESC
- LIMIT 5` as postgres.RowList;
+ LIMIT 20 ` as postgres.RowList;
return this.customerInvoicesDto(data)
} catch (error) {
diff --git a/src/feature/core/invoice/data/repo/invoice-db-repo.ts b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
index 66c0998..fd2219c 100644
--- a/src/feature/core/invoice/data/repo/invoice-db-repo.ts
+++ b/src/feature/core/invoice/data/repo/invoice-db-repo.ts
@@ -1,6 +1,7 @@
import { sql } from "@/bootstrap/db/db";
import { formatCurrency } from "@/feature/common/feature-helpers";
import InvoiceRepo from "@/feature/core/invoice/domain/i-repo/invoice-repo";
+import { InvoiceParam } from "@/feature/core/invoice/domain/param/invoice-param";
import InvoiceStatusSummary from "@/feature/core/invoice/domain/value-object/invoice-status";
import postgres from "postgres";
@@ -12,6 +13,28 @@ export default class InvoiceDbRepo implements InvoiceRepo {
return data.count ?? 0
}
+ async createInvoice(params: InvoiceParam): Promise {
+ const firstCustomerIdDb = await sql`SELECT
+ id FROM customers
+ ORDER BY id ASC
+ LIMIT 1
+ `
+ const customerId = firstCustomerIdDb.at(0)?.id
+ if (!customerId) throw new Error("There is no customer")
+
+ const { amount, status } = params;
+ const amountInCents = amount * 100;
+ const date = new Date().toISOString().split('T')[0];
+
+ // Insert data into the database
+ const result = await sql`
+ INSERT INTO invoices (customer_id, amount, status, date)
+ VALUES (${customerId}, ${amountInCents}, ${status}, ${date})
+ RETURNING id
+ `;
+ return result.at(0)?.id ?? ""
+ }
+
async fetchInvoicesStatusSummary(): Promise {
const invoiceStatusPromise = await sql`SELECT
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
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 90bac8f..983ac3f 100644
--- a/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
+++ b/src/feature/core/invoice/domain/i-repo/invoice-repo.ts
@@ -1,8 +1,10 @@
+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
fetchInvoicesStatusSummary(): Promise
+ createInvoice(params: InvoiceParam): Promise
}
export const invoiceRepoKey = "invoiceRepoKey"
\ No newline at end of file
diff --git a/src/feature/core/invoice/domain/param/invoice-param.ts b/src/feature/core/invoice/domain/param/invoice-param.ts
new file mode 100644
index 0000000..1dce62a
--- /dev/null
+++ b/src/feature/core/invoice/domain/param/invoice-param.ts
@@ -0,0 +1,10 @@
+import { z } from "zod";
+
+export const invoiceSchema = z.object({
+ amount: z.coerce.number().gt(0, { message: 'Please enter an amount greater than $0.' }),
+ status: z.enum(['pending', 'paid'], {
+ invalid_type_error: 'Please select an invoice status.',
+ }),
+});
+
+export type InvoiceParam = z.infer
\ 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
new file mode 100644
index 0000000..1b3ea6a
--- /dev/null
+++ b/src/feature/core/invoice/domain/usecase/create-invoice-usecase.ts
@@ -0,0 +1,22 @@
+"use server"
+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 { revalidatePath } from "next/cache";
+import { redirect } from "next/navigation";
+
+export default async function createInvoiceUsecase(params: InvoiceParam): Promise {
+ const isParamsValid = invoiceSchema.safeParse(params)
+
+ if (!isParamsValid) {
+ return {
+ errorMessage: "Please pass correct params"
+ }
+ }
+ const repo = serverDi(invoiceModuleKey).resolve(invoiceRepoKey)
+
+ revalidatePath("/dashboard")
+ return repo.createInvoice(params)
+
+}
\ No newline at end of file