aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.json3
-rw-r--r--.gitignore36
-rw-r--r--next-env.d.ts5
-rw-r--r--next.config.js6
-rw-r--r--orval.config.ts28
-rw-r--r--package.json31
-rw-r--r--src/components/add-item.tsx101
-rw-r--r--src/conf/mutator.ts29
-rw-r--r--src/conf/openapi-pantry.yaml87
-rw-r--r--src/index.ts2
-rw-r--r--src/model/index.ts1
-rw-r--r--src/model/pantryItem.ts14
-rw-r--r--src/pages/_app.tsx16
-rw-r--r--src/pages/index.tsx162
-rw-r--r--src/styles/Home.module.css4
-rw-r--r--src/styles/globals.css16
-rw-r--r--src/util/pantry-item-resource.ts212
-rw-r--r--tsconfig.json20
-rw-r--r--yarn.lock4229
19 files changed, 5001 insertions, 1 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..bffb357
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/.gitignore b/.gitignore
index c013b7e..6603a8c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,4 +38,38 @@ nb-configuration.xml
*.rej
# Local environment
-.env
+.env*
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+
+# build files of NextJS passed into Quarkus
+/src/main/resources/META-INF/resources/
diff --git a/next-env.d.ts b/next-env.d.ts
new file mode 100644
index 0000000..4f11a03
--- /dev/null
+++ b/next-env.d.ts
@@ -0,0 +1,5 @@
+/// <reference types="next" />
+/// <reference types="next/image-types/global" />
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/next.config.js b/next.config.js
new file mode 100644
index 0000000..a843cbe
--- /dev/null
+++ b/next.config.js
@@ -0,0 +1,6 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ reactStrictMode: true,
+}
+
+module.exports = nextConfig
diff --git a/orval.config.ts b/orval.config.ts
new file mode 100644
index 0000000..7151ef3
--- /dev/null
+++ b/orval.config.ts
@@ -0,0 +1,28 @@
+import { defineConfig } from 'orval';
+
+export default defineConfig({
+ pantry: {
+ input: './src/conf/openapi-pantry.yaml',
+ output: {
+ mode: "tags",
+ workspace: "./src",
+ target: "./util/pantry.ts",
+ schemas: "./model",
+ client: "react-query",
+ prettier: true,
+ override: {
+ useDates: true,
+ mutator: {
+ path: "./conf/mutator.ts",
+ name: "useMutator",
+ },
+ query: {
+ useQuery: true,
+ },
+ },
+ },
+ hooks: {
+ afterAllFilesWrite: 'prettier --write',
+ },
+ },
+}); \ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5938380
--- /dev/null
+++ b/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "pantry",
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build && next export",
+ "start": "next start",
+ "lint": "next lint",
+ "codegen": "orval --clean && prettier --write 'src/**/*.(ts|tsx)'",
+ "inject": "rm -rf src/main/resources/META-INF/resources && yarn build && cp -R out src/main/resources/META-INF/resources",
+ "develop": "yarn inject && mvn quarkus:dev"
+ },
+ "dependencies": {
+ "@tanstack/react-query": "^4.1.3",
+ "axios": "^0.27.2",
+ "immutable": "^4.1.0",
+ "next": "12.2.5",
+ "orval": "^6.9.6",
+ "prettier": "^2.7.1",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "semantic-ui-css": "^2.4.1",
+ "semantic-ui-react": "^2.1.3"
+ },
+ "devDependencies": {
+ "@types/node": "18.7.3",
+ "@types/react": "18.0.17",
+ "eslint": "8.22.0",
+ "eslint-config-next": "12.2.5",
+ "typescript": "4.7.4"
+ }
+}
diff --git a/src/components/add-item.tsx b/src/components/add-item.tsx
new file mode 100644
index 0000000..9c4a998
--- /dev/null
+++ b/src/components/add-item.tsx
@@ -0,0 +1,101 @@
+import { ChangeEvent, useMemo, useState } from "react";
+import { Button, Form, InputOnChangeData, Segment } from "semantic-ui-react";
+import { PantryItem } from "../model";
+
+const defaultPantryItem = () =>
+ ({
+ name: "",
+ description: "",
+ quantity: 1,
+ quantityUnitType: "oz",
+ } as PantryItem);
+
+interface AddItemProps {
+ addItem: (item: PantryItem) => Promise<void>;
+}
+
+const AddItem = ({ addItem }: AddItemProps) => {
+ const [additionItem, setAdditionItem] = useState<PantryItem | undefined>();
+ const [additionItemLoading, setAdditionItemLoading] = useState(false);
+
+ const handleItemChange = (
+ _: ChangeEvent<HTMLInputElement>,
+ { name, value }: InputOnChangeData
+ ) =>
+ setAdditionItem((item) => ({
+ ...(item !== undefined ? item : defaultPantryItem()),
+ [name]: value,
+ }));
+
+ const newItem = useMemo(
+ () => additionItem ?? defaultPantryItem(),
+ [additionItem]
+ );
+
+ return (
+ <>
+ <Segment attached="top" hidden={additionItem !== undefined}>
+ <Button primary onClick={() => setAdditionItem(defaultPantryItem())}>
+ Add Item
+ </Button>
+ </Segment>
+ <Segment attached="top" hidden={additionItem === undefined}>
+ <Form
+ loading={additionItemLoading}
+ onSubmit={() => {
+ setAdditionItemLoading(true);
+ addItem(newItem).then(() => {
+ setAdditionItem(undefined);
+ setAdditionItemLoading(false);
+ });
+ }}
+ >
+ <Form.Group widths="equal">
+ <Form.Input
+ name="name"
+ label="Item Name"
+ placeholder="Item name"
+ value={newItem.name}
+ onChange={handleItemChange}
+ />
+ <Form.Input
+ fluid
+ name="description"
+ label="Item Description"
+ placeholder="Item description"
+ value={newItem.description}
+ onChange={handleItemChange}
+ />
+ <Form.Input
+ fluid
+ name="quantity"
+ label="Item Quantity"
+ placeholder="Item quantity"
+ value={newItem.quantity}
+ onChange={handleItemChange}
+ />
+ <Form.Input
+ name="quantityUnitType"
+ label="Quantity Type"
+ placeholder="Quantity type"
+ value={newItem.quantityUnitType}
+ onChange={handleItemChange}
+ />
+ </Form.Group>
+ <Form.Group>
+ <Form.Button primary content="Submit Item" type="submit" />
+ <Form.Button
+ content="Cancel"
+ onClick={(e) => {
+ e.preventDefault();
+ setAdditionItem(undefined);
+ }}
+ />
+ </Form.Group>
+ </Form>
+ </Segment>
+ </>
+ );
+};
+
+export default AddItem;
diff --git a/src/conf/mutator.ts b/src/conf/mutator.ts
new file mode 100644
index 0000000..74f29e3
--- /dev/null
+++ b/src/conf/mutator.ts
@@ -0,0 +1,29 @@
+import Axios, { AxiosError, AxiosRequestConfig } from "axios";
+
+export const AXIOS_PANTRY_INSTANCE = Axios.create({
+ baseURL: process.env.REACT_APP_API_SERVER!,
+});
+
+export const useMutator = <T>(): ((
+ config: AxiosRequestConfig
+) => Promise<T>) => {
+ return (config: AxiosRequestConfig) => {
+ const source = Axios.CancelToken.source();
+ const promise = AXIOS_PANTRY_INSTANCE({
+ ...config,
+ cancelToken: source.token,
+ }).then(({ data }) => data);
+
+ // @ts-ignore
+ promise.cancel = () => {
+ source.cancel("Query was cancelled by React Query!");
+ };
+
+ return promise;
+ };
+};
+
+export default useMutator;
+
+// In some case with react-query and swr you want to be able to override the return error type so you can also do it here like this
+export type ErrorType<Error> = AxiosError<Error>;
diff --git a/src/conf/openapi-pantry.yaml b/src/conf/openapi-pantry.yaml
new file mode 100644
index 0000000..8409064
--- /dev/null
+++ b/src/conf/openapi-pantry.yaml
@@ -0,0 +1,87 @@
+---
+openapi: 3.0.3
+info:
+ title: pantry API
+ version: 1.0.0-SNAPSHOT
+paths:
+ /items:
+ get:
+ tags:
+ - Pantry Item Resource
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/PantryItem'
+ post:
+ tags:
+ - Pantry Item Resource
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PantryItem'
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PantryItem'
+ /items/{id}:
+ put:
+ tags:
+ - Pantry Item Resource
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int64
+ type: integer
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PantryItem'
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/PantryItem'
+ delete:
+ tags:
+ - Pantry Item Resource
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int64
+ type: integer
+ responses:
+ "204":
+ description: No Content
+components:
+ schemas:
+ PantryItem:
+ type: object
+ properties:
+ id:
+ format: int64
+ type: integer
+ name:
+ type: string
+ description:
+ type: string
+ quantity:
+ format: double
+ type: number
+ quantityUnitType:
+ type: string
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..f298f13
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,2 @@
+export * from "./model";
+export * from "./util/pantry-item-resource";
diff --git a/src/model/index.ts b/src/model/index.ts
new file mode 100644
index 0000000..a60da79
--- /dev/null
+++ b/src/model/index.ts
@@ -0,0 +1 @@
+export * from "./pantryItem";
diff --git a/src/model/pantryItem.ts b/src/model/pantryItem.ts
new file mode 100644
index 0000000..c3a6347
--- /dev/null
+++ b/src/model/pantryItem.ts
@@ -0,0 +1,14 @@
+/**
+ * Generated by orval v6.9.6 🍺
+ * Do not edit manually.
+ * pantry API
+ * OpenAPI spec version: 1.0.0-SNAPSHOT
+ */
+
+export interface PantryItem {
+ id?: number;
+ name?: string;
+ description?: string;
+ quantity?: number;
+ quantityUnitType?: string;
+}
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
new file mode 100644
index 0000000..10121ab
--- /dev/null
+++ b/src/pages/_app.tsx
@@ -0,0 +1,16 @@
+import "semantic-ui-css/semantic.min.css";
+import "../styles/globals.css";
+import type { AppProps } from "next/app";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+const queryClient = new QueryClient();
+
+function MyApp({ Component, pageProps }: AppProps) {
+ return (
+ <QueryClientProvider client={queryClient}>
+ <Component {...pageProps} />
+ </QueryClientProvider>
+ );
+}
+
+export default MyApp;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
new file mode 100644
index 0000000..674878a
--- /dev/null
+++ b/src/pages/index.tsx
@@ -0,0 +1,162 @@
+import type { NextPage } from "next";
+import Head from "next/head";
+import { List } from "immutable";
+import {
+ Header,
+ Table,
+ Message,
+ Container,
+ Pagination,
+} from "semantic-ui-react";
+
+import styles from "../styles/Home.module.css";
+import { useMemo, useState } from "react";
+import AddItem from "../components/add-item";
+import {
+ getGetItemsQueryKey,
+ useGetItems,
+ usePostItemsHook,
+} from "../util/pantry-item-resource";
+import { PantryItem } from "../model";
+import { useMutation, useQueryClient } from "@tanstack/react-query";
+
+const ENTRIES_PER_PAGE = Number(process.env.ENTRIES_PER_PAGE ?? "10");
+
+interface SortStateProps {
+ field: keyof PantryItem;
+ order: "ascending" | "descending";
+}
+
+const Home: NextPage = () => {
+ const { data, error } = useGetItems();
+ const postItems = usePostItemsHook();
+ const queryClient = useQueryClient();
+ const { mutate } = useMutation(postItems, {
+ onSuccess: async (item) => {
+ queryClient.setQueryData(
+ getGetItemsQueryKey(),
+ (data ?? []).concat(item)
+ );
+ },
+ });
+ const [activePage, setActivePage] = useState(1);
+ const [sortState, setSortState] = useState<SortStateProps>({
+ field: "name",
+ order: "ascending",
+ });
+
+ const hasEntries = useMemo(
+ () => error === null && (data === undefined || data.length > 0),
+ [error, data]
+ );
+ const entries = useMemo(() => {
+ const list = List<PantryItem>(data);
+ // case insensitive sort
+ const sorted = list.sortBy((item) =>
+ item[sortState.field]?.toString().toUpperCase()
+ );
+
+ return sortState.order === "ascending" ? sorted : sorted.reverse();
+ }, [data, sortState]);
+ const handleSortChange = (field: keyof PantryItem) => {
+ setSortState((state) =>
+ state.field === field
+ ? {
+ ...state,
+ order: state.order === "ascending" ? "descending" : "ascending",
+ }
+ : { field: field, order: "ascending" }
+ );
+ };
+
+ return (
+ <Container className={styles.container}>
+ <Head>
+ <title>Pantry</title>
+ <meta
+ name="description"
+ content="Meal planning with inventory management"
+ />
+ <link rel="icon" href="/favicon.ico" />
+ </Head>
+
+ <Header as="h1" className="title">
+ Pantry
+ </Header>
+
+ <AddItem addItem={(newItem) => Promise.resolve(mutate(newItem))} />
+ <Message error={error !== null} attached hidden={hasEntries}>
+ {error !== null
+ ? error.message !== undefined
+ ? `Network error occurred: ${error.message}`
+ : "Unknown network error occurred"
+ : "Nothing's in the pantry at the moment!"}
+ </Message>
+ <Table sortable attached="bottom">
+ <Table.Header>
+ <Table.Row>
+ <Table.HeaderCell
+ sorted={sortState.field === "name" ? sortState.order : undefined}
+ onClick={() => handleSortChange("name")}
+ >
+ Name
+ </Table.HeaderCell>
+ <Table.HeaderCell
+ sorted={
+ sortState.field === "description" ? sortState.order : undefined
+ }
+ onClick={() => handleSortChange("description")}
+ >
+ Description
+ </Table.HeaderCell>
+ <Table.HeaderCell
+ sorted={
+ sortState.field === "quantity" ? sortState.order : undefined
+ }
+ onClick={() => handleSortChange("quantity")}
+ >
+ Quantity
+ </Table.HeaderCell>
+ </Table.Row>
+ </Table.Header>
+ <Table.Body>
+ {entries
+ .valueSeq()
+ .slice(
+ (activePage - 1) * ENTRIES_PER_PAGE,
+ activePage * ENTRIES_PER_PAGE
+ )
+ .map((item: PantryItem) => (
+ <Table.Row key={item.id}>
+ <Table.Cell>{item.name}</Table.Cell>
+ <Table.Cell>
+ {item.description === "" ? "—" : item.description}
+ </Table.Cell>
+ <Table.Cell>
+ {item.quantity} {item.quantityUnitType}
+ </Table.Cell>
+ </Table.Row>
+ ))}
+ </Table.Body>
+ <Table.Footer>
+ <Table.Row>
+ <Table.HeaderCell colSpan="3">
+ <Pagination
+ activePage={activePage}
+ onPageChange={(_, { activePage }) =>
+ setActivePage(Number(activePage ?? 1))
+ }
+ totalPages={Math.max(
+ 1,
+ Math.ceil(entries.size / ENTRIES_PER_PAGE)
+ )}
+ />
+ </Table.HeaderCell>
+ </Table.Row>
+ </Table.Footer>
+ </Table>
+ </Container>
+ );
+};
+
+export default Home;
diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css
new file mode 100644
index 0000000..ae734f6
--- /dev/null
+++ b/src/styles/Home.module.css
@@ -0,0 +1,4 @@
+.action {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 0000000..344cd26
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,16 @@
+html,
+body {
+ padding: 20px 0;
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
+ Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+* {
+ box-sizing: border-box;
+}
diff --git a/src/util/pantry-item-resource.ts b/src/util/pantry-item-resource.ts
new file mode 100644
index 0000000..aedb286
--- /dev/null
+++ b/src/util/pantry-item-resource.ts
@@ -0,0 +1,212 @@
+/**
+ * Generated by orval v6.9.6 🍺
+ * Do not edit manually.
+ * pantry API
+ * OpenAPI spec version: 1.0.0-SNAPSHOT
+ */
+import { useQuery, useMutation } from "@tanstack/react-query";
+import type {
+ UseQueryOptions,
+ UseMutationOptions,
+ QueryFunction,
+ MutationFunction,
+ UseQueryResult,
+ QueryKey,
+} from "@tanstack/react-query";
+import type { PantryItem } from "../model";
+import { useMutator } from "../conf/mutator";
+import type { ErrorType } from "../conf/mutator";
+
+export const useGetItemsHook = () => {
+ const getItems = useMutator<PantryItem[]>();
+
+ return (signal?: AbortSignal) => {
+ return getItems({ url: `/items`, method: "get", signal });
+ };
+};
+
+export const getGetItemsQueryKey = () => [`/items`];
+
+export type GetItemsQueryResult = NonNullable<
+ Awaited<ReturnType<ReturnType<typeof useGetItemsHook>>>
+>;
+export type GetItemsQueryError = ErrorType<unknown>;
+
+export const useGetItems = <
+ TData = Awaited<ReturnType<ReturnType<typeof useGetItemsHook>>>,
+ TError = ErrorType<unknown>
+>(options?: {
+ query?: UseQueryOptions<
+ Awaited<ReturnType<ReturnType<typeof useGetItemsHook>>>,
+ TError,
+ TData
+ >;
+}): UseQueryResult<TData, TError> & { queryKey: QueryKey } => {
+ const { query: queryOptions } = options ?? {};
+
+ const queryKey = queryOptions?.queryKey ?? getGetItemsQueryKey();
+
+ const getItems = useGetItemsHook();
+
+ const queryFn: QueryFunction<
+ Awaited<ReturnType<ReturnType<typeof useGetItemsHook>>>
+ > = ({ signal }) => getItems(signal);
+
+ const query = useQuery<
+ Awaited<ReturnType<ReturnType<typeof useGetItemsHook>>>,
+ TError,
+ TData
+ >(queryKey, queryFn, queryOptions) as UseQueryResult<TData, TError> & {
+ queryKey: QueryKey;
+ };
+
+ query.queryKey = queryKey;
+
+ return query;
+};
+
+export const usePostItemsHook = () => {
+ const postItems = useMutator<PantryItem>();
+
+ return (pantryItem: PantryItem) => {
+ return postItems({
+ url: `/items`,
+ method: "post",
+ headers: { "Content-Type": "application/json" },
+ data: pantryItem,
+ });
+ };
+};
+
+export type PostItemsMutationResult = NonNullable<
+ Awaited<ReturnType<ReturnType<typeof usePostItemsHook>>>
+>;
+export type PostItemsMutationBody = PantryItem;
+export type PostItemsMutationError = ErrorType<unknown>;
+
+export const usePostItems = <
+ TError = ErrorType<unknown>,
+ TContext = unknown
+>(options?: {
+ mutation?: UseMutationOptions<
+ Awaited<ReturnType<ReturnType<typeof usePostItemsHook>>>,
+ TError,
+ { data: PantryItem },
+ TContext
+ >;
+}) => {
+ const { mutation: mutationOptions } = options ?? {};
+
+ const postItems = usePostItemsHook();
+
+ const mutationFn: MutationFunction<
+ Awaited<ReturnType<ReturnType<typeof usePostItemsHook>>>,
+ { data: PantryItem }
+ > = (props) => {
+ const { data } = props ?? {};
+
+ return postItems(data);
+ };
+
+ return useMutation<
+ Awaited<ReturnType<typeof postItems>>,
+ TError,
+ { data: PantryItem },
+ TContext
+ >(mutationFn, mutationOptions);
+};
+export const usePutItemsIdHook = () => {
+ const putItemsId = useMutator<PantryItem>();
+
+ return (id: number, pantryItem: PantryItem) => {
+ return putItemsId({
+ url: `/items/${id}`,
+ method: "put",
+ headers: { "Content-Type": "application/json" },
+ data: pantryItem,
+ });
+ };
+};
+
+export type PutItemsIdMutationResult = NonNullable<
+ Awaited<ReturnType<ReturnType<typeof usePutItemsIdHook>>>
+>;
+export type PutItemsIdMutationBody = PantryItem;
+export type PutItemsIdMutationError = ErrorType<unknown>;
+
+export const usePutItemsId = <
+ TError = ErrorType<unknown>,
+ TContext = unknown
+>(options?: {
+ mutation?: UseMutationOptions<
+ Awaited<ReturnType<ReturnType<typeof usePutItemsIdHook>>>,
+ TError,
+ { id: number; data: PantryItem },
+ TContext
+ >;
+}) => {
+ const { mutation: mutationOptions } = options ?? {};
+
+ const putItemsId = usePutItemsIdHook();
+
+ const mutationFn: MutationFunction<
+ Awaited<ReturnType<ReturnType<typeof usePutItemsIdHook>>>,
+ { id: number; data: PantryItem }
+ > = (props) => {
+ const { id, data } = props ?? {};
+
+ return putItemsId(id, data);
+ };
+
+ return useMutation<
+ Awaited<ReturnType<typeof putItemsId>>,
+ TError,
+ { id: number; data: PantryItem },
+ TContext
+ >(mutationFn, mutationOptions);
+};
+export const useDeleteItemsIdHook = () => {
+ const deleteItemsId = useMutator<void>();
+
+ return (id: number) => {
+ return deleteItemsId({ url: `/items/${id}`, method: "delete" });
+ };
+};
+
+export type DeleteItemsIdMutationResult = NonNullable<
+ Awaited<ReturnType<ReturnType<typeof useDeleteItemsIdHook>>>
+>;
+
+export type DeleteItemsIdMutationError = ErrorType<unknown>;
+
+export const useDeleteItemsId = <
+ TError = ErrorType<unknown>,
+ TContext = unknown
+>(options?: {
+ mutation?: UseMutationOptions<
+ Awaited<ReturnType<ReturnType<typeof useDeleteItemsIdHook>>>,
+ TError,
+ { id: number },
+ TContext
+ >;
+}) => {
+ const { mutation: mutationOptions } = options ?? {};
+
+ const deleteItemsId = useDeleteItemsIdHook();
+
+ const mutationFn: MutationFunction<
+ Awaited<ReturnType<ReturnType<typeof useDeleteItemsIdHook>>>,
+ { id: number }
+ > = (props) => {
+ const { id } = props ?? {};
+
+ return deleteItemsId(id);
+ };
+
+ return useMutation<
+ Awaited<ReturnType<typeof deleteItemsId>>,
+ TError,
+ { id: number },
+ TContext
+ >(mutationFn, mutationOptions);
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..99710e8
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..aba29fd
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,4229 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@apidevtools/json-schema-ref-parser@9.0.6":
+ version "9.0.6"
+ resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c"
+ integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==
+ dependencies:
+ "@jsdevtools/ono" "^7.1.3"
+ call-me-maybe "^1.0.1"
+ js-yaml "^3.13.1"
+
+"@apidevtools/openapi-schemas@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17"
+ integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==
+
+"@apidevtools/swagger-methods@^3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267"
+ integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==
+
+"@apidevtools/swagger-parser@^10.1.0":
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz#a987d71e5be61feb623203be0c96e5985b192ab6"
+ integrity sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==
+ dependencies:
+ "@apidevtools/json-schema-ref-parser" "9.0.6"
+ "@apidevtools/openapi-schemas" "^2.1.0"
+ "@apidevtools/swagger-methods" "^3.0.2"
+ "@jsdevtools/ono" "^7.1.3"
+ ajv "^8.6.3"
+ ajv-draft-04 "^1.0.0"
+ call-me-maybe "^1.0.1"
+
+"@asyncapi/specs@^2.14.0":
+ version "2.14.0"
+ resolved "https://registry.yarnpkg.com/@asyncapi/specs/-/specs-2.14.0.tgz#a4535fedde931181f20d41356ed1906d0fb73d48"
+ integrity sha512-hHsYF6XsYNIKb1P2rXaooF4H+uKKQ4b/Ljxrk3rZ3riEDiSxMshMEfb1fUlw9Yj4V4OmJhjXwkNvw8W59AXv1A==
+
+"@babel/runtime-corejs3@^7.10.2":
+ version "7.16.8"
+ resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz#ea533d96eda6fdc76b1812248e9fbd0c11d4a1a7"
+ integrity sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg==
+ dependencies:
+ core-js-pure "^3.20.2"
+ regenerator-runtime "^0.13.4"
+
+"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.16.3":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa"
+ integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@esbuild/linux-loong64@0.14.54":
+ version "0.14.54"
+ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
+ integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
+
+"@eslint/eslintrc@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
+ integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.3.2"
+ globals "^13.15.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
+"@exodus/schemasafe@^1.0.0-rc.2":
+ version "1.0.0-rc.7"
+ resolved "https://registry.yarnpkg.com/@exodus/schemasafe/-/schemasafe-1.0.0-rc.7.tgz#aded6839c2369883dafa46608a135c82b42ed76b"
+ integrity sha512-+1mBLsa+vvlV0lwEAP1hwgmOPkjMnoJ8hyCMfCCJga0sVDwDzrPJjnxZwdDaUmOh/vbFHQGBTk+FxsVjoI/CjQ==
+
+"@fluentui/react-component-event-listener@~0.63.0":
+ version "0.63.1"
+ resolved "https://registry.yarnpkg.com/@fluentui/react-component-event-listener/-/react-component-event-listener-0.63.1.tgz#c2af94893671f1d6bfe2a8a07dcfa2cb01b85f52"
+ integrity sha512-gSMdOh6tI3IJKZFqxfQwbTpskpME0CvxdxGM2tdglmf6ZPVDi0L4+KKIm+2dN8nzb8Ya1A8ZT+Ddq0KmZtwVQg==
+ dependencies:
+ "@babel/runtime" "^7.10.4"
+
+"@fluentui/react-component-ref@~0.63.0":
+ version "0.63.1"
+ resolved "https://registry.yarnpkg.com/@fluentui/react-component-ref/-/react-component-ref-0.63.1.tgz#a7778e2a5c724d12e828afd994c93fa2da44f64e"
+ integrity sha512-8MkXX4+R3i80msdbD4rFpEB4WWq2UDvGwG386g3ckIWbekdvN9z2kWAd9OXhRGqB7QeOsoAGWocp6gAMCivRlw==
+ dependencies:
+ "@babel/runtime" "^7.10.4"
+ react-is "^16.6.3"
+
+"@humanwhocodes/config-array@^0.10.4":
+ version "0.10.4"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c"
+ integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.4"
+
+"@humanwhocodes/gitignore-to-minimatch@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d"
+ integrity sha512-rSqmMJDdLFUsyxR6