diff --git a/package-lock.json b/package-lock.json index dcb5da919..3faec5244 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,11 +11,16 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.14", + "@types/node": "^22.9.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "axios": "^1.7.7", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "typescript": "^5.6.3", "web-vitals": "^2.1.4" } }, @@ -4056,9 +4061,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", - "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -4307,9 +4312,12 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { - "version": "20.5.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", - "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==" + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "dependencies": { + "undici-types": "~6.19.8" + } }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -4342,19 +4350,18 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/react": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", - "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "version": "18.3.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", + "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", - "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "dependencies": { "@types/react": "*" } @@ -4372,11 +4379,6 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "node_modules/@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -16692,16 +16694,15 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -16723,6 +16724,11 @@ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index cdb1393c7..5237e30d6 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,16 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/jest": "^29.5.14", + "@types/node": "^22.9.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", "axios": "^1.7.7", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "typescript": "^5.6.3", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/@types/global/index.d.ts b/src/@types/global/index.d.ts new file mode 100644 index 000000000..e238d01ca --- /dev/null +++ b/src/@types/global/index.d.ts @@ -0,0 +1,2 @@ +declare module '*.png'; +declare module '*.svg'; diff --git a/src/App.js b/src/App.tsx similarity index 100% rename from src/App.js rename to src/App.tsx diff --git a/src/components/AllItemsSection.js b/src/components/AllItemsSection.tsx similarity index 79% rename from src/components/AllItemsSection.js rename to src/components/AllItemsSection.tsx index 4ec1bf64d..1e7b7b003 100644 --- a/src/components/AllItemsSection.js +++ b/src/components/AllItemsSection.tsx @@ -4,6 +4,11 @@ import ItemCard from './ItemCard'; import '../style/Items.css'; import { ReactComponent as SearchIcon } from '../images/ic_search.svg'; import { Link, useNavigate } from 'react-router-dom'; +import { + Product, + ProductListResponse, + ProductOrderBy, +} from './types/productTypes'; const getPageSize = () => { const width = window.innerWidth; @@ -20,17 +25,29 @@ const getPageSize = () => { }; function AllItemsSection() { - const [orderBy, setOrderBy] = useState('recent'); + const [orderBy, setOrderBy] = useState('recent'); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(getPageSize()); - const [itemList, setItemList] = useState([]); + const [itemList, setItemList] = useState([]); const [isDropdownVisible, setIsDropdownVisible] = useState(false); - const [totalPageNum, setTotalPageNum] = useState(); + const [totalPageNum, setTotalPageNum] = useState(1); const navigate = useNavigate(); - const fetchSortedData = async ({ orderBy, page, pageSize }) => { + const fetchSortedData = async ({ + orderBy, + page, + pageSize, + }: { + orderBy: ProductOrderBy; + page: number; + pageSize: number; + }) => { try { - const products = await getProducts({ orderBy, page, pageSize }); + const products: ProductListResponse = await getProducts({ + orderBy, + page, + pageSize, + }); setItemList(products.list); setTotalPageNum(Math.ceil(products.totalCount / pageSize)); } catch (error) { diff --git a/src/components/BestItemsSection.js b/src/components/BestItemsSection.tsx similarity index 75% rename from src/components/BestItemsSection.js rename to src/components/BestItemsSection.tsx index e619d2dc9..cc86cf8e0 100644 --- a/src/components/BestItemsSection.js +++ b/src/components/BestItemsSection.tsx @@ -2,6 +2,11 @@ import { useEffect, useState } from 'react'; import { getProducts } from '../api'; import ItemCard from './ItemCard'; import '../style/Items.css'; +import { + Product, + ProductListResponse, + ProductOrderBy, +} from './types/productTypes'; const getPageSize = () => { const width = window.innerWidth; @@ -18,11 +23,20 @@ const getPageSize = () => { }; function BestItemSection() { - const [itemList, setItemList] = useState([]); + const [itemList, setItemList] = useState([]); const [pageSize, setPageSize] = useState(getPageSize()); - const fetchSortedData = async ({ orderBy, pageSize }) => { - const products = await getProducts({ orderBy, pageSize }); + const fetchSortedData = async ({ + orderBy, + pageSize, + }: { + orderBy: ProductOrderBy; + pageSize: number; + }) => { + const products: ProductListResponse = await getProducts({ + orderBy, + pageSize, + }); setItemList(products.list); }; diff --git a/src/components/DropdownMenu.js b/src/components/DropdownMenu.tsx similarity index 88% rename from src/components/DropdownMenu.js rename to src/components/DropdownMenu.tsx index a4ad28522..2dc9d36e8 100644 --- a/src/components/DropdownMenu.js +++ b/src/components/DropdownMenu.tsx @@ -2,7 +2,11 @@ import React, { useState } from 'react'; import { ReactComponent as Dropdown } from '../images/ic_kebab.svg'; import '../style/DropdownMenu.css'; -function DropdownMenu({ onSelection }) { +interface DropdownMenuProps { + onSelection: (Option: string) => void; +} + +function DropdownMenu({ onSelection }: DropdownMenuProps) { const [isDropdownVisible, setIsDropdownVisible] = useState(false); const toggleDropdown = () => { diff --git a/src/components/FileInput.js b/src/components/FileInput.tsx similarity index 75% rename from src/components/FileInput.js rename to src/components/FileInput.tsx index 0c48af5bd..3cf9da949 100644 --- a/src/components/FileInput.js +++ b/src/components/FileInput.tsx @@ -1,15 +1,25 @@ -import { useEffect, useRef, useState } from 'react'; +import { ChangeEvent, useEffect, useRef, useState } from 'react'; import '../style/ProductCreateForm.css'; import { ReactComponent as PlusIcon } from '../images/ic_plus.svg'; import { ReactComponent as DeleteIcon } from '../images/ic_X.svg'; -function FileInput({ name, value, initialPreview, onChange }) { +interface FileInputProps { + name: string; + value: File | null; + initialPreview: string | null; + onChange: (name: string, value: File | null) => void; +} + +function FileInput({ name, value, initialPreview, onChange }: FileInputProps) { const [preview, setPreview] = useState(initialPreview); - const inputRef = useRef(); + const inputRef = useRef(null); const [isImageValid, setIsImageValid] = useState(false); - const handleChange = (e) => { - const nextValue = e.target.files[0]; + const handleChange = (e: ChangeEvent) => { + const fileList = e.target.files; + if (!fileList || fileList.length === 0) return; + + const nextValue = fileList[0]; onChange(name, nextValue); setIsImageValid(true); }; @@ -55,7 +65,7 @@ function FileInput({ name, value, initialPreview, onChange }) {
이미지 미리보기