From 76c229599f887b923680a3fb106781470ad793a3 Mon Sep 17 00:00:00 2001 From: dorimu0 <121004915+dorimu0@users.noreply.github.com> Date: Wed, 24 Apr 2024 01:00:48 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20feat=20:=20Add=20class=20user?= =?UTF-8?q?=20info=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add API to get class user information via cid passed as a query - Modify login state management logic - Add permission enum for class user Related issue: #104 --- src/api/apiUtils.ts | 1 + src/api/classUser/getClassInfo.ts | 10 +++ src/app/intro/googleLogin/page.tsx | 1 - src/app/layout.tsx | 15 ++-- src/components/navbar/Navbar.tsx | 12 ++- src/components/navbar/profile/Profile.tsx | 97 ++++++++++++++--------- src/components/protect/Protect.tsx | 20 ----- src/components/protect/index.ts | 3 - src/constants/roles.ts | 10 +++ src/interfaces/user/ClassUser.ts | 8 ++ src/interfaces/user/index.ts | 3 +- src/recoil/atoms/classUserState.ts | 13 +++ src/recoil/atoms/userState.ts | 8 +- 13 files changed, 121 insertions(+), 80 deletions(-) create mode 100644 src/api/classUser/getClassInfo.ts delete mode 100644 src/components/protect/Protect.tsx delete mode 100644 src/components/protect/index.ts create mode 100644 src/constants/roles.ts create mode 100644 src/interfaces/user/ClassUser.ts create mode 100644 src/recoil/atoms/classUserState.ts diff --git a/src/api/apiUtils.ts b/src/api/apiUtils.ts index 661873e..c463a17 100644 --- a/src/api/apiUtils.ts +++ b/src/api/apiUtils.ts @@ -40,6 +40,7 @@ api.interceptors.response.use( } } catch (error) { console.error('トークンの有効期限が切れました。'); + window.location.href = '/intro'; } } return Promise.reject(error); diff --git a/src/api/classUser/getClassInfo.ts b/src/api/classUser/getClassInfo.ts new file mode 100644 index 0000000..e9248bd --- /dev/null +++ b/src/api/classUser/getClassInfo.ts @@ -0,0 +1,10 @@ +import req from '../apiUtils'; + +const getClassInfo = async (uId: number, cId: number) => { + const response = await req(`/cu/${uId}/${cId}/info`, 'get', 'gin'); + console.log(response); + + return response.data; +}; + +export default getClassInfo; diff --git a/src/app/intro/googleLogin/page.tsx b/src/app/intro/googleLogin/page.tsx index c88bdf3..ab1761a 100644 --- a/src/app/intro/googleLogin/page.tsx +++ b/src/app/intro/googleLogin/page.tsx @@ -17,7 +17,6 @@ const Page = () => { const setRefreshToken = useSetRecoilState(refreshTokenState); const router = useRouter(); useEffect(() => { - console.log(code); if (code) { postGoogleLogin(code).then(res => { if (res.user) { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ebff2b8..5e5b4d7 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,7 +2,6 @@ import type {Metadata} from 'next'; import {Inter} from 'next/font/google'; import {Navbar} from '../components/navbar'; import {Footer} from '../components/footer'; -import Protect from '../components/protect'; import RecoilRootContainer from '../components/RecoilRootContainer'; import './globals.css'; import '@/src/styles/variable.css'; @@ -19,15 +18,13 @@ const RootLayout = ({children}: {children: React.ReactNode}) => { - -
- -
-
{children}
-
-
+
+ +
+
{children}
+
- +
diff --git a/src/components/navbar/Navbar.tsx b/src/components/navbar/Navbar.tsx index 9805cf0..a3b4e6f 100644 --- a/src/components/navbar/Navbar.tsx +++ b/src/components/navbar/Navbar.tsx @@ -2,17 +2,13 @@ import {useState} from 'react'; import Image from 'next/image'; import Link from 'next/link'; -import {useParams, usePathname} from 'next/navigation'; -import {useRecoilValue} from 'recoil'; -import userState from '@/src/recoil/atoms/userState'; +import {useParams, usePathname, useSearchParams} from 'next/navigation'; import {MaterialContainer, MaterialForm} from './material'; import Profile from './profile'; -import {User} from '@/src/interfaces/user'; import icons from '@/public/svgs/navbar'; import '@/src/styles/variable.css'; const Navbar = () => { - const user = useRecoilValue(userState) as User; const [isOpen, setIsOpen] = useState(false); const pages = [ @@ -24,6 +20,8 @@ const Navbar = () => { const router = usePathname(); const params = useParams<{className: string; materialName: string}>(); + const searchParams = useSearchParams(); + const search = searchParams.get('id'); if (router === '/intro' || router === '/intro/googleLogin') { return null; @@ -33,7 +31,7 @@ const Navbar = () => {
{/* Profile */} - +
@@ -82,7 +80,7 @@ const Navbar = () => {
{isOpen ? : null}
- +
diff --git a/src/components/navbar/profile/Profile.tsx b/src/components/navbar/profile/Profile.tsx index 0e4f341..cf370a0 100644 --- a/src/components/navbar/profile/Profile.tsx +++ b/src/components/navbar/profile/Profile.tsx @@ -1,20 +1,37 @@ -import {useState} from 'react'; +import {useEffect, useState} from 'react'; import Image from 'next/image'; import {useRouter} from 'next/navigation'; +import {useRecoilState, useRecoilValue} from 'recoil'; import EditName from './EditName'; import Warning from '../../warning/Warning'; +import getClassInfo from '@/src/api/classUser/getClassInfo'; import putUserName from '@/src/api/classUser/putUserName'; +import classUserState from '@/src/recoil/atoms/classUserState'; +import userState from '@/src/recoil/atoms/userState'; import {ParamsProps} from '@/src/interfaces/navbar'; -import {User} from '@/src/interfaces/user'; -import ClassUser from '@/src/model/User'; import icons from '@/public/svgs/navbar'; +import ROLES from '@/src/constants/roles'; -const Profile = ({user, params}: {user: User; params: ParamsProps}) => { +const Profile = ({cId, params}: {cId: string | null; params: ParamsProps}) => { const router = useRouter(); + const user = useRecoilValue(userState); + const [classUser, setClassUser] = useRecoilState(classUserState); const [dropdownOpen, setDropdownOpen] = useState(false); const [isEditOpen, setIsEditOpen] = useState(false); const [isOpen, setIsOpen] = useState(false); + useEffect(() => { + if (cId && !classUser) { + getClassInfo(user.id, parseInt(cId)).then(res => { + console.log(res); + setClassUser(res); + }); + } + if (!params.className) { + setClassUser(null); + } + }, [cId, classUser, params]); + const toggleDropdown = () => { setDropdownOpen(!dropdownOpen); console.log(params); @@ -22,7 +39,7 @@ const Profile = ({user, params}: {user: User; params: ParamsProps}) => { const handleEditName = (name: string) => { setDropdownOpen(false); - putUserName(user.id, 4, name); + if (classUser) putUserName(classUser.uid, 4, name); }; const handleClickDelete = () => { @@ -38,14 +55,16 @@ const Profile = ({user, params}: {user: User; params: ParamsProps}) => {
userImage -
{user.name}
+
+ {classUser ? classUser.nickname : user ? user.name : ''} +
{dropdownOpen && (
    -
  • { - setIsEditOpen(true); - setDropdownOpen(false); - }} - > - Edit Name -
  • - {ClassUser.managerRoll === 'manager' ? ( -
  • { - setIsOpen(true); - setDropdownOpen(false); - }} - > - Edit group -
  • + {classUser ? ( + <> +
  • { + setIsEditOpen(true); + setDropdownOpen(false); + }} + > + Edit Name +
  • + {ROLES[classUser.role_id] === 'ADMIN' ? ( +
  • { + setIsOpen(true); + setDropdownOpen(false); + }} + > + Edit group +
  • + ) : null} +
  • { + setIsOpen(true); + setDropdownOpen(false); + }} + > + {ROLES[classUser.role_id] === 'ADMIN' + ? 'Delete group' + : 'Leave group'} +
  • + ) : null} -
  • { - setIsOpen(true); - setDropdownOpen(false); - }} - > - {ClassUser.managerRoll === 'manager' - ? 'Delete group' - : 'Leave group'} -
  • Logout
  • diff --git a/src/components/protect/Protect.tsx b/src/components/protect/Protect.tsx deleted file mode 100644 index a7a7516..0000000 --- a/src/components/protect/Protect.tsx +++ /dev/null @@ -1,20 +0,0 @@ -'use client'; -import {ReactNode, useEffect} from 'react'; -import {useRouter} from 'next/navigation'; -import {useRecoilValue} from 'recoil'; -import userState from '@/src/recoil/atoms/userState'; - -const Protect = ({children}: {children: ReactNode}) => { - const user = useRecoilValue(userState); - const router = useRouter(); - - useEffect(() => { - if (!user) { - router.push('/intro'); - } - }, [user, router]); - - return children; -}; - -export default Protect; diff --git a/src/components/protect/index.ts b/src/components/protect/index.ts deleted file mode 100644 index 601a5c7..0000000 --- a/src/components/protect/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Protect from './Protect'; - -export default Protect; diff --git a/src/constants/roles.ts b/src/constants/roles.ts new file mode 100644 index 0000000..55e4b06 --- /dev/null +++ b/src/constants/roles.ts @@ -0,0 +1,10 @@ +const ROLES: {[key: number]: string} = { + 1: 'USER', + 2: 'ADMIN', + 3: 'ASSISTANT', + 4: 'APPLICANT', + 5: 'BLACKLIST', + 6: 'INVITE', +}; + +export default ROLES; diff --git a/src/interfaces/user/ClassUser.ts b/src/interfaces/user/ClassUser.ts new file mode 100644 index 0000000..12fa115 --- /dev/null +++ b/src/interfaces/user/ClassUser.ts @@ -0,0 +1,8 @@ +interface ClassUser { + uid: number; + nickname: string; + role_id: number; + image: string; +} + +export default ClassUser; diff --git a/src/interfaces/user/index.ts b/src/interfaces/user/index.ts index 0b514a2..ad8a1c6 100644 --- a/src/interfaces/user/index.ts +++ b/src/interfaces/user/index.ts @@ -2,5 +2,6 @@ import User from './User'; import LoginData from './LoginData'; import AccessToken from './AccessToken'; import RefreshToken from './RefreshToken'; +import ClassUser from './ClassUser'; -export type {User, LoginData, AccessToken, RefreshToken}; +export type {User, LoginData, AccessToken, RefreshToken, ClassUser}; diff --git a/src/recoil/atoms/classUserState.ts b/src/recoil/atoms/classUserState.ts new file mode 100644 index 0000000..fc7c928 --- /dev/null +++ b/src/recoil/atoms/classUserState.ts @@ -0,0 +1,13 @@ +import {atom} from 'recoil'; +import {ClassUser} from '@/src/interfaces/user'; +import {recoilPersist} from 'recoil-persist'; + +const {persistAtom} = recoilPersist(); + +const classUserState = atom({ + key: 'classUserState', + default: null, + effects_UNSTABLE: [persistAtom], +}); + +export default classUserState; diff --git a/src/recoil/atoms/userState.ts b/src/recoil/atoms/userState.ts index 77ccac8..a71d3be 100644 --- a/src/recoil/atoms/userState.ts +++ b/src/recoil/atoms/userState.ts @@ -4,9 +4,13 @@ import {recoilPersist} from 'recoil-persist'; const {persistAtom} = recoilPersist(); -const userState = atom({ +const userState = atom({ key: 'userState', - default: null, + default: { + id: 0, + name: '', + image: '', + }, effects_UNSTABLE: [persistAtom], }); From c1695d5a52dbdd346c62afc7c02502d6bd20b542 Mon Sep 17 00:00:00 2001 From: dorimu0 <121004915+dorimu0@users.noreply.github.com> Date: Wed, 24 Apr 2024 01:13:07 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20:=20Modify?= =?UTF-8?q?=20API=20requests=20based=20on=20classId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Modify API requests based on the specified class user on prompt and material pages. - Fix to get locally stored token in postPrompt function Related issue: #104 --- src/api/prompts/postPrompt.ts | 7 ++++--- .../components/subComponents/PromptChat.tsx | 7 +++---- .../components/subComponents/Storage.tsx | 2 +- .../navbar/material/MaterialContainer.tsx | 18 ++++++++++++++---- .../navbar/material/MaterialForm.tsx | 12 +++++++++--- .../navbar/material/MaterialList.tsx | 12 +++++++++--- 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/api/prompts/postPrompt.ts b/src/api/prompts/postPrompt.ts index 305af6b..c4cf3e3 100644 --- a/src/api/prompts/postPrompt.ts +++ b/src/api/prompts/postPrompt.ts @@ -4,18 +4,19 @@ const postPrompt = async ( message: string, chat: (reader: ReadableStreamDefaultReader) => void ) => { + const token = localStorage.getItem('access_token'); + console.log('token:', token); const body = { message: message, }; + console.log('body:', body); try { const response = await fetch( `http://3.38.86.236:3000/api/nest/class/${cId}/prompts/${id}`, { method: 'POST', headers: { - Authorization: - 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiZXhwIjoxOTY5OTAxMDIyfQ.U0k1q2oTrp3JwsIpem16o2W77tpVGiwylwc5cTFaZgU', - 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, }, body: JSON.stringify(body), } diff --git a/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx b/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx index 5c5d9b4..7308032 100644 --- a/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx +++ b/src/app/[className]/[materialName]/components/subComponents/PromptChat.tsx @@ -16,14 +16,14 @@ const PromptChat = () => { const [reload, setReload] = useState(false); useEffect(() => { - getPrompt(1, 1, 1, 6).then(res => { + getPrompt(4, 126, 1, 6).then(res => { res.messages.reverse(); setMsg(res.messages); }); }, [reload]); const handleClickIcon = (mId: number) => { - patchMessage(1, 1, mId, true).then(res => { + patchMessage(4, 126, mId, true).then(res => { console.log(res); }); }; @@ -56,8 +56,7 @@ const PromptChat = () => { useEffect(() => { if (inputMsg === '') return; - postPrompt(1, 1, inputMsg, chat).then(res => { - console.log(res); + postPrompt(4, 126, inputMsg, chat).then(() => { setInputMsg(''); }); }, [inputMsg]); diff --git a/src/app/[className]/[materialName]/components/subComponents/Storage.tsx b/src/app/[className]/[materialName]/components/subComponents/Storage.tsx index cec3ae7..c2c97b5 100644 --- a/src/app/[className]/[materialName]/components/subComponents/Storage.tsx +++ b/src/app/[className]/[materialName]/components/subComponents/Storage.tsx @@ -14,7 +14,7 @@ const Storage = () => { useEffect(() => { // コメントを取得する処理 - getMessage(1, 1, 1, 5).then(res => { + getMessage(4, 126, 1, 5).then(res => { console.log(res); setMsg(res); setIsOpen(new Array(res.length).fill(false)); // コメントの開閉状態を初期化 diff --git a/src/components/navbar/material/MaterialContainer.tsx b/src/components/navbar/material/MaterialContainer.tsx index e73467a..f7dce35 100644 --- a/src/components/navbar/material/MaterialContainer.tsx +++ b/src/components/navbar/material/MaterialContainer.tsx @@ -7,7 +7,13 @@ import searchMaterial from '@/src/api/material/searchMaterial'; import {Material, ParamsProps} from '@/src/interfaces/navbar'; import icons from '@/public/svgs/navbar'; -const MaterialContainer = ({params}: {params: ParamsProps}) => { +const MaterialContainer = ({ + params, + cId, +}: { + params: ParamsProps; + cId: string | null; +}) => { const [materials, setMaterials] = useState([]); const [searchMaterials, setSearchMaterials] = useState([]); const [keyWord, setKeyWord] = useState(''); @@ -16,7 +22,7 @@ const MaterialContainer = ({params}: {params: ParamsProps}) => { const onLoadMore = () => { setHasMore(false); - getMaterial(1, boardPage, 8).then(res => { + getMaterial(4, boardPage, 8).then(res => { if (res.length === 0) { setHasMore(false); } else { @@ -85,9 +91,13 @@ const MaterialContainer = ({params}: {params: ParamsProps}) => { > {materials ? ( keyWord ? ( - + ) : ( - + ) ) : null} diff --git a/src/components/navbar/material/MaterialForm.tsx b/src/components/navbar/material/MaterialForm.tsx index ebb9ffa..499cbd5 100644 --- a/src/components/navbar/material/MaterialForm.tsx +++ b/src/components/navbar/material/MaterialForm.tsx @@ -1,14 +1,20 @@ -import {ChangeEvent, useRef, useState} from 'react'; +import {ChangeEvent, useEffect, useRef, useState} from 'react'; import Image from 'next/image'; import postMaterial from '@/src/api/material/postMaterial'; import {FormProps} from '@/src/interfaces/navbar'; import icons from '@/public/svgs/navbar/prompt'; -const MaterialForm = ({setIsOpen}: FormProps) => { +const MaterialForm = ({setIsOpen, editData}: FormProps) => { const inputRef = useRef(null); const [materialName, setMaterialName] = useState(''); const [material, setMaterial] = useState(); + useEffect(() => { + if (editData) { + console.log(editData); + } + }, []); + const handleEnterName = (e: ChangeEvent) => { setMaterialName(e.target.value); }; @@ -28,7 +34,7 @@ const MaterialForm = ({setIsOpen}: FormProps) => { const handleClickButton = () => { console.log(material, materialName); if (material && materialName) { - postMaterial(1, materialName, material); + postMaterial(4, materialName, material); } }; diff --git a/src/components/navbar/material/MaterialList.tsx b/src/components/navbar/material/MaterialList.tsx index c8fc3cb..4922cc7 100644 --- a/src/components/navbar/material/MaterialList.tsx +++ b/src/components/navbar/material/MaterialList.tsx @@ -10,16 +10,19 @@ import icons from '@/public/svgs/navbar'; const MaterialList = ({ materials, params, + cId, }: { materials: Material[]; params: ParamsProps; + cId: string | null; }) => { const [isToggleOpen, setIsToggleOpen] = useState([]); const [isOpen, setIsOpen] = useState(false); + const [editData, setEditData] = useState(); const handleClickSubject = (mId: number) => { - if (materials[mId] && materials[mId].prompts.length === 0) { - postPromptAccess(1, mId); + if (materials[mId] && materials[mId].prompts.length === 0 && cId) { + postPromptAccess(parseInt(cId), mId); } }; @@ -70,6 +73,7 @@ const MaterialList = ({
    { + setEditData(material); setIsOpen(true); setIsToggleOpen(prev => prev.map(() => false)); }} @@ -89,7 +93,9 @@ const MaterialList = ({ ); })} - {isOpen ? : null} + {isOpen ? ( + + ) : null}
);