diff --git a/.agents/skills/idr-web-module-generator/idr-web-module-generator/SKILL.md b/.agents/skills/idr-web-module-generator/idr-web-module-generator/SKILL.md
index 2806b96f..96adf7ec 100644
--- a/.agents/skills/idr-web-module-generator/idr-web-module-generator/SKILL.md
+++ b/.agents/skills/idr-web-module-generator/idr-web-module-generator/SKILL.md
@@ -107,8 +107,14 @@ This layer requires a highly specific file organization:
- Conditionally renders `<{Entity}DeleteDialog />` and `<{Entity}Form />`.
## Example and Reference Check
-**CRITICAL MANDATE:** To guarantee that the generated code has the exact same conditionals, React hooks usage, types, and architectural structure as the rest of the project, you **MUST ALWAYS** read the files in the `src/app/modules/input-uses` or `src/app/modules/cultivations` modules before writing any code.
+**CRITICAL MANDATE:** To guarantee that the generated code has the exact same conditionals, React hooks usage, types, error messages in use-cases, and architectural structure as the rest of the project, you **MUST ALWAYS** read the files in the `src/app/modules/input-uses` or `src/app/modules/cultivations` modules before writing any code.
-**DO NOT** rely on your general knowledge to generate the file contents. You must treat the existing modules as strict templates. For every single file you create (e.g., the Delete Dialog, the Create Form, the Data Table, the Use Cases), you must read its equivalent in the reference module, copy its exact implementation logic, and adapt only the entity names, variables, and specific domain fields. This is the only way to ensure 100% fidelity to the project's internal patterns.
+**DO NOT** rely on your general knowledge to generate the file contents. You must treat the existing modules as strict templates. For every single file you create (e.g., the Delete Dialog, the Create Form, the Data Table, the Use Cases), you must read its equivalent in the reference module, copy its exact implementation logic, and adapt only the entity names, variables, specific domain fields, and **Custom Error Messages**. This is the only way to ensure 100% fidelity to the project's internal patterns. Pay special attention to change strings like "Local de Uso" or "Praga" in exceptions like `NotFoundError("...")` or `ForbiddenError("Você não tem permissão para...")` to match the newly created entity context.
+
+**Presentation Custom Messages:** You must also strictly adapt all success and error toasts, dialog titles, form titles, and action button labels present in the UI layers (`presentation/components`, `presentation/forms`, `presentation/screens`, `presentation/hooks/queries`) to the new entity name. For example, replace `toast.success('Local de utilização removido com sucesso')` with the appropriate label for the new entity. **Pay special attention to `useEffect` error toasts in query hooks** (`presentation/hooks/queries/{entity}-query.hook.ts`).
+
+**Form Inputs and Placeholders:** Inside `presentation/forms/{entity}-form-inputs.tsx`, you must adapt all `
` and `` to match the new entity's fields and context. Avoid leaving generic labels like "Descrição" if the field is "Nome", and update examples in placeholders (e.g., from "Ex: Galpão" to "Ex: Fertilizantes").
+
+**Constants and Variables:** All constant names, like `INITIAL_FORM_DATA` or `FORM_SCHEMA`, should also be renamed to match the new entity prefix (e.g., `PRODUCT_CATEGORY_INITIAL_FORM_DATA`). Ensure that all imports of these constants are also updated accordingly.
**Once the files are created:** Always run formatting (`pnpm format:fix`) and linting (`pnpm lint:fix`) for the newly created module folder, and run `pnpm type:check` to ensure no errors were introduced.
diff --git a/scripts/seed/data/index.mjs b/scripts/seed/data/index.mjs
index e1e583da..64e14103 100644
--- a/scripts/seed/data/index.mjs
+++ b/scripts/seed/data/index.mjs
@@ -16,7 +16,7 @@ export * from './animal-inseminations.mjs'
export * from './animal-purchases.mjs'
export * from './animal-sales.mjs'
export * from './active-ingredients.mjs'
-export * from './product-categories.mjs'
+export * from './input-use-product-categories.mjs'
export * from './products.mjs'
export * from './animal-medications.mjs'
export * from './animal-mastitides.mjs'
diff --git a/scripts/seed/data/product-categories.mjs b/scripts/seed/data/input-use-product-categories.mjs
similarity index 53%
rename from scripts/seed/data/product-categories.mjs
rename to scripts/seed/data/input-use-product-categories.mjs
index 97e4037d..3a15afaf 100644
--- a/scripts/seed/data/product-categories.mjs
+++ b/scripts/seed/data/input-use-product-categories.mjs
@@ -1,14 +1,14 @@
import { faker } from '@faker-js/faker/locale/pt_BR'
-export const allProductCategoriesData = Array.from(
+export const inputUseProductCategoriesData = Array.from(
{
length: faker.number.int({
- min: 50,
- max: 150,
+ min: 10,
+ max: 50,
}),
},
(_, index) => ({
id: index + 1,
- description: faker.commerce.department(),
+ name: faker.commerce.department(),
})
)
diff --git a/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/index.ts b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/index.ts
new file mode 100644
index 00000000..6f83775b
--- /dev/null
+++ b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/index.ts
@@ -0,0 +1,5 @@
+export * from './remote-create-input-use-product-category-use-case'
+export * from './remote-update-input-use-product-category-use-case'
+export * from './remote-delete-input-use-product-category-use-case'
+export * from './remote-get-input-use-product-category-use-case'
+export * from './remote-get-input-use-product-categories-use-case'
diff --git a/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-create-input-use-product-category-use-case.ts b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-create-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..a1deee00
--- /dev/null
+++ b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-create-input-use-product-category-use-case.ts
@@ -0,0 +1,39 @@
+import { type HttpClient, HttpStatusCode } from '@/core/data/protocols/http'
+import {
+ BadRequestError,
+ ForbiddenError,
+ UnexpectedError,
+} from '@/core/domain/errors'
+
+import type { CreateInputUseProductCategoryUseCase } from '../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export class RemoteCreateInputUseProductCategoryUseCase
+ implements CreateInputUseProductCategoryUseCase
+{
+ constructor(
+ private readonly url: string,
+ private readonly httpClient: HttpClient
+ ) {}
+
+ execute: CreateInputUseProductCategoryUseCase['execute'] = async ({
+ inputUseProductCategory,
+ }) => {
+ const { statusCode } = await this.httpClient.request({
+ url: this.url,
+ method: 'post',
+ body: inputUseProductCategory,
+ })
+
+ if (statusCode === HttpStatusCode.created) return
+
+ if (statusCode === HttpStatusCode.badRequest) throw new BadRequestError()
+
+ if (statusCode === HttpStatusCode.forbidden) {
+ throw new ForbiddenError(
+ 'Você não tem permissão para criar uma categoria de produto.'
+ )
+ }
+
+ throw new UnexpectedError()
+ }
+}
diff --git a/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-delete-input-use-product-category-use-case.ts b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-delete-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..dac238d0
--- /dev/null
+++ b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-delete-input-use-product-category-use-case.ts
@@ -0,0 +1,38 @@
+import { type HttpClient, HttpStatusCode } from '@/core/data/protocols/http'
+import {
+ UnexpectedError,
+ NotFoundError,
+ ForbiddenError,
+} from '@/core/domain/errors'
+
+import type { DeleteInputUseProductCategoryUseCase } from '../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export class RemoteDeleteInputUseProductCategoryUseCase
+ implements DeleteInputUseProductCategoryUseCase
+{
+ constructor(
+ private readonly url: string,
+ private readonly httpClient: HttpClient
+ ) {}
+
+ execute: DeleteInputUseProductCategoryUseCase['execute'] = async ({ id }) => {
+ const { statusCode } = await this.httpClient.request({
+ url: `${this.url}/${id}`,
+ method: 'delete',
+ })
+
+ if (statusCode === HttpStatusCode.noContent) return
+
+ if (statusCode === HttpStatusCode.notFound) {
+ throw new NotFoundError('Categoria de Produto')
+ }
+
+ if (statusCode === HttpStatusCode.forbidden) {
+ throw new ForbiddenError(
+ 'Você não tem permissão para excluir uma categoria de produto.'
+ )
+ }
+
+ throw new UnexpectedError()
+ }
+}
diff --git a/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-categories-use-case.ts b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-categories-use-case.ts
new file mode 100644
index 00000000..33297b22
--- /dev/null
+++ b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-categories-use-case.ts
@@ -0,0 +1,73 @@
+import { type HttpClient, HttpStatusCode } from '@/core/data/protocols/http'
+import {
+ UnexpectedError,
+ NotFoundError,
+ ForbiddenError,
+} from '@/core/domain/errors'
+
+import type {
+ InputUseProductCategoryApiResponse,
+ InputUseProductCategoryModel,
+} from '../../../domain/models/input-use-product-categories-model'
+import type { GetInputUseProductCategoriesUseCase } from '../../../domain/use-cases/input-use-product-categories-use-cases'
+import type { ListApiResponse, MapApiProperties } from '@/core/domain/types'
+
+export class RemoteGetInputUseProductCategoriesUseCase
+ implements GetInputUseProductCategoriesUseCase
+{
+ constructor(
+ private readonly url: string,
+ private readonly httpClient: HttpClient<
+ InputUseProductCategoryModel,
+ InputUseProductCategoryApiResponse,
+ ListApiResponse
+ >
+ ) {}
+
+ execute: GetInputUseProductCategoriesUseCase['execute'] = async ({
+ filters,
+ pagination,
+ sort,
+ }) => {
+ const mapApiProperties: MapApiProperties<
+ InputUseProductCategoryModel,
+ InputUseProductCategoryApiResponse
+ > = {
+ id: 'id',
+ name: 'name',
+ }
+
+ const { statusCode, body } = await this.httpClient.request({
+ url: `${this.url}/search`,
+ method: 'post',
+ filters,
+ pagination,
+ sort,
+ mapApiProperties,
+ })
+
+ if (statusCode === HttpStatusCode.ok && !!body) {
+ return {
+ resources: body.content.map((item) => {
+ return {
+ id: item.id,
+ name: item.name,
+ }
+ }),
+ totalPages: Math.ceil(body.numberOfElements / body.pageable.pageSize),
+ }
+ }
+
+ if (statusCode === HttpStatusCode.notFound) {
+ throw new NotFoundError('Categoria de Produto')
+ }
+
+ if (statusCode === HttpStatusCode.forbidden) {
+ throw new ForbiddenError(
+ 'Você não tem permissão para buscar as categorias de produtos.'
+ )
+ }
+
+ throw new UnexpectedError()
+ }
+}
diff --git a/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-category-use-case.ts b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..c5c12eff
--- /dev/null
+++ b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-category-use-case.ts
@@ -0,0 +1,50 @@
+import { type HttpClient, HttpStatusCode } from '@/core/data/protocols/http'
+import {
+ BadRequestError,
+ ForbiddenError,
+ NotFoundError,
+ UnexpectedError,
+} from '@/core/domain/errors'
+
+import type {
+ InputUseProductCategoryDetailsApiResponse,
+ InputUseProductCategoryDetailsModel,
+} from '../../../domain/models/input-use-product-categories-model'
+import type { GetInputUseProductCategoryUseCase } from '../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export class RemoteGetInputUseProductCategoryUseCase
+ implements GetInputUseProductCategoryUseCase
+{
+ constructor(
+ private readonly url: string,
+ private readonly httpClient: HttpClient<
+ InputUseProductCategoryDetailsModel,
+ InputUseProductCategoryDetailsApiResponse
+ >
+ ) {}
+
+ execute: GetInputUseProductCategoryUseCase['execute'] = async ({ id }) => {
+ const { statusCode, body } = await this.httpClient.request({
+ url: `${this.url}/${id}`,
+ method: 'get',
+ })
+
+ if (statusCode === HttpStatusCode.ok && !!body)
+ return {
+ name: body.name,
+ }
+
+ if (statusCode === HttpStatusCode.badRequest) throw new BadRequestError()
+
+ if (statusCode === HttpStatusCode.notFound)
+ throw new NotFoundError('Categoria de Produto')
+
+ if (statusCode === HttpStatusCode.forbidden) {
+ throw new ForbiddenError(
+ 'Você não tem permissão para acessar os dados desta categoria de produto.'
+ )
+ }
+
+ throw new UnexpectedError()
+ }
+}
diff --git a/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-update-input-use-product-category-use-case.ts b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-update-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..bc420ffe
--- /dev/null
+++ b/src/app/modules/input-uses/data/use-cases/input-use-product-categories-use-cases/remote-update-input-use-product-category-use-case.ts
@@ -0,0 +1,39 @@
+import { type HttpClient, HttpStatusCode } from '@/core/data/protocols/http'
+import {
+ BadRequestError,
+ ForbiddenError,
+ UnexpectedError,
+} from '@/core/domain/errors'
+
+import type { UpdateInputUseProductCategoryUseCase } from '../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export class RemoteUpdateInputUseProductCategoryUseCase
+ implements UpdateInputUseProductCategoryUseCase
+{
+ constructor(
+ private readonly url: string,
+ private readonly httpClient: HttpClient
+ ) {}
+
+ execute: UpdateInputUseProductCategoryUseCase['execute'] = async ({
+ inputUseProductCategory: { id, ...inputUseProductCategory },
+ }) => {
+ const { statusCode } = await this.httpClient.request({
+ url: `${this.url}/${id}`,
+ method: 'patch',
+ body: inputUseProductCategory,
+ })
+
+ if (statusCode === HttpStatusCode.noContent) return
+
+ if (statusCode === HttpStatusCode.badRequest) throw new BadRequestError()
+
+ if (statusCode === HttpStatusCode.forbidden) {
+ throw new ForbiddenError(
+ 'Você não tem permissão para editar uma categoria de produto.'
+ )
+ }
+
+ throw new UnexpectedError()
+ }
+}
diff --git a/src/app/modules/input-uses/domain/models/input-use-product-categories-model.ts b/src/app/modules/input-uses/domain/models/input-use-product-categories-model.ts
new file mode 100644
index 00000000..d46d238a
--- /dev/null
+++ b/src/app/modules/input-uses/domain/models/input-use-product-categories-model.ts
@@ -0,0 +1,17 @@
+import type { WithId } from '@/core/domain/types'
+
+export type InputUseProductCategoryDetailsModel = {
+ name: string
+}
+
+export type InputUseProductCategoryDetailsApiResponse = {
+ name: string
+}
+
+export type InputUseProductCategoryModel = WithId<{
+ name: string
+}>
+
+export type InputUseProductCategoryApiResponse = WithId<{
+ name: string
+}>
diff --git a/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/create-input-use-product-category-use-case.ts b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/create-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..10f0ac3f
--- /dev/null
+++ b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/create-input-use-product-category-use-case.ts
@@ -0,0 +1,9 @@
+import type { InputUseProductCategoryDetailsModel } from '../../models/input-use-product-categories-model'
+import type { RequestInterface } from '@/core/domain/types'
+
+export type CreateInputUseProductCategoryUseCase = RequestInterface<
+ {
+ inputUseProductCategory: InputUseProductCategoryDetailsModel
+ },
+ void
+>
diff --git a/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/delete-input-use-product-category-use-case.ts b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/delete-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..1c2a804f
--- /dev/null
+++ b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/delete-input-use-product-category-use-case.ts
@@ -0,0 +1,8 @@
+import type { RequestInterface } from '@/core/domain/types'
+
+export type DeleteInputUseProductCategoryUseCase = RequestInterface<
+ {
+ id: number
+ },
+ void
+>
diff --git a/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/get-input-use-product-categories-use-case.ts b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/get-input-use-product-categories-use-case.ts
new file mode 100644
index 00000000..74a3dd82
--- /dev/null
+++ b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/get-input-use-product-categories-use-case.ts
@@ -0,0 +1,11 @@
+import type { InputUseProductCategoryModel } from '../../models/input-use-product-categories-model'
+import type {
+ RequestInterface,
+ ListParams,
+ ListResponse,
+} from '@/core/domain/types'
+
+export type GetInputUseProductCategoriesUseCase = RequestInterface<
+ ListParams,
+ ListResponse
+>
diff --git a/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/get-input-use-product-category-use-case.ts b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/get-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..74a74a40
--- /dev/null
+++ b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/get-input-use-product-category-use-case.ts
@@ -0,0 +1,9 @@
+import type { InputUseProductCategoryDetailsModel } from '../../models/input-use-product-categories-model'
+import type { RequestInterface } from '@/core/domain/types'
+
+export type GetInputUseProductCategoryUseCase = RequestInterface<
+ {
+ id: number
+ },
+ InputUseProductCategoryDetailsModel
+>
diff --git a/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/index.ts b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/index.ts
new file mode 100644
index 00000000..5b4f9270
--- /dev/null
+++ b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/index.ts
@@ -0,0 +1,5 @@
+export * from './create-input-use-product-category-use-case'
+export * from './delete-input-use-product-category-use-case'
+export * from './get-input-use-product-category-use-case'
+export * from './get-input-use-product-categories-use-case'
+export * from './update-input-use-product-category-use-case'
diff --git a/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/update-input-use-product-category-use-case.ts b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/update-input-use-product-category-use-case.ts
new file mode 100644
index 00000000..c8e1d04c
--- /dev/null
+++ b/src/app/modules/input-uses/domain/use-cases/input-use-product-categories-use-cases/update-input-use-product-category-use-case.ts
@@ -0,0 +1,9 @@
+import type { InputUseProductCategoryDetailsModel } from '../../models/input-use-product-categories-model'
+import type { RequestInterface, WithId } from '@/core/domain/types'
+
+export type UpdateInputUseProductCategoryUseCase = RequestInterface<
+ {
+ inputUseProductCategory: WithId
+ },
+ void
+>
diff --git a/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/index.ts b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/index.ts
new file mode 100644
index 00000000..a98edc51
--- /dev/null
+++ b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/index.ts
@@ -0,0 +1,5 @@
+export * from './remote-create-input-use-product-category-use-case-factory'
+export * from './remote-update-input-use-product-category-use-case-factory'
+export * from './remote-delete-input-use-product-category-use-case-factory'
+export * from './remote-get-input-use-product-category-use-case-factory'
+export * from './remote-get-input-use-product-categories-use-case-factory'
diff --git a/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-create-input-use-product-category-use-case-factory.ts b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-create-input-use-product-category-use-case-factory.ts
new file mode 100644
index 00000000..8ede07dc
--- /dev/null
+++ b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-create-input-use-product-category-use-case-factory.ts
@@ -0,0 +1,12 @@
+import { makeApiHttpClient } from '@/core/main/factories/http'
+
+import { RemoteCreateInputUseProductCategoryUseCase } from '../../../../data/use-cases/input-use-product-categories-use-cases'
+
+import type { CreateInputUseProductCategoryUseCase } from '../../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export function makeRemoteCreateInputUseProductCategoryUseCase(): CreateInputUseProductCategoryUseCase {
+ return new RemoteCreateInputUseProductCategoryUseCase(
+ '/input-use-product-categories',
+ makeApiHttpClient()
+ )
+}
diff --git a/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-delete-input-use-product-category-use-case-factory.ts b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-delete-input-use-product-category-use-case-factory.ts
new file mode 100644
index 00000000..4305819b
--- /dev/null
+++ b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-delete-input-use-product-category-use-case-factory.ts
@@ -0,0 +1,12 @@
+import { makeApiHttpClient } from '@/core/main/factories/http'
+
+import { RemoteDeleteInputUseProductCategoryUseCase } from '../../../../data/use-cases/input-use-product-categories-use-cases'
+
+import type { DeleteInputUseProductCategoryUseCase } from '../../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export function makeRemoteDeleteInputUseProductCategoryUseCase(): DeleteInputUseProductCategoryUseCase {
+ return new RemoteDeleteInputUseProductCategoryUseCase(
+ '/input-use-product-categories',
+ makeApiHttpClient()
+ )
+}
diff --git a/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-categories-use-case-factory.ts b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-categories-use-case-factory.ts
new file mode 100644
index 00000000..ee8189f3
--- /dev/null
+++ b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-categories-use-case-factory.ts
@@ -0,0 +1,12 @@
+import { makeApiHttpClient } from '@/core/main/factories/http'
+
+import { RemoteGetInputUseProductCategoriesUseCase } from '../../../../data/use-cases/input-use-product-categories-use-cases'
+
+import type { GetInputUseProductCategoriesUseCase } from '../../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export function makeRemoteGetInputUseProductCategoriesUseCase(): GetInputUseProductCategoriesUseCase {
+ return new RemoteGetInputUseProductCategoriesUseCase(
+ '/input-use-product-categories',
+ makeApiHttpClient()
+ )
+}
diff --git a/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-category-use-case-factory.ts b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-category-use-case-factory.ts
new file mode 100644
index 00000000..47b16a72
--- /dev/null
+++ b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-get-input-use-product-category-use-case-factory.ts
@@ -0,0 +1,12 @@
+import { makeApiHttpClient } from '@/core/main/factories/http'
+
+import { RemoteGetInputUseProductCategoryUseCase } from '../../../../data/use-cases/input-use-product-categories-use-cases'
+
+import type { GetInputUseProductCategoryUseCase } from '../../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export function makeRemoteGetInputUseProductCategoryUseCase(): GetInputUseProductCategoryUseCase {
+ return new RemoteGetInputUseProductCategoryUseCase(
+ '/input-use-product-categories',
+ makeApiHttpClient()
+ )
+}
diff --git a/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-update-input-use-product-category-use-case-factory.ts b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-update-input-use-product-category-use-case-factory.ts
new file mode 100644
index 00000000..f2681d62
--- /dev/null
+++ b/src/app/modules/input-uses/main/factories/use-cases/input-use-product-categories-use-cases/remote-update-input-use-product-category-use-case-factory.ts
@@ -0,0 +1,12 @@
+import { makeApiHttpClient } from '@/core/main/factories/http'
+
+import { RemoteUpdateInputUseProductCategoryUseCase } from '../../../../data/use-cases/input-use-product-categories-use-cases'
+
+import type { UpdateInputUseProductCategoryUseCase } from '../../../../domain/use-cases/input-use-product-categories-use-cases'
+
+export function makeRemoteUpdateInputUseProductCategoryUseCase(): UpdateInputUseProductCategoryUseCase {
+ return new RemoteUpdateInputUseProductCategoryUseCase(
+ '/input-use-product-categories',
+ makeApiHttpClient()
+ )
+}
diff --git a/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/create-input-use-product-category-handler.ts b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/create-input-use-product-category-handler.ts
new file mode 100644
index 00000000..25819ac2
--- /dev/null
+++ b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/create-input-use-product-category-handler.ts
@@ -0,0 +1,19 @@
+import { HttpResponse, type PathParams } from 'msw'
+
+import { HttpStatusCode } from '@/core/data/protocols/http'
+import { httpWithMiddleware } from '@/core/mocks/lib'
+import { withAuth, withDelay } from '@/core/mocks/middleware'
+
+import type { InputUseProductCategoryDetailsModel } from '@/app/modules/input-uses/domain/models/input-use-product-categories-model'
+
+export const createInputUseProductCategoryHandler = httpWithMiddleware<
+ PathParams,
+ InputUseProductCategoryDetailsModel,
+ never
+>({
+ routePath: '/api/input-use-product-categories',
+ method: 'post',
+ middlewares: [withDelay(), withAuth],
+ resolver: async () =>
+ HttpResponse.json({}, { status: HttpStatusCode.created }),
+})
diff --git a/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/delete-input-use-product-category-handler.ts b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/delete-input-use-product-category-handler.ts
new file mode 100644
index 00000000..cc630d32
--- /dev/null
+++ b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/delete-input-use-product-category-handler.ts
@@ -0,0 +1,17 @@
+import { HttpResponse, type PathParams } from 'msw'
+
+import { HttpStatusCode } from '@/core/data/protocols/http'
+import { httpWithMiddleware } from '@/core/mocks/lib'
+import { withAuth, withDelay } from '@/core/mocks/middleware'
+
+export const deleteInputUseProductCategoryHandler = httpWithMiddleware<
+ PathParams<'id'>,
+ never,
+ never
+>({
+ routePath: '/api/input-use-product-categories/:id',
+ method: 'delete',
+ middlewares: [withDelay(), withAuth],
+ resolver: async () =>
+ HttpResponse.json(undefined, { status: HttpStatusCode.noContent }),
+})
diff --git a/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/get-input-use-product-categories-handler.ts b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/get-input-use-product-categories-handler.ts
new file mode 100644
index 00000000..cabe4e82
--- /dev/null
+++ b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/get-input-use-product-categories-handler.ts
@@ -0,0 +1,74 @@
+import { HttpResponse, type PathParams } from 'msw'
+
+import { HttpStatusCode } from '@/core/data/protocols/http'
+import { httpWithMiddleware } from '@/core/mocks/lib'
+import { withAuth, withDelay } from '@/core/mocks/middleware'
+import { filterData, paginateData, sortData } from '@/core/mocks/utils'
+
+import inputUseProductCategoriesData from '@database/inputUseProductCategoriesData.json'
+
+import type { InputUseProductCategoryApiResponse } from '@/app/modules/input-uses/domain/models/input-use-product-categories-model'
+import type { MockParams } from '@/core/mocks/types/mock-params-type'
+import type { MockResponse } from '@/core/mocks/types/mock-response-type'
+
+export const getInputUseProductCategoriesHandler = httpWithMiddleware<
+ PathParams,
+ MockParams,
+ MockResponse
+>({
+ routePath: '/api/input-use-product-categories/search',
+ method: 'post',
+ middlewares: [withDelay(), withAuth],
+ resolver: async ({ request }) => {
+ const { filters, page, rows, sort } = await request.json()
+
+ if (!inputUseProductCategoriesData.length) {
+ return HttpResponse.json(
+ {
+ content: [],
+ numberOfElements: 0,
+ pageable: {
+ pageSize: 0,
+ },
+ },
+ {
+ status: 404,
+ }
+ )
+ }
+
+ let inputUseProductCategories = inputUseProductCategoriesData
+
+ if (filters) {
+ inputUseProductCategories =
+ filterData(
+ filters,
+ inputUseProductCategories
+ )
+ }
+ if (sort) {
+ inputUseProductCategories = sortData(
+ sort,
+ inputUseProductCategories
+ )
+ }
+
+ const numberOfElements = inputUseProductCategories.length
+ inputUseProductCategories =
+ paginateData(
+ { page, perPage: rows },
+ inputUseProductCategories
+ )
+
+ return HttpResponse.json(
+ {
+ content: inputUseProductCategories,
+ numberOfElements,
+ pageable: {
+ pageSize: rows,
+ },
+ },
+ { status: HttpStatusCode.ok }
+ )
+ },
+})
diff --git a/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/get-input-use-product-category-handler.ts b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/get-input-use-product-category-handler.ts
new file mode 100644
index 00000000..66c68516
--- /dev/null
+++ b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/get-input-use-product-category-handler.ts
@@ -0,0 +1,35 @@
+import { HttpResponse, type PathParams } from 'msw'
+
+import { HttpStatusCode } from '@/core/data/protocols/http'
+import { httpWithMiddleware } from '@/core/mocks/lib'
+import { withAuth, withDelay } from '@/core/mocks/middleware'
+
+import inputUseProductCategoriesData from '@database/inputUseProductCategoriesData.json'
+
+import type { InputUseProductCategoryApiResponse } from '@/app/modules/input-uses/domain/models/input-use-product-categories-model'
+
+export const getInputUseProductCategoryHandler = httpWithMiddleware<
+ PathParams<'id'>,
+ never,
+ InputUseProductCategoryApiResponse
+>({
+ routePath: '/api/input-use-product-categories/:id',
+ method: 'get',
+ middlewares: [withDelay(), withAuth],
+ resolver: async ({ params }) => {
+ const id = Number(params.id)
+ const inputUseProductCategory = inputUseProductCategoriesData.find(
+ (item) => item.id === id
+ )
+
+ if (!inputUseProductCategory) {
+ return HttpResponse.json({} as InputUseProductCategoryApiResponse, {
+ status: HttpStatusCode.notFound,
+ })
+ }
+
+ return HttpResponse.json(inputUseProductCategory, {
+ status: HttpStatusCode.ok,
+ })
+ },
+})
diff --git a/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/index.ts b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/index.ts
new file mode 100644
index 00000000..5cb2b4aa
--- /dev/null
+++ b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/index.ts
@@ -0,0 +1,5 @@
+export * from './create-input-use-product-category-handler'
+export * from './delete-input-use-product-category-handler'
+export * from './get-input-use-product-category-handler'
+export * from './get-input-use-product-categories-handler'
+export * from './update-input-use-product-category-handler'
diff --git a/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/update-input-use-product-category-handler.ts b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/update-input-use-product-category-handler.ts
new file mode 100644
index 00000000..77a84ce5
--- /dev/null
+++ b/src/app/modules/input-uses/mocks/handlers/input-use-product-categories-handlers/update-input-use-product-category-handler.ts
@@ -0,0 +1,19 @@
+import { HttpResponse, type PathParams } from 'msw'
+
+import { HttpStatusCode } from '@/core/data/protocols/http'
+import { httpWithMiddleware } from '@/core/mocks/lib'
+import { withAuth, withDelay } from '@/core/mocks/middleware'
+
+import type { InputUseProductCategoryApiResponse } from '@/app/modules/input-uses/domain/models/input-use-product-categories-model'
+
+export const updateInputUseProductCategoryHandler = httpWithMiddleware<
+ PathParams<'id'>,
+ Omit,
+ never
+>({
+ routePath: '/api/input-use-product-categories/:id',
+ method: 'patch',
+ middlewares: [withDelay(), withAuth],
+ resolver: async () =>
+ HttpResponse.json(undefined, { status: HttpStatusCode.noContent }),
+})
diff --git a/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/index.ts b/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/index.ts
new file mode 100644
index 00000000..2a43ea00
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/index.ts
@@ -0,0 +1 @@
+export * from './input-use-product-category-data-table'
diff --git a/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/input-use-product-category-data-table.hook.tsx b/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/input-use-product-category-data-table.hook.tsx
new file mode 100644
index 00000000..b98e0f81
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/input-use-product-category-data-table.hook.tsx
@@ -0,0 +1,92 @@
+import { useMemo, useState } from 'react'
+
+import { MoreHorizontalIcon, PencilIcon, Trash2Icon } from 'lucide-react'
+
+import { DropdownMenu } from '@/core/presentation/components/ui'
+import { useDebounce } from '@/core/presentation/hooks'
+
+import { useInputUseProductCategoryContext } from '../../hooks/input-use-product-category-context.hook'
+import { useInputUseProductCategoriesQuery } from '../../hooks/queries/input-use-product-categories-query.hook'
+
+import type { InputUseProductCategoryModel } from '../../../domain/models/input-use-product-categories-model'
+import type { InputUseProductCategorySort } from '../../types/input-use-product-category-types'
+import type { ColumnDef } from '@tanstack/react-table'
+
+export function useInputUseProductCategoryDataTable() {
+ const {
+ filters,
+ openEditInputUseProductCategoryForm,
+ openDeleteInputUseProductCategoryContainer,
+ } = useInputUseProductCategoryContext()
+
+ const [page, setPage] = useState(1)
+ const [sort, setSort] = useState()
+
+ const debouncedFilters = useDebounce({ value: filters })
+
+ const { isLoading, inputUseProductCategories } =
+ useInputUseProductCategoriesQuery({
+ filters: debouncedFilters,
+ page,
+ sort,
+ })
+
+ const columns = useMemo[]>(
+ () => [
+ {
+ accessorKey: 'name',
+ header: 'Nome',
+ },
+ {
+ id: 'row-actions',
+ header: '',
+ cell: ({ row }) => {
+ const { original: inputUseProductCategory } = row
+
+ return (
+
+
+
+
+
+
+ openEditInputUseProductCategoryForm(inputUseProductCategory)
+ }
+ >
+ Editar
+
+
+
+ openDeleteInputUseProductCategoryContainer(
+ inputUseProductCategory
+ )
+ }
+ >
+ Excluir
+
+
+
+ )
+ },
+ },
+ ],
+ [
+ openDeleteInputUseProductCategoryContainer,
+ openEditInputUseProductCategoryForm,
+ ]
+ )
+
+ return {
+ columns,
+ inputUseProductCategories,
+ isLoading,
+ page,
+ sort,
+ setSort,
+ setPage,
+ }
+}
diff --git a/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/input-use-product-category-data-table.tsx b/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/input-use-product-category-data-table.tsx
new file mode 100644
index 00000000..a465a873
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/components/input-use-product-category-data-table/input-use-product-category-data-table.tsx
@@ -0,0 +1,37 @@
+import { DataTable } from '@/core/presentation/components/ui'
+
+import { useInputUseProductCategoryDataTable } from './input-use-product-category-data-table.hook'
+
+import type { InputUseProductCategoryModel } from '../../../domain/models/input-use-product-categories-model'
+
+export function InputUseProductCategoryDataTable() {
+ const {
+ columns,
+ inputUseProductCategories,
+ isLoading,
+ page,
+ sort,
+ setSort,
+ setPage,
+ } = useInputUseProductCategoryDataTable()
+
+ return (
+
+ columns={columns}
+ data={inputUseProductCategories.resources}
+ totalPages={inputUseProductCategories.totalPages}
+ pagination={{
+ currentPage: page,
+ onPageChange: setPage,
+ }}
+ sorting={{
+ currentSorting: sort,
+ onSorting: setSort,
+ }}
+ loading={isLoading}
+ />
+ )
+}
+
+InputUseProductCategoryDataTable.displayName =
+ 'InputUseProductCategoryDataTable'
diff --git a/src/app/modules/input-uses/presentation/components/input-use-product-category-delete-dialog/index.ts b/src/app/modules/input-uses/presentation/components/input-use-product-category-delete-dialog/index.ts
new file mode 100644
index 00000000..1acd0934
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/components/input-use-product-category-delete-dialog/index.ts
@@ -0,0 +1 @@
+export * from './input-use-product-category-delete-dialog'
diff --git a/src/app/modules/input-uses/presentation/components/input-use-product-category-delete-dialog/input-use-product-category-delete-dialog.tsx b/src/app/modules/input-uses/presentation/components/input-use-product-category-delete-dialog/input-use-product-category-delete-dialog.tsx
new file mode 100644
index 00000000..c7c2e332
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/components/input-use-product-category-delete-dialog/input-use-product-category-delete-dialog.tsx
@@ -0,0 +1,84 @@
+import { useCallback } from 'react'
+
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import toast from 'react-hot-toast'
+
+import { AlertDialog } from '@/core/presentation/components/ui'
+
+import { makeRemoteDeleteInputUseProductCategoryUseCase } from '../../../main/factories/use-cases/input-use-product-categories-use-cases'
+import { useInputUseProductCategoryContext } from '../../hooks/input-use-product-category-context.hook'
+
+export function InputUseProductCategoryDeleteDialog() {
+ const deleteInputUseProductCategoryUseCase =
+ makeRemoteDeleteInputUseProductCategoryUseCase()
+
+ const {
+ selectedInputUseProductCategory,
+ isOpenDeleteInputUseProductCategoryContainer,
+ closeDeleteInputUseProductCategoryContainer,
+ } = useInputUseProductCategoryContext()
+
+ const queryClient = useQueryClient()
+
+ const { mutateAsync: mutateHandleDeleteInputUseProductCategory } =
+ useMutation({
+ mutationFn: deleteInputUseProductCategoryUseCase.execute,
+ })
+
+ const handleDeleteInputUseProductCategory = useCallback(async () => {
+ if (!selectedInputUseProductCategory?.id) {
+ toast.error('Erro ao remover categoria de produto')
+ return
+ }
+
+ try {
+ await mutateHandleDeleteInputUseProductCategory({
+ id: selectedInputUseProductCategory.id,
+ })
+
+ queryClient.invalidateQueries({
+ queryKey: ['input-use-product-categories'],
+ exact: false,
+ })
+
+ toast.success('Categoria de produto removido com sucesso')
+ } catch {
+ toast.error('Erro ao remover categoria de produto')
+ } finally {
+ closeDeleteInputUseProductCategoryContainer()
+ }
+ }, [
+ closeDeleteInputUseProductCategoryContainer,
+ mutateHandleDeleteInputUseProductCategory,
+ queryClient,
+ selectedInputUseProductCategory,
+ ])
+
+ return (
+
+
+
+
+ {`Deseja remover o categoria de produto
+ ${selectedInputUseProductCategory?.name}?`}
+
+
+ Não será possível desfazer essa ação!
+
+
+
+ Cancelar
+
+ Remover
+
+
+
+
+ )
+}
+
+InputUseProductCategoryDeleteDialog.displayName =
+ 'InputUseProductCategoryDeleteDialog'
diff --git a/src/app/modules/input-uses/presentation/contexts/input-use-product-category-context.tsx b/src/app/modules/input-uses/presentation/contexts/input-use-product-category-context.tsx
new file mode 100644
index 00000000..79b93273
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/contexts/input-use-product-category-context.tsx
@@ -0,0 +1,147 @@
+import {
+ createContext,
+ useCallback,
+ useMemo,
+ useState,
+ type ReactNode,
+} from 'react'
+
+import type { InputUseProductCategoryModel } from '../../domain/models/input-use-product-categories-model'
+import type { Filters } from '@/core/domain/types'
+
+export type InputUseProductCategoryContextData = {
+ selectedInputUseProductCategory: InputUseProductCategoryModel | null
+ isOpenNewInputUseProductCategoryForm: boolean
+ isOpenEditInputUseProductCategoryForm: boolean
+ isOpenDeleteInputUseProductCategoryContainer: boolean
+ filters: Filters
+ openNewInputUseProductCategoryForm: () => void
+ closeNewInputUseProductCategoryForm: () => void
+ openEditInputUseProductCategoryForm: (
+ inputUseProductCategory: InputUseProductCategoryModel
+ ) => void
+ closeEditInputUseProductCategoryForm: () => void
+ openDeleteInputUseProductCategoryContainer: (
+ inputUseProductCategory: InputUseProductCategoryModel
+ ) => void
+ closeDeleteInputUseProductCategoryContainer: () => void
+ handleChangeFilters: (
+ newFilters: Filters
+ ) => void
+ clearFilters: () => void
+}
+
+export const InputUseProductCategoryContext =
+ createContext(
+ {} as InputUseProductCategoryContextData
+ )
+
+type InputUseProductCategoryProviderProps = {
+ children: ReactNode
+}
+
+export function InputUseProductCategoryProvider({
+ children,
+}: InputUseProductCategoryProviderProps) {
+ const [selectedInputUseProductCategory, setSelectedInputUseProductCategory] =
+ useState(null)
+ const [
+ isOpenNewInputUseProductCategoryForm,
+ setIsOpenNewInputUseProductCategoryForm,
+ ] = useState(false)
+ const [
+ isOpenEditInputUseProductCategoryForm,
+ setIsOpenEditInputUseProductCategoryForm,
+ ] = useState(false)
+ const [
+ isOpenDeleteInputUseProductCategoryContainer,
+ setIsOpenDeleteInputUseProductCategoryContainer,
+ ] = useState(false)
+ const [filters, setFilters] = useState>(
+ {}
+ )
+
+ const openNewInputUseProductCategoryForm = useCallback(() => {
+ setIsOpenNewInputUseProductCategoryForm(true)
+ }, [])
+
+ const closeNewInputUseProductCategoryForm = useCallback(() => {
+ setIsOpenNewInputUseProductCategoryForm(false)
+ }, [])
+
+ const openEditInputUseProductCategoryForm = useCallback(
+ (inputUseProductCategory: InputUseProductCategoryModel) => {
+ setSelectedInputUseProductCategory(inputUseProductCategory)
+ setIsOpenEditInputUseProductCategoryForm(true)
+ },
+ []
+ )
+
+ const closeEditInputUseProductCategoryForm = useCallback(() => {
+ setSelectedInputUseProductCategory(null)
+ setIsOpenEditInputUseProductCategoryForm(false)
+ }, [])
+
+ const openDeleteInputUseProductCategoryContainer = useCallback(
+ (inputUseProductCategory: InputUseProductCategoryModel) => {
+ setSelectedInputUseProductCategory(inputUseProductCategory)
+ setIsOpenDeleteInputUseProductCategoryContainer(true)
+ },
+ []
+ )
+
+ const closeDeleteInputUseProductCategoryContainer = useCallback(() => {
+ setSelectedInputUseProductCategory(null)
+ setIsOpenDeleteInputUseProductCategoryContainer(false)
+ }, [])
+
+ const handleChangeFilters = useCallback(
+ (newFilters: Filters) => {
+ setFilters((prev) => ({ ...prev, ...newFilters }))
+ },
+ []
+ )
+
+ const clearFilters = useCallback(() => {
+ setFilters({})
+ }, [])
+
+ const value = useMemo(
+ () => ({
+ selectedInputUseProductCategory,
+ isOpenNewInputUseProductCategoryForm,
+ isOpenEditInputUseProductCategoryForm,
+ isOpenDeleteInputUseProductCategoryContainer,
+ filters,
+ openNewInputUseProductCategoryForm,
+ closeNewInputUseProductCategoryForm,
+ openEditInputUseProductCategoryForm,
+ closeEditInputUseProductCategoryForm,
+ openDeleteInputUseProductCategoryContainer,
+ closeDeleteInputUseProductCategoryContainer,
+ handleChangeFilters,
+ clearFilters,
+ }),
+ [
+ selectedInputUseProductCategory,
+ isOpenNewInputUseProductCategoryForm,
+ isOpenEditInputUseProductCategoryForm,
+ isOpenDeleteInputUseProductCategoryContainer,
+ filters,
+ openNewInputUseProductCategoryForm,
+ closeNewInputUseProductCategoryForm,
+ openEditInputUseProductCategoryForm,
+ closeEditInputUseProductCategoryForm,
+ openDeleteInputUseProductCategoryContainer,
+ closeDeleteInputUseProductCategoryContainer,
+ handleChangeFilters,
+ clearFilters,
+ ]
+ )
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/create-input-use-product-category-form.tsx b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/create-input-use-product-category-form.tsx
new file mode 100644
index 00000000..8ed3a330
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/create-input-use-product-category-form.tsx
@@ -0,0 +1,116 @@
+import { useCallback } from 'react'
+
+import { zodResolver } from '@hookform/resolvers/zod'
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import toast from 'react-hot-toast'
+
+import {
+ Button,
+ Form,
+ ScrollArea,
+ Sheet,
+} from '@/core/presentation/components/ui'
+import { useHookForm } from '@/core/presentation/hooks'
+
+import { makeRemoteCreateInputUseProductCategoryUseCase } from '../../../main/factories/use-cases/input-use-product-categories-use-cases'
+import { useInputUseProductCategoryContext } from '../../hooks/input-use-product-category-context.hook'
+import {
+ inputUseProductCategoryFormSchema,
+ type InputUseProductCategoryFormSchema,
+} from '../../validations/input-use-product-category-form-schema'
+
+import { InputUseProductCategoryFormInputs } from './input-use-product-category-form-inputs'
+import { INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA } from './input-use-product-category-initial-form-data'
+
+export function CreateInputUseProductCategoryForm() {
+ const {
+ isOpenNewInputUseProductCategoryForm,
+ closeNewInputUseProductCategoryForm,
+ } = useInputUseProductCategoryContext()
+
+ const createInputUseProductCategoryUseCase =
+ makeRemoteCreateInputUseProductCategoryUseCase()
+
+ const queryClient = useQueryClient()
+
+ const form = useHookForm({
+ defaultValues: INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA,
+ resolver: zodResolver(inputUseProductCategoryFormSchema),
+ })
+
+ const { mutateAsync: mutateHandleCreateInputUseProductCategory } =
+ useMutation({
+ mutationFn: createInputUseProductCategoryUseCase.execute,
+ })
+
+ const handleCreateInputUseProductCategory = useCallback(
+ async (data: InputUseProductCategoryFormSchema) => {
+ try {
+ await mutateHandleCreateInputUseProductCategory({
+ inputUseProductCategory: data,
+ })
+
+ queryClient.invalidateQueries({
+ queryKey: ['input-use-product-categories'],
+ exact: false,
+ })
+
+ toast.success('Categoria de produto cadastrado com sucesso')
+
+ form.reset(INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA)
+
+ closeNewInputUseProductCategoryForm()
+ } catch {
+ toast.error('Erro ao cadastrar categoria de produto')
+ }
+ },
+ [
+ closeNewInputUseProductCategoryForm,
+ form,
+ mutateHandleCreateInputUseProductCategory,
+ queryClient,
+ ]
+ )
+
+ return (
+
+
+
+ Novo Categoria de Produto
+
+ Preencha o formulário para criar um novo categoria de produto
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+CreateInputUseProductCategoryForm.displayName =
+ 'CreateInputUseProductCategoryForm'
diff --git a/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/edit-input-use-product-category-form.tsx b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/edit-input-use-product-category-form.tsx
new file mode 100644
index 00000000..38f951c1
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/edit-input-use-product-category-form.tsx
@@ -0,0 +1,140 @@
+import { useCallback } from 'react'
+
+import { zodResolver } from '@hookform/resolvers/zod'
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import toast from 'react-hot-toast'
+
+import {
+ Button,
+ Form,
+ Loading,
+ ScrollArea,
+ Sheet,
+} from '@/core/presentation/components/ui'
+import { useHookForm } from '@/core/presentation/hooks'
+
+import { makeRemoteUpdateInputUseProductCategoryUseCase } from '../../../main/factories/use-cases/input-use-product-categories-use-cases'
+import { useInputUseProductCategoryContext } from '../../hooks/input-use-product-category-context.hook'
+import { useInputUseProductCategoryQuery } from '../../hooks/queries/input-use-product-category-query.hook'
+import {
+ inputUseProductCategoryFormSchema,
+ type InputUseProductCategoryFormSchema,
+} from '../../validations/input-use-product-category-form-schema'
+
+import { InputUseProductCategoryFormInputs } from './input-use-product-category-form-inputs'
+import { INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA } from './input-use-product-category-initial-form-data'
+
+export function EditInputUseProductCategoryForm() {
+ const {
+ isOpenEditInputUseProductCategoryForm,
+ closeEditInputUseProductCategoryForm,
+ selectedInputUseProductCategory,
+ } = useInputUseProductCategoryContext()
+
+ const { isLoading, inputUseProductCategory } =
+ useInputUseProductCategoryQuery({
+ id: selectedInputUseProductCategory!.id,
+ })
+
+ const updateInputUseProductCategoryUseCase =
+ makeRemoteUpdateInputUseProductCategoryUseCase()
+
+ const queryClient = useQueryClient()
+
+ const form = useHookForm({
+ defaultValues: INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA,
+ ...(inputUseProductCategory && {
+ values: {
+ ...inputUseProductCategory,
+ },
+ }),
+ resolver: zodResolver(inputUseProductCategoryFormSchema),
+ })
+
+ const { mutateAsync: mutateHandleUpdateInputUseProductCategory } =
+ useMutation({
+ mutationFn: updateInputUseProductCategoryUseCase.execute,
+ })
+
+ const handleUpdateInputUseProductCategory = useCallback(
+ async (data: InputUseProductCategoryFormSchema) => {
+ try {
+ if (!selectedInputUseProductCategory?.id) {
+ toast.error('Erro ao atualizar categoria de produto')
+ return
+ }
+
+ await mutateHandleUpdateInputUseProductCategory({
+ inputUseProductCategory: {
+ ...data,
+ id: selectedInputUseProductCategory.id,
+ },
+ })
+
+ queryClient.invalidateQueries({
+ queryKey: ['input-use-product-categories'],
+ exact: false,
+ })
+
+ toast.success('Categoria de produto foi editado com sucesso')
+ form.reset(INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA)
+ closeEditInputUseProductCategoryForm()
+ } catch {
+ toast.error('Erro ao salvar alterações')
+ }
+ },
+ [
+ closeEditInputUseProductCategoryForm,
+ form,
+ mutateHandleUpdateInputUseProductCategory,
+ queryClient,
+ selectedInputUseProductCategory,
+ ]
+ )
+
+ return (
+
+
+
+ Editar Categoria de Produto
+
+ Preencha o formulário para editar o categoria de produto
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+EditInputUseProductCategoryForm.displayName = 'EditInputUseProductCategoryForm'
diff --git a/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/index.ts b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/index.ts
new file mode 100644
index 00000000..755e91b2
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/index.ts
@@ -0,0 +1 @@
+export * from './input-use-product-category-form'
diff --git a/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-form-inputs.tsx b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-form-inputs.tsx
new file mode 100644
index 00000000..8dc49f4e
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-form-inputs.tsx
@@ -0,0 +1,28 @@
+import { useFormContext } from 'react-hook-form'
+
+import { Form, Input } from '@/core/presentation/components/ui'
+
+import type { InputUseProductCategoryFormSchema } from '../../validations/input-use-product-category-form-schema'
+
+export function InputUseProductCategoryFormInputs() {
+ const form = useFormContext()
+
+ return (
+ (
+
+ Nome
+
+
+
+
+
+ )}
+ />
+ )
+}
+
+InputUseProductCategoryFormInputs.displayName =
+ 'InputUseProductCategoryFormInputs'
diff --git a/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-form.tsx b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-form.tsx
new file mode 100644
index 00000000..8b0ea553
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-form.tsx
@@ -0,0 +1,18 @@
+import { CreateInputUseProductCategoryForm } from './create-input-use-product-category-form'
+import { EditInputUseProductCategoryForm } from './edit-input-use-product-category-form'
+
+type InputUseProductCategoryFormProps = {
+ id?: string | number
+}
+
+export function InputUseProductCategoryForm({
+ id,
+}: Readonly) {
+ if (id) {
+ return
+ }
+
+ return
+}
+
+InputUseProductCategoryForm.displayName = 'InputUseProductCategoryForm'
diff --git a/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-initial-form-data.ts b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-initial-form-data.ts
new file mode 100644
index 00000000..7fc451e6
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/forms/input-use-product-category-form/input-use-product-category-initial-form-data.ts
@@ -0,0 +1,3 @@
+export const INPUT_USE_PRODUCT_CATEGORY_INITIAL_FORM_DATA = {
+ name: '',
+}
diff --git a/src/app/modules/input-uses/presentation/hooks/input-use-product-category-context.hook.ts b/src/app/modules/input-uses/presentation/hooks/input-use-product-category-context.hook.ts
new file mode 100644
index 00000000..3710330c
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/hooks/input-use-product-category-context.hook.ts
@@ -0,0 +1,15 @@
+import { useContext } from 'react'
+
+import { InputUseProductCategoryContext } from '../contexts/input-use-product-category-context'
+
+export function useInputUseProductCategoryContext() {
+ const context = useContext(InputUseProductCategoryContext)
+
+ if (!context) {
+ throw new Error(
+ 'useInputUseProductCategoryContext must be used within a InputUseProductCategoryProvider'
+ )
+ }
+
+ return context
+}
diff --git a/src/app/modules/input-uses/presentation/hooks/queries/input-use-product-categories-query.hook.ts b/src/app/modules/input-uses/presentation/hooks/queries/input-use-product-categories-query.hook.ts
new file mode 100644
index 00000000..759635ab
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/hooks/queries/input-use-product-categories-query.hook.ts
@@ -0,0 +1,56 @@
+import { useEffect } from 'react'
+
+import { useQuery } from '@tanstack/react-query'
+import toast from 'react-hot-toast'
+
+import { makeRemoteGetInputUseProductCategoriesUseCase } from '../../../main/factories/use-cases/input-use-product-categories-use-cases'
+
+import type {
+ InputUseProductCategoryFilters,
+ InputUseProductCategorySort,
+} from '../../types/input-use-product-category-types'
+
+type Props = {
+ filters: InputUseProductCategoryFilters
+ page: number
+ sort?: InputUseProductCategorySort
+}
+
+export function useInputUseProductCategoriesQuery({
+ filters,
+ page,
+ sort,
+}: Props) {
+ const getInputUseProductCategoriesUseCase =
+ makeRemoteGetInputUseProductCategoriesUseCase()
+
+ const {
+ data,
+ isError,
+ error,
+ isLoading,
+ refetch: refetchInputUseProductCategories,
+ } = useQuery({
+ queryKey: ['input-use-product-categories', { page, sort, filters }],
+ queryFn: () =>
+ getInputUseProductCategoriesUseCase.execute({
+ pagination: { page },
+ sort,
+ filters,
+ }),
+ })
+
+ useEffect(() => {
+ if (isError)
+ toast.error(error?.message ?? 'Erro ao buscar categorias de produtos')
+ }, [error, isError])
+
+ return {
+ inputUseProductCategories: data ?? {
+ resources: [],
+ totalPages: 1,
+ },
+ isLoading,
+ refetchInputUseProductCategories,
+ }
+}
diff --git a/src/app/modules/input-uses/presentation/hooks/queries/input-use-product-category-query.hook.ts b/src/app/modules/input-uses/presentation/hooks/queries/input-use-product-category-query.hook.ts
new file mode 100644
index 00000000..ad2cb762
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/hooks/queries/input-use-product-category-query.hook.ts
@@ -0,0 +1,38 @@
+import { useEffect } from 'react'
+
+import { useQuery } from '@tanstack/react-query'
+import toast from 'react-hot-toast'
+
+import { makeRemoteGetInputUseProductCategoryUseCase } from '../../../main/factories/use-cases/input-use-product-categories-use-cases'
+
+type Props = {
+ id: number
+}
+
+export function useInputUseProductCategoryQuery({ id }: Props) {
+ const getInputUseProductCategoryUseCase =
+ makeRemoteGetInputUseProductCategoryUseCase()
+
+ const {
+ data: inputUseProductCategory,
+ isError,
+ error,
+ isLoading,
+ refetch: refetchInputUseProductCategory,
+ } = useQuery({
+ queryKey: ['input-use-product-category', id],
+ queryFn: () => getInputUseProductCategoryUseCase.execute({ id }),
+ enabled: !!id,
+ })
+
+ useEffect(() => {
+ if (isError)
+ toast.error(error?.message ?? 'Erro ao buscar categoria de produto')
+ }, [error, isError])
+
+ return {
+ inputUseProductCategory,
+ isLoading,
+ refetchInputUseProductCategory,
+ }
+}
diff --git a/src/app/modules/input-uses/presentation/screens/input-use-product-categories-screen.tsx b/src/app/modules/input-uses/presentation/screens/input-use-product-categories-screen.tsx
new file mode 100644
index 00000000..0c7eee66
--- /dev/null
+++ b/src/app/modules/input-uses/presentation/screens/input-use-product-categories-screen.tsx
@@ -0,0 +1,67 @@
+import { Button, Input } from '@/core/presentation/components/ui'
+
+import { InputUseProductCategoryDataTable } from '../components/input-use-product-category-data-table'
+import { InputUseProductCategoryDeleteDialog } from '../components/input-use-product-category-delete-dialog'
+import {
+ InputUseProductCategoryContext,
+ InputUseProductCategoryProvider,
+} from '../contexts/input-use-product-category-context'
+import { InputUseProductCategoryForm } from '../forms/input-use-product-category-form'
+
+export function InputUseProductCategoriesScreen() {
+ return (
+
+
+ {({
+ selectedInputUseProductCategory,
+ isOpenDeleteInputUseProductCategoryContainer,
+ isOpenNewInputUseProductCategoryForm,
+ isOpenEditInputUseProductCategoryForm,
+ filters,
+ handleChangeFilters,
+ openNewInputUseProductCategoryForm,
+ }) => (
+
+