diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 8654cf40f..c19d2c666 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -3,6 +3,7 @@ export { customFetch } from './customFetch'; export { QUERY_KEY } from './queryKeys'; export { TEMPLATE_API_URL, + PAGE_SIZE, getTemplateList, getTemplate, postTemplate, diff --git a/frontend/src/api/templates.ts b/frontend/src/api/templates.ts index 81c3767ce..eb9be0254 100644 --- a/frontend/src/api/templates.ts +++ b/frontend/src/api/templates.ts @@ -1,24 +1,35 @@ -import type { Template, TemplateEditRequest, TemplateListResponse, TemplateUploadRequest } from '@/types'; +import type { + Template, + TemplateEditRequest, + TemplateListResponse, + TemplateUploadRequest, + TemplateListRequest, +} from '@/types'; import { customFetch } from './customFetch'; const API_URL = process.env.REACT_APP_API_URL; export const TEMPLATE_API_URL = `${API_URL}/templates`; -export const getTemplateList = async ( - categoryId?: number, - tagId?: number, - page: number = 1, - pageSize: number = 20, -): Promise => { +export const PAGE_SIZE = 20; + +export const getTemplateList = async ({ + categoryId, + tagIds, + page = 1, + pageSize = PAGE_SIZE, + keyword = '', +}: TemplateListRequest): Promise => { const url = new URL(TEMPLATE_API_URL); + url.searchParams.append('keyword', keyword); + if (categoryId) { url.searchParams.append('categoryId', categoryId.toString()); } - if (tagId) { - url.searchParams.append('tags', tagId.toString()); + if (tagIds) { + url.searchParams.append('tagIds', tagIds.toString()); } url.searchParams.append('pageNumber', page.toString()); diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index c1250a3ca..f748a4c56 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -1 +1,2 @@ export { useWindowWidth } from './useWindowWidth'; +export { useDebounce } from './utils/useDebounce'; diff --git a/frontend/src/hooks/utils/useDebounce.ts b/frontend/src/hooks/utils/useDebounce.ts new file mode 100644 index 000000000..576c6688f --- /dev/null +++ b/frontend/src/hooks/utils/useDebounce.ts @@ -0,0 +1,17 @@ +import { useState, useEffect } from 'react'; + +export const useDebounce = (value: string, delay: number) => { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + return () => { + clearTimeout(handler); + }; + }, [value, delay]); + + return debouncedValue; +}; diff --git a/frontend/src/mocks/categoryList.json b/frontend/src/mocks/categoryList.json index 9cba418a2..ed83c49aa 100644 --- a/frontend/src/mocks/categoryList.json +++ b/frontend/src/mocks/categoryList.json @@ -1,5 +1,9 @@ { "categories": [ + { + "id": 1, + "name": "카테고리 없음" + }, { "id": 2, "name": "Category1" diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index fc4f226af..c94b38b66 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -17,20 +17,30 @@ import mockTemplateList from './templateList.json'; export const templateHandlers = [ http.get(`${TEMPLATE_API_URL}`, (req) => { const url = new URL(req.request.url); - const categoryId = url.searchParams.get('category'); - const tagId = url.searchParams.get('tag'); + const keyword = url.searchParams.get('keyword'); + const categoryId = url.searchParams.get('categoryId'); + const tagIds = url.searchParams.get('tagIds'); const page = parseInt(url.searchParams.get('page') || '1', 10); const pageSize = parseInt(url.searchParams.get('pageSize') || '20', 10); let filteredTemplates = mockTemplateList.templates; + if (keyword) { + filteredTemplates = filteredTemplates.filter( + (template) => + template.title.includes(keyword) || + template.description.includes(keyword) || + template.snippets.some((snippet) => snippet.content.includes(keyword)), + ); + } + if (categoryId) { filteredTemplates = filteredTemplates.filter((template) => template.category.id.toString() === categoryId); } - if (tagId) { + if (tagIds) { filteredTemplates = filteredTemplates.filter((template) => - template.tags.some((tag) => tag.id.toString() === tagId), + template.tags.some((tag) => tagIds.split(',').includes(tag.id.toString())), ); } diff --git a/frontend/src/mocks/templateList.json b/frontend/src/mocks/templateList.json index 0c21ae984..6d0f6830e 100644 --- a/frontend/src/mocks/templateList.json +++ b/frontend/src/mocks/templateList.json @@ -4,7 +4,20 @@ "id": 1, "title": "title1", "description": "description1", - "snippets": [], + "snippets": [ + { + "id": 1, + "filename": "MyComponent.tsx", + "content": "import React from 'react';\n\ninterface MyComponentProps {\n name: string;\n age: number;\n}\n\nconst MyComponent: React.FC = ({ name, age }) => {\n return (\n
\n

Hello, {name}!

\n

You are {age} years old.

\n
\n );\n};\n\nexport default MyComponent;", + "ordinal": 1 + }, + { + "id": 2, + "filename": "App.tsx", + "content": "import React from 'react';\nimport MyComponent from './MyComponent';\n\nconst App: React.FC = () => {\n return (\n
\n \n
\n );\n};\n\nexport default App;", + "ordinal": 2 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -16,7 +29,20 @@ "id": 2, "title": "title2", "description": "description2", - "snippets": [], + "snippets": [ + { + "id": 3, + "filename": "script1.js", + "content": "console.log('Hello, world!');", + "ordinal": 1 + }, + { + "id": 4, + "filename": "script2.js", + "content": "const add = (a, b) => a + b;", + "ordinal": 2 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -28,7 +54,14 @@ "id": 3, "title": "title3", "description": "description3", - "snippets": [], + "snippets": [ + { + "id": 5, + "filename": "example.java", + "content": "public class Example { public static void main(String[] args) { System.out.println(\"Hello, World!\"); } }", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [{ "id": 1, "name": "Java" }], "modifiedAt": "2024-07-10 12:34" @@ -37,7 +70,14 @@ "id": 4, "title": "title4", "description": "description4", - "snippets": [], + "snippets": [ + { + "id": 6, + "filename": "style.css", + "content": "body { margin: 0; padding: 0; }", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 6, "name": "CSS" }, @@ -49,7 +89,14 @@ "id": 5, "title": "title5", "description": "description5", - "snippets": [], + "snippets": [ + { + "id": 7, + "filename": "program.java", + "content": "public class Program { public static void main(String[] args) { System.out.println(\"Run program\"); } }", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [{ "id": 1, "name": "Java" }], "modifiedAt": "2024-07-08 12:34" @@ -58,7 +105,20 @@ "id": 6, "title": "title6", "description": "description6", - "snippets": [], + "snippets": [ + { + "id": 8, + "filename": "Example.java", + "content": "public class Example { public static void main(String[] args) { System.out.println(\"Hello, Example!\"); } }", + "ordinal": 1 + }, + { + "id": 9, + "filename": "Sample.java", + "content": "public class Sample { public static void main(String[] args) { System.out.println(\"Sample code\"); } }", + "ordinal": 2 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 1, "name": "Java" }, @@ -70,7 +130,20 @@ "id": 7, "title": "title7", "description": "description7", - "snippets": [], + "snippets": [ + { + "id": 10, + "filename": "App.js", + "content": "console.log('App running');", + "ordinal": 1 + }, + { + "id": 11, + "filename": "Component.js", + "content": "export const Component = () => console.log('Component loaded');", + "ordinal": 2 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 4, "name": "React" }, @@ -82,7 +155,14 @@ "id": 8, "title": "title8", "description": "description8", - "snippets": [], + "snippets": [ + { + "id": 12, + "filename": "index.js", + "content": "console.log('Index file');", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [], "modifiedAt": "2024-07-05 12:34" @@ -91,7 +171,20 @@ "id": 9, "title": "title9", "description": "description9", - "snippets": [], + "snippets": [ + { + "id": 13, + "filename": "main.js", + "content": "console.log('Main file');", + "ordinal": 1 + }, + { + "id": 14, + "filename": "helper.js", + "content": "export const helper = () => console.log('Helper function');", + "ordinal": 2 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -103,7 +196,14 @@ "id": 10, "title": "title10", "description": "description10", - "snippets": [], + "snippets": [ + { + "id": 15, + "filename": "frontend.py", + "content": "print('Hello, Frontend!')", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 9, "name": "Frontend" }, @@ -115,7 +215,20 @@ "id": 11, "title": "title11", "description": "description11", - "snippets": [], + "snippets": [ + { + "id": 16, + "filename": "backend.js", + "content": "console.log('Backend logic');", + "ordinal": 1 + }, + { + "id": 17, + "filename": "database.js", + "content": "console.log('Database connection');", + "ordinal": 2 + } + ], "category": { "id": 1, "name": "" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -127,7 +240,14 @@ "id": 12, "title": "title12", "description": "description12", - "snippets": [], + "snippets": [ + { + "id": 18, + "filename": "example1.js", + "content": "console.log('Example snippet');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [], "modifiedAt": "2024-07-01 12:34" @@ -136,7 +256,20 @@ "id": 13, "title": "title13", "description": "description13", - "snippets": [], + "snippets": [ + { + "id": 19, + "filename": "oop.js", + "content": "class OOPExample { constructor() { console.log('OOP Example'); } }", + "ordinal": 1 + }, + { + "id": 20, + "filename": "styling.js", + "content": "const style = { color: 'blue' };", + "ordinal": 2 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 7, "name": "Styling" }, @@ -148,7 +281,14 @@ "id": 14, "title": "title14", "description": "description14", - "snippets": [], + "snippets": [ + { + "id": 21, + "filename": "hello.java", + "content": "public class Hello { public static void main(String[] args) { System.out.println(\"Hello\"); } }", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [{ "id": 1, "name": "Java" }], "modifiedAt": "2024-07-10 12:34" @@ -157,7 +297,20 @@ "id": 15, "title": "title15", "description": "description15", - "snippets": [], + "snippets": [ + { + "id": 22, + "filename": "example.py", + "content": "print('Example snippet')", + "ordinal": 1 + }, + { + "id": 23, + "filename": "sample.py", + "content": "print('Sample code')", + "ordinal": 2 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 10, "name": "Python" }, @@ -169,7 +322,14 @@ "id": 16, "title": "title16", "description": "description16", - "snippets": [], + "snippets": [ + { + "id": 24, + "filename": "backend.java", + "content": "public class Backend { public static void main(String[] args) { System.out.println(\"Backend\"); } }", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -181,7 +341,14 @@ "id": 17, "title": "title17", "description": "description17", - "snippets": [], + "snippets": [ + { + "id": 25, + "filename": "react.js", + "content": "import React from 'react';", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 4, "name": "React" }, @@ -193,7 +360,20 @@ "id": 18, "title": "title18", "description": "description18", - "snippets": [], + "snippets": [ + { + "id": 26, + "filename": "oop.js", + "content": "class OOP { constructor() { console.log('OOP'); } }", + "ordinal": 1 + }, + { + "id": 27, + "filename": "example.js", + "content": "console.log('Example');", + "ordinal": 2 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 7, "name": "Styling" }, @@ -206,7 +386,14 @@ "id": 19, "title": "title19", "description": "description19", - "snippets": [], + "snippets": [ + { + "id": 28, + "filename": "java_snippet.java", + "content": "public class JavaSnippet { public static void main(String[] args) { System.out.println(\"Java Snippet\"); } }", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [{ "id": 1, "name": "Java" }], "modifiedAt": "2024-07-05 12:34" @@ -215,7 +402,14 @@ "id": 20, "title": "title20", "description": "description20", - "snippets": [], + "snippets": [ + { + "id": 29, + "filename": "python_example.py", + "content": "print('Python example')", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 10, "name": "Python" }, @@ -227,7 +421,14 @@ "id": 21, "title": "title21", "description": "description21", - "snippets": [], + "snippets": [ + { + "id": 30, + "filename": "log.js", + "content": "console.log('Log snippet');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [], "modifiedAt": "2024-07-03 12:34" @@ -236,7 +437,14 @@ "id": 22, "title": "title22", "description": "description22", - "snippets": [], + "snippets": [ + { + "id": 31, + "filename": "react_component.js", + "content": "import React from 'react';\n\nconst Component = () =>
React Component
;\n\nexport default Component;", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 4, "name": "React" }, @@ -248,7 +456,14 @@ "id": 23, "title": "title23", "description": "description23", - "snippets": [], + "snippets": [ + { + "id": 32, + "filename": "snippet.js", + "content": "console.log('Snippet');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [], "modifiedAt": "2024-07-01 12:34" @@ -257,7 +472,14 @@ "id": 24, "title": "title24", "description": "description24", - "snippets": [], + "snippets": [ + { + "id": 33, + "filename": "frontend_snippet.js", + "content": "console.log('Frontend snippet');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 1, "name": "Java" }, @@ -269,7 +491,14 @@ "id": 25, "title": "title25", "description": "description25", - "snippets": [], + "snippets": [ + { + "id": 34, + "filename": "python_code.py", + "content": "print('Python code')", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 10, "name": "Python" }, @@ -281,7 +510,14 @@ "id": 26, "title": "title26", "description": "description26", - "snippets": [], + "snippets": [ + { + "id": 35, + "filename": "backend_code.js", + "content": "console.log('Backend code');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -293,7 +529,14 @@ "id": 27, "title": "title27", "description": "description27", - "snippets": [], + "snippets": [ + { + "id": 36, + "filename": "react_code.js", + "content": "console.log('React code');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 4, "name": "React" }, @@ -305,7 +548,14 @@ "id": 28, "title": "title28", "description": "description28", - "snippets": [], + "snippets": [ + { + "id": 37, + "filename": "style_snippet.js", + "content": "console.log('Style snippet');", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [{ "id": 7, "name": "Styling" }], "modifiedAt": "2024-07-07 12:34" @@ -314,7 +564,14 @@ "id": 29, "title": "title29", "description": "description29", - "snippets": [], + "snippets": [ + { + "id": 38, + "filename": "frontend_code.js", + "content": "console.log('Frontend code');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 1, "name": "Java" }, @@ -326,7 +583,14 @@ "id": 30, "title": "title30", "description": "description30", - "snippets": [], + "snippets": [ + { + "id": 39, + "filename": "python_example2.py", + "content": "print('Another Python example')", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [{ "id": 10, "name": "Python" }], "modifiedAt": "2024-07-05 12:34" @@ -335,7 +599,14 @@ "id": 31, "title": "title31", "description": "description31", - "snippets": [], + "snippets": [ + { + "id": 40, + "filename": "javascript_example.js", + "content": "console.log('JavaScript example');", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [{ "id": 3, "name": "JavaScript" }], "modifiedAt": "2024-07-04 12:34" @@ -344,7 +615,14 @@ "id": 32, "title": "title32", "description": "description32", - "snippets": [], + "snippets": [ + { + "id": 41, + "filename": "react_component2.js", + "content": "const Component = () =>
Another React Component
;\n\nexport default Component;", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [ { "id": 4, "name": "React" }, @@ -356,7 +634,14 @@ "id": 33, "title": "title33", "description": "description33", - "snippets": [], + "snippets": [ + { + "id": 42, + "filename": "empty_snippet.js", + "content": "console.log('Empty snippet');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [], "modifiedAt": "2024-07-02 12:34" @@ -365,7 +650,14 @@ "id": 34, "title": "title34", "description": "description34", - "snippets": [], + "snippets": [ + { + "id": 43, + "filename": "frontend_snippet2.js", + "content": "console.log('Another frontend snippet');", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 1, "name": "Java" }, @@ -377,7 +669,14 @@ "id": 35, "title": "title35", "description": "description35", - "snippets": [], + "snippets": [ + { + "id": 44, + "filename": "python_code2.py", + "content": "print('Another Python code')", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [{ "id": 10, "name": "Python" }], "modifiedAt": "2024-07-11 12:34" @@ -386,7 +685,14 @@ "id": 36, "title": "title36", "description": "description36", - "snippets": [], + "snippets": [ + { + "id": 45, + "filename": "javascript_code.js", + "content": "console.log('JavaScript code');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -398,7 +704,20 @@ "id": 37, "title": "title37", "description": "description37", - "snippets": [], + "snippets": [ + { + "id": 46, + "filename": "react_styling.js", + "content": "console.log('React styling');", + "ordinal": 1 + }, + { + "id": 47, + "filename": "css_styling.js", + "content": "console.log('CSS styling');", + "ordinal": 2 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 4, "name": "React" }, @@ -411,7 +730,14 @@ "id": 38, "title": "title38", "description": "description38", - "snippets": [], + "snippets": [ + { + "id": 48, + "filename": "style_example.js", + "content": "console.log('Style example');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [{ "id": 7, "name": "Styling" }], "modifiedAt": "2024-07-08 12:34" @@ -420,7 +746,14 @@ "id": 39, "title": "title39", "description": "description39", - "snippets": [], + "snippets": [ + { + "id": 49, + "filename": "empty.js", + "content": "console.log('Empty file');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [], "modifiedAt": "2024-07-07 12:34" @@ -429,7 +762,14 @@ "id": 40, "title": "title40", "description": "description40", - "snippets": [], + "snippets": [ + { + "id": 50, + "filename": "python_log.py", + "content": "print('Python log')", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 10, "name": "Python" }, @@ -441,7 +781,14 @@ "id": 41, "title": "title41", "description": "description41", - "snippets": [], + "snippets": [ + { + "id": 51, + "filename": "empty_file.js", + "content": "console.log('Empty file content');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [], "modifiedAt": "2024-07-05 12:34" @@ -450,7 +797,14 @@ "id": 42, "title": "title42", "description": "description42", - "snippets": [], + "snippets": [ + { + "id": 52, + "filename": "react_css.js", + "content": "console.log('React and CSS');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 4, "name": "React" }, @@ -462,7 +816,14 @@ "id": 43, "title": "title43", "description": "description43", - "snippets": [], + "snippets": [ + { + "id": 53, + "filename": "oop_snippet.js", + "content": "class OOPSnippet { constructor() { console.log('OOP Snippet'); } }", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 7, "name": "Styling" }, @@ -474,7 +835,14 @@ "id": 44, "title": "title44", "description": "description44", - "snippets": [], + "snippets": [ + { + "id": 54, + "filename": "log_file.js", + "content": "console.log('Log file');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [], "modifiedAt": "2024-07-02 12:34" @@ -483,7 +851,14 @@ "id": 45, "title": "title45", "description": "description45", - "snippets": [], + "snippets": [ + { + "id": 55, + "filename": "python_log2.py", + "content": "print('Another Python log')", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [{ "id": 10, "name": "Python" }], "modifiedAt": "2024-07-01 12:34" @@ -492,7 +867,14 @@ "id": 46, "title": "title46", "description": "description46", - "snippets": [], + "snippets": [ + { + "id": 56, + "filename": "backend_snippet.js", + "content": "console.log('Backend snippet');", + "ordinal": 1 + } + ], "category": { "id": 3, "name": "CategoryID3" }, "tags": [ { "id": 3, "name": "JavaScript" }, @@ -505,7 +887,14 @@ "id": 47, "title": "title47", "description": "description47", - "snippets": [], + "snippets": [ + { + "id": 57, + "filename": "react_snippet.js", + "content": "console.log('React snippet');", + "ordinal": 1 + } + ], "category": { "id": 4, "name": "CategoryID4" }, "tags": [{ "id": 4, "name": "React" }], "modifiedAt": "2024-07-10 12:34" @@ -514,7 +903,14 @@ "id": 48, "title": "title48", "description": "description48", - "snippets": [], + "snippets": [ + { + "id": 58, + "filename": "styling_snippet.js", + "content": "console.log('Styling snippet');", + "ordinal": 1 + } + ], "category": { "id": 2, "name": "CategoryID2" }, "tags": [ { "id": 7, "name": "Styling" }, diff --git a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx index e45bd79c6..7de4a12e6 100644 --- a/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx +++ b/frontend/src/pages/MyTemplatesPage/MyTemplatePage.tsx @@ -2,7 +2,8 @@ import { useState, useCallback } from 'react'; import { searchIcon } from '@/assets/images'; import { CategoryMenu, Flex, Heading, Input, TemplateGrid, PagingButton } from '@/components'; -import { useWindowWidth } from '@/hooks'; +import { useWindowWidth, useDebounce } from '@/hooks'; +import { useInput } from '@/hooks/utils/useInput'; import { useCategoryListQuery } from '@/queries/category'; import { useTemplateListQuery } from '@/queries/template'; import { theme } from '@/style/theme'; @@ -15,12 +16,14 @@ const MyTemplatePage = () => { const windowWidth = useWindowWidth(); const [selectedCategoryId, setSelectedCategoryId] = useState(undefined); const [page, setPage] = useState(1); - const [pageSize] = useState(20); + const [keyword, handleKeywordChange] = useInput(''); + + const debouncedKeyword = useDebounce(keyword, 300); const { data: templateData } = useTemplateListQuery({ categoryId: selectedCategoryId, page, - pageSize, + keyword: debouncedKeyword, }); const { data: categoryData } = useCategoryListQuery(); @@ -30,6 +33,12 @@ const MyTemplatePage = () => { setPage(1); }, []); + const handleSearchSubmit = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + setPage(1); + } + }; + const templates = templateData?.templates || []; const categories = categoryData?.categories || []; const totalPages = templateData?.totalPages || 0; @@ -60,7 +69,12 @@ const MyTemplatePage = () => { - + diff --git a/frontend/src/queries/template/useTemplateListQuery.ts b/frontend/src/queries/template/useTemplateListQuery.ts index 375b4f4b1..4bb43b780 100644 --- a/frontend/src/queries/template/useTemplateListQuery.ts +++ b/frontend/src/queries/template/useTemplateListQuery.ts @@ -1,18 +1,19 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query'; -import { QUERY_KEY, getTemplateList } from '@/api'; +import { PAGE_SIZE, QUERY_KEY, getTemplateList } from '@/api'; import type { TemplateListResponse } from '@/types'; interface Props { categoryId?: number; - tagId?: number; + tagIds?: number[]; page?: number; pageSize?: number; + keyword?: string; } -export const useTemplateListQuery = ({ categoryId, tagId, page = 1, pageSize = 20 }: Props) => +export const useTemplateListQuery = ({ categoryId, tagIds, page = 1, pageSize = PAGE_SIZE, keyword }: Props) => useQuery({ - queryKey: [QUERY_KEY.TEMPLATE_LIST, categoryId, tagId, page, pageSize], - queryFn: () => getTemplateList(categoryId, tagId, page, pageSize), + queryKey: [QUERY_KEY.TEMPLATE_LIST, categoryId, tagIds, page, pageSize, keyword], + queryFn: () => getTemplateList({ categoryId, tagIds, page, pageSize, keyword }), placeholderData: keepPreviousData, }); diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index 5469b0d8b..b4aa7e888 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -32,3 +32,11 @@ export interface CategoryListResponse { export interface CategoryRequest { name: string; } + +export interface TemplateListRequest { + categoryId?: number; + tagIds?: number[]; + page?: number; + pageSize?: number; + keyword?: string; +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 166f1895f..20297ed4f 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -3,6 +3,7 @@ export type { TemplateEditRequest, TemplateListResponse, TemplateUploadRequest, + TemplateListRequest, CategoryRequest, CategoryListResponse, } from './api';