diff --git a/__tests__/api/prompt/getMessage.test.ts b/__tests__/api/prompt/getMessage.test.ts new file mode 100644 index 0000000..2c07e76 --- /dev/null +++ b/__tests__/api/prompt/getMessage.test.ts @@ -0,0 +1,37 @@ +import getMessage from '@/src/api/prompts/getMessage'; +import {StorageMessage} from '@/src/interfaces/prompt'; +import req from '@/src/api/apiUtils'; + +jest.mock('@/src/api/apiUtils'); + +describe('getMessage function', () => { + it('should return messages', async () => { + const mockMessages: StorageMessage[] = [ + { + id: '1', + answer: 'Test answer 1', + question: 'Test question 1', + }, + { + id: '2', + answer: 'Test answer 2', + question: 'Test question 2', + }, + ]; + + (req as jest.Mock).mockResolvedValue({ + data: { + messages: mockMessages, + }, + }); + + const result = await getMessage(1, 1, 1, 2); + expect(result).toEqual(mockMessages); + }); + + it('should throw an error if the request fails', async () => { + (req as jest.Mock).mockRejectedValue(new Error('Request failed')); + + await expect(getMessage(1, 1, 1, 2)).rejects.toThrow('Request failed'); + }); +}); diff --git a/__tests__/api/prompt/getPrompt.test.ts b/__tests__/api/prompt/getPrompt.test.ts new file mode 100644 index 0000000..66894ea --- /dev/null +++ b/__tests__/api/prompt/getPrompt.test.ts @@ -0,0 +1,20 @@ +import getPrompt from '@/src/api/prompts/getPrompt'; +import req from '@/src/api/apiUtils'; + +jest.mock('@/src/api/apiUtils'); + +describe('getPrompt', () => { + it('should call the correct endpoint and return data', async () => { + const mockData = {message: 'test'}; + (req as jest.Mock).mockResolvedValue({data: mockData}); + + const result = await getPrompt(1, 2, 3, 4); + + expect(req).toHaveBeenCalledWith( + '/class/1/prompts/2?page=3&limit=4', + 'get', + 'nest' + ); + expect(result).toEqual(mockData); + }); +}); diff --git a/__tests__/api/prompt/patchMessage.test.ts b/__tests__/api/prompt/patchMessage.test.ts new file mode 100644 index 0000000..7db6532 --- /dev/null +++ b/__tests__/api/prompt/patchMessage.test.ts @@ -0,0 +1,20 @@ +import patchMessage from '@/src/api/prompts/patchMessage'; +import req from '@/src/api/apiUtils'; + +jest.mock('@/src/api/apiUtils'); + +describe('patchMessage', () => { + it('should call the correct endpoint and return response', async () => { + const mockResponse = {status: 200}; + (req as jest.Mock).mockResolvedValue(mockResponse); + + const result = await patchMessage(1, 2, 3, true); + + expect(req).toHaveBeenCalledWith( + '/class/1/prompts/2/messages/3?is_save=true', + 'patch', + 'nest' + ); + expect(result).toEqual(mockResponse); + }); +}); diff --git a/__tests__/api/prompt/postPrompt.test.ts b/__tests__/api/prompt/postPrompt.test.ts new file mode 100644 index 0000000..fd2ebb8 --- /dev/null +++ b/__tests__/api/prompt/postPrompt.test.ts @@ -0,0 +1,19 @@ +import postPrompt from '@/src/api/prompts/postPrompt'; +import req from '@/src/api/apiUtils'; + +jest.mock('@/src/api/apiUtils'); + +describe('postPrompt', () => { + it('should call the correct endpoint with the correct body and return response', async () => { + const mockResponse = {status: 200}; + (req as jest.Mock).mockResolvedValue(mockResponse); + + const message = 'test message'; + const result = await postPrompt(1, 2, message); + + expect(req).toHaveBeenCalledWith('/class/1/prompts/2', 'post', 'nest', { + message, + }); + expect(result).toEqual(mockResponse); + }); +}); diff --git a/public/svgs/prompt/delete.svg b/public/svgs/prompt/delete.svg new file mode 100644 index 0000000..c9469be --- /dev/null +++ b/public/svgs/prompt/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svgs/prompt/drive.svg b/public/svgs/prompt/drive.svg new file mode 100644 index 0000000..df6303c --- /dev/null +++ b/public/svgs/prompt/drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svgs/prompt/index.ts b/public/svgs/prompt/index.ts new file mode 100644 index 0000000..c7d81b8 --- /dev/null +++ b/public/svgs/prompt/index.ts @@ -0,0 +1,6 @@ +const icons = { + delete: '/svgs/prompt/delete.svg', + drive: '/svgs/prompt/drive.svg', +}; + +export default icons; diff --git a/src/api/prompts/getMessage.ts b/src/api/prompts/getMessage.ts new file mode 100644 index 0000000..b8cdd16 --- /dev/null +++ b/src/api/prompts/getMessage.ts @@ -0,0 +1,21 @@ +import {StorageMessage} from '@/src/interfaces/prompt'; +import req from '../apiUtils'; + +const getMessage = async ( + cId: number, + id: number, + pageNumber: number, + limitNumber: number +): Promise => { + const response = await req( + `/class/${cId}/prompts/${id}/messages/saved?page=${pageNumber}&limit=${limitNumber}`, + 'get', + 'nest' + ); + + console.log(response); + + return response.data.messages; +}; + +export default getMessage; diff --git a/src/api/prompts/getPrompt.ts b/src/api/prompts/getPrompt.ts new file mode 100644 index 0000000..33b1cdc --- /dev/null +++ b/src/api/prompts/getPrompt.ts @@ -0,0 +1,21 @@ +import req from '../apiUtils'; +import {PromptMessages} from '@/src/interfaces/prompt'; + +const getPrompt = async ( + cId: number, + id: number, + pageNumber: number, + limitNumber: number +): Promise => { + const response = await req( + `/class/${cId}/prompts/${id}?page=${pageNumber}&limit=${limitNumber}`, + 'get', + 'nest' + ); + + console.log(response); + + return response.data; +}; + +export default getPrompt; diff --git a/src/api/prompts/patchMessage.ts b/src/api/prompts/patchMessage.ts new file mode 100644 index 0000000..503d044 --- /dev/null +++ b/src/api/prompts/patchMessage.ts @@ -0,0 +1,20 @@ +import req from '../apiUtils'; + +const patchMessage = async ( + cId: number, + id: number, + mId: number, + isSave: boolean +) => { + const response = await req( + `/class/${cId}/prompts/${id}/messages/${mId}?is_save=${isSave}`, + 'patch', + 'nest' + ); + + console.log(response); + + return response; +}; + +export default patchMessage; diff --git a/src/api/prompts/postPrompt.ts b/src/api/prompts/postPrompt.ts new file mode 100644 index 0000000..07e646b --- /dev/null +++ b/src/api/prompts/postPrompt.ts @@ -0,0 +1,19 @@ +import req from '../apiUtils'; + +const postPrompt = async (cId: number, id: number, message: string) => { + const body = { + message: message, + }; + const response = await req( + `/class/${cId}/prompts/${id}`, + 'post', + 'nest', + body + ); + + console.log(response); + + return response; +}; + +export default postPrompt; diff --git a/src/app/[className]/[materialName]/components/Quiz.tsx b/src/app/[className]/[materialName]/components/Quiz.tsx new file mode 100644 index 0000000..1c947d5 --- /dev/null +++ b/src/app/[className]/[materialName]/components/Quiz.tsx @@ -0,0 +1,56 @@ +'use client'; +import {useState} from 'react'; + +const Quiz = () => { + const [isOpen, setIsOpen] = useState(false); + + const toggleDropdown = () => { + setIsOpen(!isOpen); + }; + return ( + <> +
+
Question
+
Question
+
+
+
+
+
Answer
+
+ + {isOpen ? ( +
+
+ Correct Answer: A +
+
haesul
+
+ ) : null} +
+
+
    +
  • +
    A.
    +
  • +
  • +
    B.
    +
  • +
  • +
    C.
    +
  • +
  • +
    D.
    +
  • +
+
+ + ); +}; + +export default Quiz; diff --git a/src/app/[className]/[materialName]/components/UserContainer.tsx b/src/app/[className]/[materialName]/components/UserContainer.tsx new file mode 100644 index 0000000..43f2f45 --- /dev/null +++ b/src/app/[className]/[materialName]/components/UserContainer.tsx @@ -0,0 +1,26 @@ +'use client'; +import {useState} from 'react'; +import {Dashboard, TabsMapping} from '@/src/components/dashboard'; +// import Material from './Material'; +import Quiz from './Quiz'; + +const UserContainer = () => { + const TABS = ['Material', 'Quiz']; + const [activeTab, setActiveTab] = useState(TABS[0]); + const tabMapping = { + // Material: , + Quiz: , + }; + return ( +
+ + +
+ ); +}; + +export default UserContainer; diff --git a/src/app/[className]/[materialName]/components/index.ts b/src/app/[className]/[materialName]/components/index.ts new file mode 100644 index 0000000..0cf4899 --- /dev/null +++ b/src/app/[className]/[materialName]/components/index.ts @@ -0,0 +1,5 @@ +// import ManageContainer from './ManageContainer'; +import UserContainer from './UserContainer'; + +// export {ManageContainer, UserContainer}; +export {UserContainer}; diff --git a/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx b/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx new file mode 100644 index 0000000..a520df3 --- /dev/null +++ b/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx @@ -0,0 +1,57 @@ +import {useEffect, useState} from 'react'; +import Image from 'next/image'; +import getPrompt from '@/src/api/prompts/getPrompt'; +import patchMessage from '@/src/api/prompts/patchMessage'; +import {PromptMessagesProps} from '@/src/interfaces/prompt'; +import icons from '@/public/svgs/prompt'; + +const PromptChat = () => { + const [messages, setMsg] = useState(); + + useEffect(() => { + getPrompt(1, 1, 1, 6).then(res => { + res.messages.reverse(); + setMsg(res.messages); + }); + }, []); + + const handleClickIcon = (mId: number) => { + patchMessage(1, 1, mId, true).then(res => { + console.log(res); + }); + }; + + return ( +
+ {messages?.map((message, index) => { + return ( +
+
+
+ {message.question} +
+
+
+
+ {message.answer} +
+
+ icon { + handleClickIcon(parseInt(message.id)); + }} + /> +
+
+
+ ); + })} +
+ ); +}; + +export default PromptChat; diff --git a/src/app/[className]/[materialName]/components/subComponents/Storage.tsx b/src/app/[className]/[materialName]/components/subComponents/Storage.tsx new file mode 100644 index 0000000..98eb2c8 --- /dev/null +++ b/src/app/[className]/[materialName]/components/subComponents/Storage.tsx @@ -0,0 +1,63 @@ +'use client'; +import {useEffect, useState} from 'react'; +import Image from 'next/image'; +import getMessage from '@/src/api/prompts/getMessage'; +import patchMessage from '@/src/api/prompts/patchMessage'; +import {StorageMessage} from '@/src/interfaces/prompt'; +import icons from '@/public/svgs/prompt'; + +const Storage = () => { + const [isOpen, setIsOpen] = useState([]); + const [messages, setMsg] = useState(); + + useEffect(() => { + // コメントを取得する処理 + getMessage(1, 1, 1, 5).then(res => { + console.log(res); + setMsg(res); + setIsOpen(new Array(res.length).fill(false)); // コメントの開閉状態を初期化 + }); + }, []); + + const handleClickDelete = (messageId: number) => { + // コメントを削除する処理 + patchMessage(1, 1, messageId, false).then(res => { + console.log(res); + }); + }; + + const toggleDropdown = (index: number) => { + setIsOpen(prev => prev.map((open, i) => (i === index ? !open : open))); // クリックしたコメントの開閉状態を反転 + }; + + return ( +
+ {messages?.map((message, index) => { + return ( +
+
toggleDropdown(index)} + > +
{message.question}
+
{ + handleClickDelete(parseInt(message.id)); + }} + > + icon +
+
+ {isOpen[index] ? ( +
+ {message.answer} +
+ ) : null} +
+ ); + })} +
+ ); +}; + +export default Storage; diff --git a/src/app/[className]/[materialName]/components/subComponents/SubContainer.tsx b/src/app/[className]/[materialName]/components/subComponents/SubContainer.tsx new file mode 100644 index 0000000..093de53 --- /dev/null +++ b/src/app/[className]/[materialName]/components/subComponents/SubContainer.tsx @@ -0,0 +1,52 @@ +'use client'; +import {ChangeEvent, useState} from 'react'; +import {Dashboard, TabsMapping} from '@/src/components/dashboard'; +import PromptChat from './PromptChat'; +import Storage from './Storage'; +import postPrompt from '@/src/api/prompts/postPrompt'; +import '@/src/styles/variable.css'; + +const SubContainer = () => { + const TABS = ['PromptChat', 'Storage']; + const [inputMsg, setInputMsg] = useState(''); + const [activeTab, setActiveTab] = useState(TABS[0]); + const tabMapping = { + PromptChat: , + Storage: , + }; + + const handleInputMsg = (e: ChangeEvent) => { + setInputMsg(e.target.value); + }; + + const handleClickButton = () => { + postPrompt(1, 1, inputMsg); + }; + return ( +
+ +
+ +
+
+
+ +
+ Enter +
+
+
+
+ ); +}; + +export default SubContainer; diff --git a/src/app/[className]/[materialName]/components/subComponents/index.ts b/src/app/[className]/[materialName]/components/subComponents/index.ts new file mode 100644 index 0000000..31061ad --- /dev/null +++ b/src/app/[className]/[materialName]/components/subComponents/index.ts @@ -0,0 +1,3 @@ +import SubContainer from './SubContainer'; + +export {SubContainer}; diff --git a/src/app/[className]/[materialName]/layout.tsx b/src/app/[className]/[materialName]/layout.tsx new file mode 100644 index 0000000..7311035 --- /dev/null +++ b/src/app/[className]/[materialName]/layout.tsx @@ -0,0 +1,7 @@ +export default function MaterialLayout({ + children, // will be a page or nested layout +}: { + children: React.ReactNode; +}) { + return
{children}
; +} diff --git a/src/app/[className]/[materialName]/page.tsx b/src/app/[className]/[materialName]/page.tsx new file mode 100644 index 0000000..02f894f --- /dev/null +++ b/src/app/[className]/[materialName]/page.tsx @@ -0,0 +1,33 @@ +// import {ManageContainer, UserContainer} from './components'; +import {UserContainer} from './components'; +import {SubContainer} from './components/subComponents'; +import User from '@/src/model/User'; + +const Page = () => { + return ( +
+ {User.managerRoll === 'manager' ? ( +
+
SubjectName
+ {/* */} +
+ ) : ( +
+
+
SubjectName
+ +
+ {/* 右側 */} +
+
+
+
+ +
+
+ )} +
+ ); +}; + +export default Page; diff --git a/src/interfaces/navbar/index.ts b/src/interfaces/navbar/index.ts new file mode 100644 index 0000000..0e9e447 --- /dev/null +++ b/src/interfaces/navbar/index.ts @@ -0,0 +1,5 @@ +import Material from './material'; +import UserProps from './userProps'; +import ParamsProps from './paramsProps'; + +export type {Material, UserProps, ParamsProps}; diff --git a/src/interfaces/navbar/index.tsx b/src/interfaces/navbar/index.tsx deleted file mode 100644 index d6d9f67..0000000 --- a/src/interfaces/navbar/index.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import Material from './material'; -import UserProps from './userProps'; - -export type {Material, UserProps}; diff --git a/src/interfaces/prompt/index.ts b/src/interfaces/prompt/index.ts new file mode 100644 index 0000000..b53d156 --- /dev/null +++ b/src/interfaces/prompt/index.ts @@ -0,0 +1,5 @@ +import PromptMessages from './promptMessages'; +import PromptMessagesProps from './promptMessagesProps'; +import StorageMessage from './storageMessage'; + +export type {PromptMessages, PromptMessagesProps, StorageMessage}; diff --git a/src/interfaces/prompt/promptMessages.ts b/src/interfaces/prompt/promptMessages.ts new file mode 100644 index 0000000..1195227 --- /dev/null +++ b/src/interfaces/prompt/promptMessages.ts @@ -0,0 +1,11 @@ +interface PromptMessages { + id: string; + messages: { + id: string; + answer: string; + is_save: boolean; + question: string; + }[]; +} + +export default PromptMessages; diff --git a/src/interfaces/prompt/promptMessagesProps.ts b/src/interfaces/prompt/promptMessagesProps.ts new file mode 100644 index 0000000..a60b436 --- /dev/null +++ b/src/interfaces/prompt/promptMessagesProps.ts @@ -0,0 +1,8 @@ +interface PromptMessagesProps { + id: string; + answer: string; + is_save: boolean; + question: string; +} + +export default PromptMessagesProps; diff --git a/src/interfaces/prompt/storageMessage.ts b/src/interfaces/prompt/storageMessage.ts new file mode 100644 index 0000000..61e3ef6 --- /dev/null +++ b/src/interfaces/prompt/storageMessage.ts @@ -0,0 +1,7 @@ +interface StorageMessage { + id: string; + answer: string; + question: string; +} + +export default StorageMessage; diff --git a/src/styles/variable.css b/src/styles/variable.css index bf2a3be..66f9bea 100644 --- a/src/styles/variable.css +++ b/src/styles/variable.css @@ -1,3 +1,15 @@ +.mainContainer { + min-height: calc(100% - 110px); +} + .navbar { height: calc(100% - 36px); } + +.feedbackContainer { + height: calc(100% - 116px); +} + +.subContainer { + height: calc(100% - 120px); +}