From 7e05eda259a6fa1891bcb6c3c2967b1a86d0acac Mon Sep 17 00:00:00 2001 From: Takeshi Kimata <117462761+kimatata@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:17:10 +0900 Subject: [PATCH] feat: add project owner info on project settings page --- backend/index.js | 2 + backend/routes/users/show.js | 36 +++++++++++ frontend/messages/en.json | 1 + frontend/messages/ja.json | 1 + .../[projectId]/settings/SettingsPage.tsx | 62 ++++++++++++++++++- .../projects/[projectId]/settings/page.tsx | 1 + frontend/types/settings.ts | 1 + frontend/types/user.ts | 2 +- 8 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 backend/routes/users/show.js diff --git a/backend/index.js b/backend/index.js index 5acd9ad..fe0d20c 100644 --- a/backend/index.js +++ b/backend/index.js @@ -31,10 +31,12 @@ app.use('/', indexRoute); // "users" const usersIndexRoute = require('./routes/users/index')(sequelize); +const usersShowRoute = require('./routes/users/show')(sequelize); const usersFindRoute = require('./routes/users/find')(sequelize); const signUpRoute = require('./routes/users/signup')(sequelize); const signInRoute = require('./routes/users/signin')(sequelize); app.use('/users', usersIndexRoute); +app.use('/users', usersShowRoute); app.use('/users', usersFindRoute); app.use('/users', signUpRoute); app.use('/users', signInRoute); diff --git a/backend/routes/users/show.js b/backend/routes/users/show.js new file mode 100644 index 0000000..7886717 --- /dev/null +++ b/backend/routes/users/show.js @@ -0,0 +1,36 @@ +const express = require('express'); +const router = express.Router(); +const defineUser = require('../../models/users'); +const { DataTypes } = require('sequelize'); + +module.exports = function (sequelize) { + const { verifySignedIn } = require('../../middleware/auth')(sequelize); + const User = defineUser(sequelize, DataTypes); + + router.get('/:userId', verifySignedIn, async (req, res) => { + try { + const userId = req.params.userId; + if (!userId) { + return res.status(400).json({ error: 'userId is required' }); + } + + try { + const user = await User.findByPk(userId, { + attributes: ['id', 'username', 'role', 'avatarPath'], + }); + if (!user) { + return res.status(404).send('User not found'); + } + res.json(user); + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } + }); + + return router; +}; diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 9e3c7a4..6fda770 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -296,6 +296,7 @@ "project_management": "Project Management", "project_name": "Project Name", "project_detail": "Project Detail", + "project_owner": "Project Owner", "edit_project": "Edit Project", "project": "Project", "publicity": "Publicity", diff --git a/frontend/messages/ja.json b/frontend/messages/ja.json index 04b1079..b12a4e0 100644 --- a/frontend/messages/ja.json +++ b/frontend/messages/ja.json @@ -296,6 +296,7 @@ "project_management": "プロジェクト管理", "project_name": "プロジェクト名", "project_detail": "プロジェクト詳細", + "project_owner": "所有者", "edit_project": "プロジェクトの編集", "project": "プロジェクト", "publicity": "公開", diff --git a/frontend/src/app/[locale]/projects/[projectId]/settings/SettingsPage.tsx b/frontend/src/app/[locale]/projects/[projectId]/settings/SettingsPage.tsx index 0ac0465..dee5498 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/settings/SettingsPage.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/settings/SettingsPage.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { useState, useEffect, useContext } from 'react'; import { Button, Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from '@nextui-org/react'; +import Avatar from 'boring-avatars'; import { Pencil, Trash } from 'lucide-react'; import { SettingsMessages } from '@/types/settings'; import { TokenContext } from '@/utils/TokenProvider'; @@ -10,6 +11,9 @@ import { ProjectType } from '@/types/project'; import DeleteConfirmDialog from '@/components/DeleteConfirmDialog'; import { useRouter } from '@/src/navigation'; import ProjectDialog from '@/components/ProjectDialog'; +import Config from '@/config/config'; +import { UserType } from '@/types/user'; +const apiServer = Config.apiServer; type Props = { projectId: string; @@ -31,6 +35,37 @@ export default function SettingsPage({ projectId, messages, locale }: Props) { Folders: [], Runs: [], }); + const [owner, setOwner] = useState({ + id: 0, + email: '', + password: '', + avatarPath: '', + role: -1, + username: '', + }); + + async function fetchUser(jwt: string, userId: string) { + const fetchOptions = { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${jwt}`, + }, + }; + + const url = `${apiServer}/users/${userId}`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error: any) { + console.error('Error fetching data:', error.message); + } + } useEffect(() => { async function fetchDataEffect() { @@ -41,6 +76,13 @@ export default function SettingsPage({ projectId, messages, locale }: Props) { try { const data = await fetchProject(context.token.access_token, Number(projectId)); setProject(data); + + if (data.userId) { + const ownerData = await fetchUser(context.token.access_token, data.userId); + setOwner(ownerData); + } else { + console.error('failed to get project owner id'); + } } catch (error: any) { console.error('Error in effect:', error.message); } @@ -99,15 +141,29 @@ export default function SettingsPage({ projectId, messages, locale }: Props) { dummy - + {messages.projectName} {project.name} - + {messages.projectDetail} {project.detail} - + + {messages.projectOwner} + +
+ +

{owner.username}

+
+
+
+ {messages.publicity} {project.isPublic ? messages.public : messages.private} diff --git a/frontend/src/app/[locale]/projects/[projectId]/settings/page.tsx b/frontend/src/app/[locale]/projects/[projectId]/settings/page.tsx index 84ee812..bf98458 100644 --- a/frontend/src/app/[locale]/projects/[projectId]/settings/page.tsx +++ b/frontend/src/app/[locale]/projects/[projectId]/settings/page.tsx @@ -7,6 +7,7 @@ export default function Page({ params }: { params: { projectId: string; locale: projectManagement: t('project_management'), projectName: t('project_name'), projectDetail: t('project_detail'), + projectOwner: t('project_owner'), editProject: t('edit_project'), project: t('project'), ifYouMakePublic: t('if_you_make_public'), diff --git a/frontend/types/settings.ts b/frontend/types/settings.ts index 93cb6cf..6c1b819 100644 --- a/frontend/types/settings.ts +++ b/frontend/types/settings.ts @@ -2,6 +2,7 @@ export type SettingsMessages = { projectManagement: string; projectName: string; projectDetail: string; + projectOwner: string; editProject: string; project: string; publicity: string; diff --git a/frontend/types/user.ts b/frontend/types/user.ts index f0c3933..0484bcb 100644 --- a/frontend/types/user.ts +++ b/frontend/types/user.ts @@ -7,7 +7,7 @@ export type UserType = { username: string; role: number; avatarPath: string | null; -} | null; +}; export type TokenProps = { toastMessages: ToastMessages;